@@ -308,54 +308,208 @@ class InChIOptionsLatestMoInElement extends InChIOptionsElement {
308308 }
309309}
310310
311+ function createAnnotation ( text , color ) {
312+ const annotation = document . createElement ( "div" ) ;
313+ annotation . textContent = text ;
314+ annotation . classList . add ( color ) ;
315+ annotation . classList . add ( "active" ) ;
316+ annotation . style . color = "black" ;
317+ annotation . style . fontWeight = "500" ;
318+ annotation . style . paddingLeft = "1%" ;
319+ annotation . style . paddingRight = "1%" ;
320+
321+ return annotation ;
322+ }
323+
324+ function getAnnotationData ( inchi , auxinfo ) {
325+ // Returns a map of maps of maps. Innermost maps can be empty.
326+ const inchiParsed = parseInchi ( inchi ) ;
327+ const auxinfoParsed = parseAuxinfo ( auxinfo ) ;
328+
329+ const annotationData = new Map ( ) ;
330+
331+ annotationData . set ( "canonicalIndex" , auxinfoParsed . get ( "N" ) ) ;
332+ annotationData . set ( "equivalenceClass" , auxinfoParsed . get ( "E" ) ) ;
333+ annotationData . set ( "hydrogenGroup" , inchiParsed . get ( "h" ) ) ;
334+ annotationData . set (
335+ "hydrogenGroupClass" ,
336+ mapCanonicalAtomIndicesToMobileHydrogenGroupClasses (
337+ annotationData . get ( "hydrogenGroup" ) ,
338+ auxinfoParsed . get ( "gE" )
339+ )
340+ ) ;
341+
342+ return annotationData ;
343+ }
344+
311345class NGLViewerElement extends HTMLElement {
312346 constructor ( ) {
313347 super ( ) ;
314- }
315348
316- connectedCallback ( ) {
317- this . innerHTML = `<div id="ngl-viewport" style="width: 100%; height: 600px;" /div>` ;
318- const viewport = this . querySelector ( "#ngl-viewport" ) ;
349+ this . annotationColors = {
350+ index : "annotation-index" ,
351+ canonicalIndex : "annotation-canonical-index" ,
352+ equivalenceClass : "annotation-equivalence-class" ,
353+ hydrogenGroup : "annotation-hydrogen-group" ,
354+ hydrogenGroupClass : "annotation-hydrogen-group-class" ,
355+ } ;
319356
320- this . stage = new NGL . Stage ( viewport , { backgroundColor : "white" } ) ;
357+ const annotationButtonTexts = {
358+ index : "Index" ,
359+ canonicalIndex : "Canonical Index" ,
360+ equivalenceClass : "Equivalence Class" ,
361+ hydrogenGroup : "Hydrogen Group" ,
362+ hydrogenGroupClass : "Hydrogen Group Class" ,
363+ } ;
321364
322- const resizeObserver = new window . ResizeObserver ( ( ) =>
323- this . stage . handleResize ( )
365+ this . annotationButtons = Object . keys ( this . annotationColors ) . map ( ( id ) => ( {
366+ id,
367+ text : annotationButtonTexts [ id ] ,
368+ color : this . annotationColors [ id ] ,
369+ } ) ) ;
370+
371+ this . annotationSelection = Object . fromEntries (
372+ Object . keys ( this . annotationColors ) . map ( ( id ) => [ id , false ] )
324373 ) ;
325- resizeObserver . observe ( viewport ) ;
326- }
327- }
328374
329- class AtomLabelLegendElement extends HTMLElement {
330- constructor ( ) {
331- super ( ) ;
375+ this . innerHTML = `<div id="annotation-selection" class="mt-2"></div>
376+ <div id="ngl-viewport" style="width: 100%; height: 600px;"></div>` ;
377+
378+ this . stage = undefined ;
379+ this . structure = undefined ;
380+ this . annotationData = undefined ;
381+ this . annotationSelectionElement = undefined ;
332382 }
333383
334384 connectedCallback ( ) {
335- const annotationLegend = document . createElement ( "div" ) ;
336- annotationLegend . style . display = "flex" ;
337- annotationLegend . style . flexWrap = "wrap" ;
385+ const viewportElement = this . querySelector ( "#ngl-viewport" ) ;
386+ this . stage = new NGL . Stage ( viewportElement , { backgroundColor : "white" } ) ;
387+ const resizeObserver = new ResizeObserver ( ( ) => this . stage . handleResize ( ) ) ;
388+ resizeObserver . observe ( viewportElement ) ;
338389
339- annotationLegend . appendChild (
340- createAnnotation ( "Original Index" , annotationColors . index )
341- ) ;
342- annotationLegend . appendChild (
343- createAnnotation ( "Canonical Index" , annotationColors . canonicalIndex )
344- ) ;
345- annotationLegend . appendChild (
346- createAnnotation ( "Equivalence Class" , annotationColors . equivalenceClass )
347- ) ;
348- annotationLegend . appendChild (
349- createAnnotation ( "Hydrogen Group" , annotationColors . mobileHydrogenGroup )
350- ) ;
351- annotationLegend . appendChild (
352- createAnnotation (
353- "Hydrogen Group Class" ,
354- annotationColors . mobileHydrogenGroupClass
355- )
390+ this . annotationSelectionElement = this . querySelector (
391+ "#annotation-selection"
356392 ) ;
393+ this . annotationButtons . forEach ( ( button ) => {
394+ const buttonElement = document . createElement ( "button" ) ;
395+ buttonElement . id = button . id ;
396+ buttonElement . textContent = button . text ;
397+ buttonElement . classList . add ( button . color ) ;
398+ buttonElement . classList . add ( "annotation-button" ) ;
399+ buttonElement . disabled = true ;
400+
401+ buttonElement . addEventListener ( "click" , ( ) => {
402+ const isActive = buttonElement . classList . toggle ( "active" ) ;
403+ this . annotationSelection [ button . id ] = isActive ;
404+ this . annotateStructure ( ) ;
405+ } ) ;
406+
407+ this . annotationSelectionElement . appendChild ( buttonElement ) ;
408+ } ) ;
409+ }
410+
411+ async loadStructure ( molfile , inchi , auxinfo ) {
412+ this . stage . removeAllComponents ( ) ;
413+
414+ const molfileBlob = new Blob ( [ molfile ] , { type : "text/plain" } ) ;
415+ try {
416+ this . structure = await this . stage . loadFile ( molfileBlob , { ext : "sdf" } ) ;
417+ this . structure . addRepresentation ( "ball+stick" , {
418+ multipleBond : "symmetric" ,
419+ } ) ;
420+ this . annotationData = getAnnotationData ( inchi , auxinfo ) ;
421+ this . annotationButtons . forEach ( ( button ) => {
422+ const buttonElement = this . annotationSelectionElement . querySelector (
423+ `#${ button . id } `
424+ ) ;
425+ const annotationAvailable =
426+ button . id === "index"
427+ ? true
428+ : this . annotationData . get ( button . id ) . size > 0 ;
429+ buttonElement . disabled = ! annotationAvailable ;
430+ buttonElement . classList . remove ( "active" ) ;
431+ } ) ;
432+ this . annotationSelection = Object . fromEntries (
433+ Object . keys ( this . annotationColors ) . map ( ( id ) => [ id , false ] )
434+ ) ;
435+
436+ this . structure . autoView ( ) ;
437+ } catch ( error ) {
438+ console . log ( error ) ;
439+ this . structure = undefined ;
440+ this . annotationData = undefined ;
441+ this . annotationButtons . forEach ( ( button ) => {
442+ const buttonElement = this . annotationSelectionElement . querySelector (
443+ `#${ button . id } `
444+ ) ;
445+ buttonElement . disabled = true ;
446+ buttonElement . classList . remove ( "active" ) ;
447+ } ) ;
448+ }
449+ }
450+
451+ annotateStructure ( ) {
452+ if ( ! ( this . structure && this . annotationData ) ) {
453+ return ;
454+ }
455+
456+ this . structure . removeAllAnnotations ( ) ;
457+ this . structure . structure . eachAtom ( ( atom ) => {
458+ const annotations = document . createElement ( "div" ) ;
459+ annotations . style . display = "flex" ;
460+ annotations . style . height = "20px" ;
461+
462+ const atomIndex = atom . index + 1 ;
463+ const canonicalIndex = this . annotationData
464+ . get ( "canonicalIndex" )
465+ . get ( atomIndex ) ;
466+ const equivalenceClass = this . annotationData
467+ . get ( "equivalenceClass" )
468+ . get ( canonicalIndex ) ;
469+ const hydrogenGroup = this . annotationData
470+ . get ( "hydrogenGroup" )
471+ . get ( canonicalIndex ) ;
472+ const hydrogenGroupClass = this . annotationData
473+ . get ( "hydrogenGroupClass" )
474+ . get ( canonicalIndex ) ;
475+
476+ if ( this . annotationSelection . index ) {
477+ annotations . appendChild (
478+ createAnnotation ( atomIndex , this . annotationColors . index )
479+ ) ;
480+ }
481+
482+ if ( canonicalIndex && this . annotationSelection . canonicalIndex ) {
483+ annotations . appendChild (
484+ createAnnotation ( canonicalIndex , this . annotationColors . canonicalIndex )
485+ ) ;
486+ }
357487
358- this . appendChild ( annotationLegend ) ;
488+ if ( equivalenceClass && this . annotationSelection . equivalenceClass ) {
489+ annotations . appendChild (
490+ createAnnotation (
491+ equivalenceClass ,
492+ this . annotationColors . equivalenceClass
493+ )
494+ ) ;
495+ }
496+ if ( hydrogenGroup && this . annotationSelection . hydrogenGroup ) {
497+ annotations . appendChild (
498+ createAnnotation ( hydrogenGroup , this . annotationColors . hydrogenGroup )
499+ ) ;
500+ }
501+ if ( hydrogenGroupClass && this . annotationSelection . hydrogenGroupClass ) {
502+ annotations . appendChild (
503+ createAnnotation (
504+ hydrogenGroupClass ,
505+ this . annotationColors . hydrogenGroupClass
506+ )
507+ ) ;
508+ }
509+
510+ this . structure . addAnnotation ( atom . positionToVector3 ( ) , annotations ) ;
511+ } ) ;
512+ this . structure . setVisibility ( true ) ; // Re-render the structure.
359513 }
360514}
361515
@@ -371,4 +525,3 @@ customElements.define(
371525 InChIOptionsLatestMoInElement
372526) ;
373527customElements . define ( "inchi-ngl-viewer" , NGLViewerElement ) ;
374- customElements . define ( "inchi-atom-label-legend" , AtomLabelLegendElement ) ;
0 commit comments