Browse Source

Merge branch 'molstar:master' into forkdev

ludovic autin 2 years ago
parent
commit
db93a669ab
100 changed files with 4303 additions and 1066 deletions
  1. 10 0
      .github/pull_request_template.md
  2. 0 1
      .vscode/extensions.json
  3. 121 0
      CHANGELOG.md
  4. 2 2
      README.md
  5. 1694 0
      examples/1bna_confal_pyramids.cif
  6. 283 261
      package-lock.json
  7. 40 35
      package.json
  8. 27 27
      src/apps/docking-viewer/index.ts
  9. 3 3
      src/apps/docking-viewer/viewport.tsx
  10. 9 2
      src/apps/viewer/app.ts
  11. 9 0
      src/apps/viewer/embedded.html
  12. 5 1
      src/apps/viewer/index.html
  13. 9 4
      src/cli/chem-comp-dict/create-ions.ts
  14. 9 4
      src/cli/chem-comp-dict/create-saccharides.ts
  15. 15 5
      src/cli/chem-comp-dict/create-table.ts
  16. 13 3
      src/cli/chem-comp-dict/util.ts
  17. 1 0
      src/cli/cifschema/util/cif-dic.ts
  18. 3 3
      src/examples/alpha-orbitals/controls.tsx
  19. 11 15
      src/examples/alpha-orbitals/index.ts
  20. BIN
      src/extensions/backgrounds/images/cells.jpg
  21. 91 0
      src/extensions/backgrounds/index.ts
  22. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_back6.jpg
  23. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_bottom4.jpg
  24. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_front5.jpg
  25. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_left2.jpg
  26. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_right1.jpg
  27. BIN
      src/extensions/backgrounds/skyboxes/nebula/nebula_top3.jpg
  28. 10 0
      src/extensions/backgrounds/typings.d.ts
  29. 11 0
      src/extensions/cellpack/model.ts
  30. 11 13
      src/extensions/dnatco/confal-pyramids/behavior.ts
  31. 2 2
      src/extensions/dnatco/confal-pyramids/color.ts
  32. 49 30
      src/extensions/dnatco/confal-pyramids/property.ts
  33. 78 59
      src/extensions/dnatco/confal-pyramids/representation.ts
  34. 27 22
      src/extensions/dnatco/confal-pyramids/types.ts
  35. 87 255
      src/extensions/dnatco/confal-pyramids/util.ts
  36. 2 0
      src/extensions/geo-export/ui.tsx
  37. 3 1
      src/extensions/mp4-export/controls.ts
  38. 1 0
      src/extensions/mp4-export/encoder.ts
  39. 1 4
      src/extensions/rcsb/graphql/types.ts
  40. 11 7
      src/mol-canvas3d/camera.ts
  41. 3 3
      src/mol-canvas3d/camera/stereo.ts
  42. 67 17
      src/mol-canvas3d/canvas3d.ts
  43. 467 0
      src/mol-canvas3d/passes/background.ts
  44. 309 0
      src/mol-canvas3d/passes/dpoit.ts
  45. 125 48
      src/mol-canvas3d/passes/draw.ts
  46. 2 2
      src/mol-canvas3d/passes/fxaa.ts
  47. 13 3
      src/mol-canvas3d/passes/image.ts
  48. 4 4
      src/mol-canvas3d/passes/marking.ts
  49. 11 10
      src/mol-canvas3d/passes/multi-sample.ts
  50. 7 3
      src/mol-canvas3d/passes/passes.ts
  51. 54 24
      src/mol-canvas3d/passes/postprocessing.ts
  52. 7 6
      src/mol-canvas3d/passes/smaa.ts
  53. 11 1
      src/mol-canvas3d/passes/wboit.ts
  54. 1 0
      src/mol-geo/geometry/base.ts
  55. 4 0
      src/mol-geo/geometry/mesh/mesh.ts
  56. 2 2
      src/mol-geo/geometry/text/font-atlas.ts
  57. 14 18
      src/mol-geo/geometry/texture-mesh/color-smoothing.ts
  58. 3 0
      src/mol-geo/geometry/texture-mesh/texture-mesh.ts
  59. 5 5
      src/mol-gl/_spec/renderer.spec.ts
  60. 2 2
      src/mol-gl/compute/grid3d.ts
  61. 8 8
      src/mol-gl/compute/histogram-pyramid/reduction.ts
  62. 2 2
      src/mol-gl/compute/histogram-pyramid/sum.ts
  63. 5 4
      src/mol-gl/compute/marching-cubes/active-voxels.ts
  64. 17 10
      src/mol-gl/compute/marching-cubes/isosurface.ts
  65. 5 5
      src/mol-gl/compute/util.ts
  66. 5 6
      src/mol-gl/renderable.ts
  67. 3 3
      src/mol-gl/renderable/cylinders.ts
  68. 2 2
      src/mol-gl/renderable/direct-volume.ts
  69. 2 2
      src/mol-gl/renderable/lines.ts
  70. 4 3
      src/mol-gl/renderable/mesh.ts
  71. 24 13
      src/mol-gl/renderable/schema.ts
  72. 3 3
      src/mol-gl/renderable/spheres.ts
  73. 8 8
      src/mol-gl/renderable/text.ts
  74. 4 4
      src/mol-gl/renderable/texture-mesh.ts
  75. 15 3
      src/mol-gl/renderable/util.ts
  76. 98 13
      src/mol-gl/renderer.ts
  77. 32 4
      src/mol-gl/scene.ts
  78. 5 1
      src/mol-gl/shader-code.ts
  79. 90 0
      src/mol-gl/shader/background.frag.ts
  80. 12 0
      src/mol-gl/shader/background.vert.ts
  81. 20 0
      src/mol-gl/shader/blend-back-dpoit.frag.ts
  82. 14 3
      src/mol-gl/shader/chunks/apply-fog.glsl.ts
  83. 2 8
      src/mol-gl/shader/chunks/apply-light-color.glsl.ts
  84. 10 8
      src/mol-gl/shader/chunks/apply-marker-color.glsl.ts
  85. 1 1
      src/mol-gl/shader/chunks/assign-marker-varying.glsl.ts
  86. 2 2
      src/mol-gl/shader/chunks/assign-material-color.glsl.ts
  87. 1 1
      src/mol-gl/shader/chunks/color-frag-params.glsl.ts
  88. 1 1
      src/mol-gl/shader/chunks/color-vert-params.glsl.ts
  89. 16 8
      src/mol-gl/shader/chunks/common-frag-params.glsl.ts
  90. 6 4
      src/mol-gl/shader/chunks/common-vert-params.glsl.ts
  91. 5 1
      src/mol-gl/shader/chunks/common.glsl.ts
  92. 70 0
      src/mol-gl/shader/chunks/dpoit-write.glsl.ts
  93. 1 2
      src/mol-gl/shader/compute/color-smoothing/accumulate.vert.ts
  94. 7 6
      src/mol-gl/shader/cylinders.frag.ts
  95. 19 15
      src/mol-gl/shader/direct-volume.frag.ts
  96. 17 0
      src/mol-gl/shader/evaluate-dpoit.frag.ts
  97. 2 1
      src/mol-gl/shader/image.frag.ts
  98. 2 1
      src/mol-gl/shader/lines.frag.ts
  99. 2 2
      src/mol-gl/shader/marching-cubes/isosurface.frag.ts
  100. 2 1
      src/mol-gl/shader/mesh.frag.ts

+ 10 - 0
.github/pull_request_template.md

@@ -0,0 +1,10 @@
+<!-- Thank you for contributing to Mol* -->
+
+# Description
+
+
+## Actions
+
+- [ ] Added description of changes to the `[Unreleased]` section of `CHANGELOG.md`
+- [ ] Updated headers of modified files
+- [ ] Added my name to `package.json`'s `contributors`

+ 0 - 1
.vscode/extensions.json

@@ -6,7 +6,6 @@
 	"recommendations": [
 		"dbaeumer.vscode-eslint",
 		"firsttris.vscode-jest-runner",
-		"msjsdiag.debugger-for-chrome",
 		"slevesque.shader",
 		"stpn.vscode-graphql",
 		"wayou.vscode-todo-highlight"

+ 121 - 0
CHANGELOG.md

@@ -6,6 +6,127 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Make `PluginContext.initContainer` checkered canvas background optional
+
+## [v3.23.0] - 2022-10-19
+
+- Add `PluginContext.initContainer/mount/unmount` methods; these should make it easier to reuse a plugin context with both custom and built-in UI
+- Add `PluginContext.canvas3dInitialized`
+- `createPluginUI` now resolves after the 3d canvas has been initialized
+- Change EM Volume Streaming default from `Whote Structure` to `Auto`
+
+## [v3.22.0] - 2022-10-17
+
+- Replace `VolumeIsosurfaceParams.pickingGranularity` param with `Volume.PickingGranuality` 
+
+## [v3.21.0] - 2022-10-17
+
+- Add `VolumeIsosurfaceParams.pickingGranularity` param
+- Prevent component controls collapsing when option is selected
+
+## [v3.20.0] - 2022-10-16
+
+- [Breaking] Rename the ``model-index`` color theme to ``trajectory-index``
+- Add a new ``model-index`` color theme that uniquely colors each loaded model
+- Add the new ``model-index`` and ``structure-index`` color themes as an option for the carbon color in the ``element-symbol`` and ``ilustrative`` color themes
+- Add ``structure-index`` color theme that uniquely colors each root structure
+- Add ``nearest`` method to ``Lookup3D``
+- Add mipmap-based blur for skybox backgrounds
+
+## [v3.19.0] - 2022-10-01
+
+- Fix "empty textures" error on empty canvas
+- Optimize BinaryCIF integer packing encoder
+- Fix dual depth peeling when post-processing is off or when rendering direct-volumes
+- Add ``cameraClipping.minNear`` parameter
+- Fix black artifacts on specular highlights with transparent background
+
+## [v3.18.0] - 2022-09-17
+
+- Integration of Dual depth peeling - OIT method
+- Stereo camera improvements
+    - Fix param updates not applied
+    - Better param ranges and description
+    - Add timer.mark for left/right camera
+
+## [v3.17.0] - 2022-09-11
+
+- [Fix] Clone ``Canvas3DParams`` when creating a ``Canvas3D`` instance to prevent shared state between multiple instances
+- Add ``includeResidueTest`` option to ``alignAndSuperposeWithSIFTSMapping``
+- Add ``parentDisplay`` param for interactions representation.
+- [Experimental] Add support for PyMOL, VMD, and Jmol atom expressions in selection scripts
+- Support for ``failIfMajorPerformanceCaveat`` webgl attribute. Add ``PluginConfig.General.AllowMajorPerformanceCaveat`` and ``allow-major-performance-caveat`` Viewer GET param.
+- Fix handling of PDB TER records (#549)
+- Add support for getting multiple loci from a representation (``.getAllLoci()``)
+- Add ``key`` property to intra- and inter-bonds for referencing source data
+- Fix click event triggered after move
+
+## [v3.16.0] - 2022-08-25
+
+- Support ``globalColorParams`` and ``globalSymmetryParams`` in common representation params
+- Support ``label`` parameter in ``Viewer.loadStructureFromUrl``
+- Fix ``ViewportHelpContent`` Mouse Controls section
+
+## [v3.15.0] - 2022-08-23
+
+- Fix wboit in Safari >=15 (add missing depth renderbuffer to wboit pass)
+- Add 'Around Camera' option to Volume streaming
+- Avoid queuing more than one update in Volume streaming
+
+## [v3.14.0] - 2022-08-20
+
+- Expose inter-bonds compute params in structure
+- Improve performance of inter/intra-bonds compute
+- Fix defaultAttribs handling in Canvas3DContext.fromCanvas
+- Confal pyramids extension improvements
+    - Add custom labels to Confal pyramids
+    - Improve naming of some internal types in Confal pyramids extension coordinate
+    - Add example mmCIF file with categories necessary to display Confal pyramids
+    - Change the lookup logic of NtC steps from residues
+- Add support for download of gzipped files
+- Don't filter IndexPairBonds by element-based rules in MOL/SDF and MOL2 (without symmetry) models
+- Fix Glycam Saccharide Names used by default
+- Fix GPU surfaces rendering in Safari with WebGL2
+- Add ``fov`` (Field of View) Canvas3D parameter
+- Add ``sceneRadiusFactor`` Canvas3D parameter
+- Add background pass (skybox, image, horizontal/radial gradient)
+    - Set simple-settings presets via ``PluginConfig.Background.Styles``
+    - Example presets in new backgrounds extension
+    - Load skybox/image from URL or File (saved in session)
+    - Opacity, saturation, lightness controls for skybox/image
+    - Coverage (viewport or canvas) controls for image/gradient
+- [Breaking] ``AssetManager`` needs to be passed to various graphics related classes
+- Fix SSAO renderable initialization
+- Reduce number of webgl state changes
+    - Add ``viewport`` and ``scissor`` to state object
+    - Add ``hasOpaque`` to scene object
+- Handle edge cases where some renderables would not get (correctly) rendered
+    - Fix text background rendering for opaque text
+    - Fix helper scenes not shown when rendering directly to draw target
+- Fix ``CustomElementProperty`` coloring not working
+
+## [v3.13.0] - 2022-07-24
+
+- Fix: only update camera state if manualReset is off (#494)
+- Improve handling principal axes of points in a plane
+- Add 'material' annotation support for textures
+- More effort to avoid using ``flat`` qualifier in shaders: add ``dVaryingGroup``
+- Enable ``immediateUpdate`` for iso level in isosurface and volume streaming controls
+- Add support to download CCD from configurable URL
+
+## [v3.12.1] - 2022-07-20
+
+- Fix plugin behavior dispose logic to correctly unsubscribe observables.
+
+## [v3.12.0] - 2022-07-17
+
+- Add ``colorMarker`` option to Renderer. This disables the highlight and select marker at a shader level for faster rendering of large scenes in some cases.
+- Bind shared textures only once per pass, not for each render item
+- Fix missing 'material' annotation for some uniforms, causing unnecessary uniform updates
+- Remove use of ``isnan`` in impostor shaders, not needed and causing slowdown
+- Avoid using ``flat`` qualifier in shaders, causing slowdown
+- Improve CellPack's ``adjustStyle`` option (disable ``colorMarker``, set component options, enable marking w/o ghost)
+- Scan all entities when looking for ``struct_conn`` entries (fixes issue when the same ``label_asym_id`` is used in more than one entity)
 
 ## [v3.11.0] - 2022-07-04
 

+ 2 - 2
README.md

@@ -126,7 +126,7 @@ and navigate to `build/viewer`
 
 **GraphQL schemas**
 
-    node node_modules//@graphql-codegen/cli/bin -c src/extensions/rcsb/graphql/codegen.yml
+    node node_modules/@graphql-codegen/cli/cjs/bin -c src/extensions/rcsb/graphql/codegen.yml
 
 ### Other scripts
 **Create chem comp bond table**
@@ -152,7 +152,7 @@ Or
     node lib/commonjs/cli/cif2bcif
 
 E.g.
- 
+
     node lib/commonjs/cli/cif2bcif src.cif out.bcif.gz
     node lib/commonjs/cli/cif2bcif src.bcif.gz out.cif
 

+ 1694 - 0
examples/1bna_confal_pyramids.cif

@@ -0,0 +1,1694 @@
+data_1BNA
+#
+_entry.id  1BNA
+##
+_audit_conform.dict_name      mmcif_pdbx.dic
+_audit_conform.dict_version   5.279
+_audit_conform.dict_location  http://mmcif.pdb.org/dictionaries/ascii/mmcif_pdbx.dic
+##
+loop_
+_database_2.database_id
+_database_2.database_code
+PDB    1BNA          
+RCSB   BDL001        
+WWPDB  D_1000171933  
+##
+_pdbx_database_status.status_code                    REL
+_pdbx_database_status.entry_id                       1BNA
+_pdbx_database_status.recvd_initial_deposition_date  1981-01-26
+_pdbx_database_status.deposit_site                   BNL
+_pdbx_database_status.process_site                   BNL
+_pdbx_database_status.status_code_sf                 REL
+_pdbx_database_status.status_code_mr                 ?
+_pdbx_database_status.SG_entry                       ?
+_pdbx_database_status.pdb_format_compatible          Y
+_pdbx_database_status.status_code_cs                 ?
+##
+loop_
+_audit_author.name
+_audit_author.pdbx_ordinal
+'Drew, H.R.'       1  
+'Wing, R.M.'       2  
+'Takano, T.'       3  
+'Broka, C.'        4  
+'Tanaka, S.'       5  
+'Itakura, K.'      6  
+'Dickerson, R.E.'  7  
+##
+loop_
+_citation.id
+_citation.title
+_citation.journal_abbrev
+_citation.journal_volume
+_citation.page_first
+_citation.page_last
+_citation.year
+_citation.journal_id_ASTM
+_citation.country
+_citation.journal_id_ISSN
+_citation.journal_id_CSD
+_citation.book_publisher
+_citation.pdbx_database_id_PubMed
+_citation.pdbx_database_id_DOI
+primary  'Structure of a B-DNA dodecamer: conformation and dynamics.'                         Proc.Natl.Acad.Sci.USA   78  2179  2183  1981  PNASA6  US  0027-8424  0040  ?  6941276  10.1073/pnas.78.4.2179  
+1        'Kinematic Model for B-DNA'                                                          Proc.Natl.Acad.Sci.USA   78  7318  7322  1981  PNASA6  US  0027-8424  0040  ?        ?                       ?  
+2        'Structure of a B-DNA Dodecamer. II. Influence of Base Sequence on Helix Structure'  J.Mol.Biol.             149   761   786  1981  JMOBAK  UK  0022-2836  0070  ?        ?                       ?  
+3        'Structure of a B-DNA Dodecamer. III. Geometry of Hydration'                         J.Mol.Biol.             151   535   556  1981  JMOBAK  UK  0022-2836  0070  ?        ?                       ?  
+4        'Crystal Structure Analysis of a Complete Turn of B-DNA'                             Nature                  287   755   758  1980  NATUAS  UK  0028-0836  0006  ?        ?                       ?  
+##
+loop_
+_citation_author.citation_id
+_citation_author.name
+_citation_author.ordinal
+primary  'Drew, H.R.'        1  
+primary  'Wing, R.M.'        2  
+primary  'Takano, T.'        3  
+primary  'Broka, C.'         4  
+primary  'Tanaka, S.'        5  
+primary  'Itakura, K.'       6  
+primary  'Dickerson, R.E.'   7  
+1        'Dickerson, R.E.'   8  
+1        'Drew, H.R.'        9  
+2        'Dickerson, R.E.'  10  
+2        'Drew, H.R.'       11  
+3        'Drew, H.R.'       12  
+3        'Dickerson, R.E.'  13  
+4        'Wing, R.'         14  
+4        'Drew, H.R.'       15  
+4        'Takano, T.'       16  
+4        'Broka, C.'        17  
+4        'Tanaka, S.'       18  
+4        'Itakura, K.'      19  
+4        'Dickerson, R.E.'  20  
+##
+_cell.entry_id          1BNA
+_cell.length_a          24.870
+_cell.length_b          40.390
+_cell.length_c          66.200
+_cell.angle_alpha       90.00
+_cell.angle_beta        90.00
+_cell.angle_gamma       90.00
+_cell.Z_PDB             8
+_cell.pdbx_unique_axis  ?
+##
+_symmetry.entry_id                        1BNA
+_symmetry.space_group_name_H-M            'P 21 21 21'
+_symmetry.pdbx_full_space_group_name_H-M  ?
+_symmetry.cell_setting                    ?
+_symmetry.Int_Tables_number               19
+##
+loop_
+_entity.id
+_entity.type
+_entity.src_method
+_entity.pdbx_description
+_entity.formula_weight
+_entity.pdbx_number_of_molecules
+_entity.pdbx_ec
+_entity.pdbx_mutation
+_entity.pdbx_fragment
+_entity.details
+1  polymer  syn  "DNA(5'-D(*CP*GP*CP*GP*AP*AP*TP*TP*CP*GP*CP*G)-3')"  3663.392   2  ?  ?  ?  ?  
+2  water    nat  water                                                  18.015  80  ?  ?  ?  ?  
+##
+_entity_poly.entity_id                     1
+_entity_poly.type                          polydeoxyribonucleotide
+_entity_poly.nstd_linkage                  no
+_entity_poly.nstd_monomer                  no
+_entity_poly.pdbx_seq_one_letter_code      '(DC)(DG)(DC)(DG)(DA)(DA)(DT)(DT)(DC)(DG)(DC)(DG)'
+_entity_poly.pdbx_seq_one_letter_code_can  CGCGAATTCGCG
+_entity_poly.pdbx_strand_id                A,B
+_entity_poly.pdbx_target_identifier        ?
+##
+loop_
+_entity_poly_seq.entity_id
+_entity_poly_seq.num
+_entity_poly_seq.mon_id
+_entity_poly_seq.hetero
+1   1  DC  n  
+1   2  DG  n  
+1   3  DC  n  
+1   4  DG  n  
+1   5  DA  n  
+1   6  DA  n  
+1   7  DT  n  
+1   8  DT  n  
+1   9  DC  n  
+1  10  DG  n  
+1  11  DC  n  
+1  12  DG  n  
+##
+_struct_ref.id                        1
+_struct_ref.entity_id                 1
+_struct_ref.db_name                   PDB
+_struct_ref.db_code                   1BNA
+_struct_ref.pdbx_db_accession         1BNA
+_struct_ref.pdbx_db_isoform           ?
+_struct_ref.pdbx_seq_one_letter_code  ?
+_struct_ref.pdbx_align_begin          ?
+##
+loop_
+_struct_ref_seq.align_id
+_struct_ref_seq.ref_id
+_struct_ref_seq.pdbx_PDB_id_code
+_struct_ref_seq.pdbx_strand_id
+_struct_ref_seq.seq_align_beg
+_struct_ref_seq.pdbx_seq_align_beg_ins_code
+_struct_ref_seq.seq_align_end
+_struct_ref_seq.pdbx_seq_align_end_ins_code
+_struct_ref_seq.pdbx_db_accession
+_struct_ref_seq.db_align_beg
+_struct_ref_seq.pdbx_db_align_beg_ins_code
+_struct_ref_seq.db_align_end
+_struct_ref_seq.pdbx_db_align_end_ins_code
+_struct_ref_seq.pdbx_auth_seq_align_beg
+_struct_ref_seq.pdbx_auth_seq_align_end
+1  1  1BNA  A  1  ?  12  ?  1BNA   1  ?  12  ?   1  12  
+2  1  1BNA  B  1  ?  12  ?  1BNA  13  ?  24  ?  13  24  
+##
+loop_
+_chem_comp.id
+_chem_comp.type
+_chem_comp.mon_nstd_flag
+_chem_comp.name
+_chem_comp.pdbx_synonyms
+_chem_comp.formula
+_chem_comp.formula_weight
+DA   'DNA linking'  y  "2'-DEOXYADENOSINE-5'-MONOPHOSPHATE"  ?  'C10 H14 N5 O6 P'  331.222  
+DC   'DNA linking'  y  "2'-DEOXYCYTIDINE-5'-MONOPHOSPHATE"   ?  'C9 H14 N3 O7 P'   307.197  
+DG   'DNA linking'  y  "2'-DEOXYGUANOSINE-5'-MONOPHOSPHATE"  ?  'C10 H14 N5 O7 P'  347.221  
+DT   'DNA linking'  y  "THYMIDINE-5'-MONOPHOSPHATE"          ?  'C10 H15 N2 O8 P'  322.208  
+HOH  non-polymer    .  WATER                                 ?  'H2 O'              18.015  
+##
+_exptl.entry_id         1BNA
+_exptl.method           'X-RAY DIFFRACTION'
+_exptl.crystals_number  ?
+##
+_exptl_crystal.id                   1
+_exptl_crystal.density_meas         ?
+_exptl_crystal.density_Matthews     2.27
+_exptl_crystal.density_percent_sol  45.79
+_exptl_crystal.description          ?
+##
+_exptl_crystal_grow.crystal_id     1
+_exptl_crystal_grow.method         'VAPOR DIFFUSION'
+_exptl_crystal_grow.temp           290.00
+_exptl_crystal_grow.temp_details   ?
+_exptl_crystal_grow.pH             ?
+_exptl_crystal_grow.pdbx_details   'VAPOR DIFFUSION, temperature 290.00K'
+_exptl_crystal_grow.pdbx_pH_range  ?
+##
+loop_
+_exptl_crystal_grow_comp.crystal_id
+_exptl_crystal_grow_comp.id
+_exptl_crystal_grow_comp.sol_id
+_exptl_crystal_grow_comp.name
+_exptl_crystal_grow_comp.volume
+_exptl_crystal_grow_comp.conc
+_exptl_crystal_grow_comp.details
+1  1  1  WATER         ?  ?  ?  
+1  2  1  'MG ACETATE'  ?  ?  ?  
+1  3  1  SPERMINE_HCL  ?  ?  ?  
+1  4  2  WATER         ?  ?  ?  
+1  5  2  MPD           ?  ?  ?  
+##
+_diffrn.id                    1
+_diffrn.crystal_id            1
+_diffrn.ambient_temp          ?
+_diffrn.ambient_temp_details  ?
+##
+_diffrn_detector.diffrn_id             1
+_diffrn_detector.detector              DIFFRACTOMETER
+_diffrn_detector.type                  ?
+_diffrn_detector.pdbx_collection_date  ?
+_diffrn_detector.details               ?
+##
+_diffrn_radiation.diffrn_id                       1
+_diffrn_radiation.wavelength_id                   1
+_diffrn_radiation.pdbx_monochromatic_or_laue_m_l  ?
+_diffrn_radiation.monochromator                   ?
+_diffrn_radiation.pdbx_diffrn_protocol            ?
+_diffrn_radiation.pdbx_scattering_type            x-ray
+##
+_diffrn_radiation_wavelength.id          1
+_diffrn_radiation_wavelength.wavelength  .
+_diffrn_radiation_wavelength.wt          1.0
+##
+_diffrn_source.diffrn_id                  1
+_diffrn_source.source                     ?
+_diffrn_source.type                       ?
+_diffrn_source.pdbx_synchrotron_site      ?
+_diffrn_source.pdbx_synchrotron_beamline  ?
+_diffrn_source.pdbx_wavelength            ?
+_diffrn_source.pdbx_wavelength_list       ?
+##
+_reflns.entry_id                    1BNA
+_reflns.observed_criterion_sigma_I  ?
+_reflns.observed_criterion_sigma_F  ?
+_reflns.d_resolution_low            8.0
+_reflns.d_resolution_high           1.900
+_reflns.number_obs                  5534
+_reflns.number_all                  ?
+_reflns.percent_possible_obs        ?
+_reflns.pdbx_Rmerge_I_obs           ?
+_reflns.pdbx_Rsym_value             ?
+_reflns.pdbx_netI_over_sigmaI       ?
+_reflns.B_iso_Wilson_estimate       ?
+_reflns.pdbx_redundancy             ?
+_reflns.pdbx_diffrn_id              1
+_reflns.pdbx_ordinal                1
+##
+_refine.entry_id                                1BNA
+_refine.ls_number_reflns_obs                    2725
+_refine.ls_number_reflns_all                    ?
+_refine.pdbx_ls_sigma_I                         2.000
+_refine.pdbx_ls_sigma_F                         ?
+_refine.pdbx_data_cutoff_high_absF              ?
+_refine.pdbx_data_cutoff_low_absF               ?
+_refine.pdbx_data_cutoff_high_rms_absF          ?
+_refine.ls_d_res_low                            8.000
+_refine.ls_d_res_high                           1.900
+_refine.ls_percent_reflns_obs                   ?
+_refine.ls_R_factor_obs                         0.1780000
+_refine.ls_R_factor_all                         ?
+_refine.ls_R_factor_R_work                      ?
+_refine.ls_R_factor_R_free                      ?
+_refine.ls_R_factor_R_free_error                ?
+_refine.ls_R_factor_R_free_error_details        ?
+_refine.ls_percent_reflns_R_free                ?
+_refine.ls_number_reflns_R_free                 ?
+_refine.ls_number_parameters                    ?
+_refine.ls_number_restraints                    ?
+_refine.occupancy_min                           ?
+_refine.occupancy_max                           ?
+_refine.B_iso_mean                              ?
+_refine.aniso_B[1][1]                           ?
+_refine.aniso_B[2][2]                           ?
+_refine.aniso_B[3][3]                           ?
+_refine.aniso_B[1][2]                           ?
+_refine.aniso_B[1][3]                           ?
+_refine.aniso_B[2][3]                           ?
+_refine.solvent_model_details                   ?
+_refine.solvent_model_param_ksol                ?
+_refine.solvent_model_param_bsol                ?
+_refine.pdbx_ls_cross_valid_method              ?
+_refine.details                                 ?
+_refine.pdbx_starting_model                     ?
+_refine.pdbx_method_to_determine_struct         ?
+_refine.pdbx_isotropic_thermal_model            ?
+_refine.pdbx_stereochemistry_target_values      ?
+_refine.pdbx_stereochem_target_val_spec_case    ?
+_refine.pdbx_R_Free_selection_details           ?
+_refine.pdbx_overall_ESU_R                      ?
+_refine.pdbx_overall_ESU_R_Free                 ?
+_refine.overall_SU_ML                           ?
+_refine.overall_SU_B                            ?
+_refine.pdbx_refine_id                          'X-RAY DIFFRACTION'
+_refine.pdbx_diffrn_id                          1
+_refine.pdbx_TLS_residual_ADP_flag              ?
+_refine.correlation_coeff_Fo_to_Fc              ?
+_refine.correlation_coeff_Fo_to_Fc_free         ?
+_refine.pdbx_solvent_vdw_probe_radii            ?
+_refine.pdbx_solvent_ion_probe_radii            ?
+_refine.pdbx_solvent_shrinkage_radii            ?
+_refine.pdbx_overall_phase_error                ?
+_refine.overall_SU_R_Cruickshank_DPI            ?
+_refine.pdbx_overall_SU_R_free_Cruickshank_DPI  ?
+_refine.pdbx_overall_SU_R_Blow_DPI              ?
+_refine.pdbx_overall_SU_R_free_Blow_DPI         ?
+##
+_refine_hist.pdbx_refine_id                  'X-RAY DIFFRACTION'
+_refine_hist.cycle_id                        LAST
+_refine_hist.pdbx_number_atoms_protein       0
+_refine_hist.pdbx_number_atoms_nucleic_acid  486
+_refine_hist.pdbx_number_atoms_ligand        0
+_refine_hist.number_atoms_solvent            80
+_refine_hist.number_atoms_total              566
+_refine_hist.d_res_high                      1.900
+_refine_hist.d_res_low                       8.000
+##
+_struct.entry_id                 1BNA
+_struct.title                    'STRUCTURE OF A B-DNA DODECAMER. CONFORMATION AND DYNAMICS'
+_struct.pdbx_descriptor          "5'-D(*CP*GP*CP*GP*AP*AP*TP*TP*CP*GP*CP*G)-3',290 K"
+_struct.pdbx_model_details       ?
+_struct.pdbx_CASP_flag           ?
+_struct.pdbx_model_type_details  ?
+##
+_struct_keywords.entry_id       1BNA
+_struct_keywords.pdbx_keywords  DNA
+_struct_keywords.text           'B-DNA, DOUBLE HELIX, DNA'
+##
+loop_
+_struct_asym.id
+_struct_asym.pdbx_blank_PDB_chainid_flag
+_struct_asym.pdbx_modified
+_struct_asym.entity_id
+_struct_asym.details
+A  N  N  1  ?  
+B  N  N  1  ?  
+C  N  N  2  ?  
+D  N  N  2  ?  
+##
+_struct_biol.id  1
+##
+loop_
+_struct_conn.id
+_struct_conn.conn_type_id
+_struct_conn.pdbx_leaving_atom_flag
+_struct_conn.pdbx_PDB_id
+_struct_conn.ptnr1_label_asym_id
+_struct_conn.ptnr1_label_comp_id
+_struct_conn.ptnr1_label_seq_id
+_struct_conn.ptnr1_label_atom_id
+_struct_conn.pdbx_ptnr1_label_alt_id
+_struct_conn.pdbx_ptnr1_PDB_ins_code
+_struct_conn.pdbx_ptnr1_standard_comp_id
+_struct_conn.ptnr1_symmetry
+_struct_conn.ptnr2_label_asym_id
+_struct_conn.ptnr2_label_comp_id
+_struct_conn.ptnr2_label_seq_id
+_struct_conn.ptnr2_label_atom_id
+_struct_conn.pdbx_ptnr2_label_alt_id
+_struct_conn.pdbx_ptnr2_PDB_ins_code
+_struct_conn.ptnr1_auth_asym_id
+_struct_conn.ptnr1_auth_comp_id
+_struct_conn.ptnr1_auth_seq_id
+_struct_conn.ptnr2_auth_asym_id
+_struct_conn.ptnr2_auth_comp_id
+_struct_conn.ptnr2_auth_seq_id
+_struct_conn.ptnr2_symmetry
+_struct_conn.pdbx_ptnr3_label_atom_id
+_struct_conn.pdbx_ptnr3_label_seq_id
+_struct_conn.pdbx_ptnr3_label_comp_id
+_struct_conn.pdbx_ptnr3_label_asym_id
+_struct_conn.pdbx_ptnr3_label_alt_id
+_struct_conn.pdbx_ptnr3_PDB_ins_code
+_struct_conn.details
+_struct_conn.pdbx_dist_value
+_struct_conn.pdbx_value_order
+hydrog1   hydrog  ?  ?  A  DC   1  N3  ?  ?  ?  1_555  B  DG  12  N1  ?  ?  A  DC   1  B  DG  24  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog2   hydrog  ?  ?  A  DC   1  N4  ?  ?  ?  1_555  B  DG  12  O6  ?  ?  A  DC   1  B  DG  24  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog3   hydrog  ?  ?  A  DC   1  O2  ?  ?  ?  1_555  B  DG  12  N2  ?  ?  A  DC   1  B  DG  24  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog4   hydrog  ?  ?  A  DG   2  N1  ?  ?  ?  1_555  B  DC  11  N3  ?  ?  A  DG   2  B  DC  23  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog5   hydrog  ?  ?  A  DG   2  N2  ?  ?  ?  1_555  B  DC  11  O2  ?  ?  A  DG   2  B  DC  23  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog6   hydrog  ?  ?  A  DG   2  O6  ?  ?  ?  1_555  B  DC  11  N4  ?  ?  A  DG   2  B  DC  23  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog7   hydrog  ?  ?  A  DC   3  N3  ?  ?  ?  1_555  B  DG  10  N1  ?  ?  A  DC   3  B  DG  22  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog8   hydrog  ?  ?  A  DC   3  N4  ?  ?  ?  1_555  B  DG  10  O6  ?  ?  A  DC   3  B  DG  22  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog9   hydrog  ?  ?  A  DC   3  O2  ?  ?  ?  1_555  B  DG  10  N2  ?  ?  A  DC   3  B  DG  22  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog10  hydrog  ?  ?  A  DG   4  N1  ?  ?  ?  1_555  B  DC   9  N3  ?  ?  A  DG   4  B  DC  21  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog11  hydrog  ?  ?  A  DG   4  N2  ?  ?  ?  1_555  B  DC   9  O2  ?  ?  A  DG   4  B  DC  21  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog12  hydrog  ?  ?  A  DG   4  O6  ?  ?  ?  1_555  B  DC   9  N4  ?  ?  A  DG   4  B  DC  21  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog13  hydrog  ?  ?  A  DA   5  N1  ?  ?  ?  1_555  B  DT   8  N3  ?  ?  A  DA   5  B  DT  20  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog14  hydrog  ?  ?  A  DA   5  N6  ?  ?  ?  1_555  B  DT   8  O4  ?  ?  A  DA   5  B  DT  20  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog15  hydrog  ?  ?  A  DA   6  N1  ?  ?  ?  1_555  B  DT   7  N3  ?  ?  A  DA   6  B  DT  19  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog16  hydrog  ?  ?  A  DA   6  N6  ?  ?  ?  1_555  B  DT   7  O4  ?  ?  A  DA   6  B  DT  19  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog17  hydrog  ?  ?  A  DT   7  N3  ?  ?  ?  1_555  B  DA   6  N1  ?  ?  A  DT   7  B  DA  18  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog18  hydrog  ?  ?  A  DT   7  O4  ?  ?  ?  1_555  B  DA   6  N6  ?  ?  A  DT   7  B  DA  18  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog19  hydrog  ?  ?  A  DT   8  N3  ?  ?  ?  1_555  B  DA   5  N1  ?  ?  A  DT   8  B  DA  17  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog20  hydrog  ?  ?  A  DT   8  O4  ?  ?  ?  1_555  B  DA   5  N6  ?  ?  A  DT   8  B  DA  17  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog21  hydrog  ?  ?  A  DC   9  N3  ?  ?  ?  1_555  B  DG   4  N1  ?  ?  A  DC   9  B  DG  16  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog22  hydrog  ?  ?  A  DC   9  N4  ?  ?  ?  1_555  B  DG   4  O6  ?  ?  A  DC   9  B  DG  16  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog23  hydrog  ?  ?  A  DC   9  O2  ?  ?  ?  1_555  B  DG   4  N2  ?  ?  A  DC   9  B  DG  16  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog24  hydrog  ?  ?  A  DG  10  N1  ?  ?  ?  1_555  B  DC   3  N3  ?  ?  A  DG  10  B  DC  15  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog25  hydrog  ?  ?  A  DG  10  N2  ?  ?  ?  1_555  B  DC   3  O2  ?  ?  A  DG  10  B  DC  15  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog26  hydrog  ?  ?  A  DG  10  O6  ?  ?  ?  1_555  B  DC   3  N4  ?  ?  A  DG  10  B  DC  15  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog27  hydrog  ?  ?  A  DC  11  N3  ?  ?  ?  1_555  B  DG   2  N1  ?  ?  A  DC  11  B  DG  14  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog28  hydrog  ?  ?  A  DC  11  N4  ?  ?  ?  1_555  B  DG   2  O6  ?  ?  A  DC  11  B  DG  14  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog29  hydrog  ?  ?  A  DC  11  O2  ?  ?  ?  1_555  B  DG   2  N2  ?  ?  A  DC  11  B  DG  14  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog30  hydrog  ?  ?  A  DG  12  N1  ?  ?  ?  1_555  B  DC   1  N3  ?  ?  A  DG  12  B  DC  13  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog31  hydrog  ?  ?  A  DG  12  N2  ?  ?  ?  1_555  B  DC   1  O2  ?  ?  A  DG  12  B  DC  13  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+hydrog32  hydrog  ?  ?  A  DG  12  O6  ?  ?  ?  1_555  B  DC   1  N4  ?  ?  A  DG  12  B  DC  13  1_555  ?  ?  ?  ?  ?  ?  WATSON-CRICK  ?  ?  
+##
+_struct_conn_type.id         hydrog
+_struct_conn_type.criteria   ?
+_struct_conn_type.reference  ?
+##
+_database_PDB_matrix.entry_id         1BNA
+_database_PDB_matrix.origx[1][1]      1.000000
+_database_PDB_matrix.origx[1][2]      0.000000
+_database_PDB_matrix.origx[1][3]      0.000000
+_database_PDB_matrix.origx[2][1]      0.000000
+_database_PDB_matrix.origx[2][2]      1.000000
+_database_PDB_matrix.origx[2][3]      0.000000
+_database_PDB_matrix.origx[3][1]      0.000000
+_database_PDB_matrix.origx[3][2]      0.000000
+_database_PDB_matrix.origx[3][3]      1.000000
+_database_PDB_matrix.origx_vector[1]  0.00000
+_database_PDB_matrix.origx_vector[2]  0.00000
+_database_PDB_matrix.origx_vector[3]  0.00000
+##
+_atom_sites.entry_id                   1BNA
+_atom_sites.fract_transf_matrix[1][1]  0.040209
+_atom_sites.fract_transf_matrix[1][2]  0.000000
+_atom_sites.fract_transf_matrix[1][3]  0.000000
+_atom_sites.fract_transf_matrix[2][1]  0.000000
+_atom_sites.fract_transf_matrix[2][2]  0.024759
+_atom_sites.fract_transf_matrix[2][3]  0.000000
+_atom_sites.fract_transf_matrix[3][1]  0.000000
+_atom_sites.fract_transf_matrix[3][2]  0.000000
+_atom_sites.fract_transf_matrix[3][3]  0.015106
+_atom_sites.fract_transf_vector[1]     0.00000
+_atom_sites.fract_transf_vector[2]     0.00000
+_atom_sites.fract_transf_vector[3]     0.00000
+##
+loop_
+_atom_type.symbol
+C  
+N  
+O  
+P  
+##
+loop_
+_atom_site.group_PDB
+_atom_site.id
+_atom_site.type_symbol
+_atom_site.label_atom_id
+_atom_site.label_alt_id
+_atom_site.label_comp_id
+_atom_site.label_asym_id
+_atom_site.label_entity_id
+_atom_site.label_seq_id
+_atom_site.pdbx_PDB_ins_code
+_atom_site.Cartn_x
+_atom_site.Cartn_y
+_atom_site.Cartn_z
+_atom_site.occupancy
+_atom_site.B_iso_or_equiv
+_atom_site.pdbx_formal_charge
+_atom_site.auth_seq_id
+_atom_site.auth_comp_id
+_atom_site.auth_asym_id
+_atom_site.auth_atom_id
+_atom_site.pdbx_PDB_model_num
+ATOM      1  O  "O5'"  .  DC   A  1   1  ?  18.935  34.195   25.617  1.00  64.35  ?    1  DC   A  "O5'"  1  
+ATOM      2  C  "C5'"  .  DC   A  1   1  ?  19.130  33.921   24.219  1.00  44.69  ?    1  DC   A  "C5'"  1  
+ATOM      3  C  "C4'"  .  DC   A  1   1  ?  19.961  32.668   24.100  1.00  31.28  ?    1  DC   A  "C4'"  1  
+ATOM      4  O  "O4'"  .  DC   A  1   1  ?  19.360  31.583   24.852  1.00  37.45  ?    1  DC   A  "O4'"  1  
+ATOM      5  C  "C3'"  .  DC   A  1   1  ?  20.172  32.122   22.694  1.00  46.72  ?    1  DC   A  "C3'"  1  
+ATOM      6  O  "O3'"  .  DC   A  1   1  ?  21.350  31.325   22.681  1.00  48.89  ?    1  DC   A  "O3'"  1  
+ATOM      7  C  "C2'"  .  DC   A  1   1  ?  18.948  31.223   22.647  1.00  30.88  ?    1  DC   A  "C2'"  1  
+ATOM      8  C  "C1'"  .  DC   A  1   1  ?  19.231  30.482   23.944  1.00  36.58  ?    1  DC   A  "C1'"  1  
+ATOM      9  N  N1     .  DC   A  1   1  ?  18.070  29.661   24.380  1.00  40.51  ?    1  DC   A  N1     1  
+ATOM     10  C  C2     .  DC   A  1   1  ?  18.224  28.454   25.015  1.00  16.62  ?    1  DC   A  C2     1  
+ATOM     11  O  O2     .  DC   A  1   1  ?  19.360  28.014   25.214  1.00  27.75  ?    1  DC   A  O2     1  
+ATOM     12  N  N3     .  DC   A  1   1  ?  17.143  27.761   25.377  1.00  20.55  ?    1  DC   A  N3     1  
+ATOM     13  C  C4     .  DC   A  1   1  ?  15.917  28.226   25.120  1.00  34.72  ?    1  DC   A  C4     1  
+ATOM     14  N  N4     .  DC   A  1   1  ?  14.828  27.477   25.444  1.00  40.31  ?    1  DC   A  N4     1  
+ATOM     15  C  C5     .  DC   A  1   1  ?  15.719  29.442   24.471  1.00  30.78  ?    1  DC   A  C5     1  
+ATOM     16  C  C6     .  DC   A  1   1  ?  16.843  30.171   24.101  1.00  25.90  ?    1  DC   A  C6     1  
+ATOM     17  P  P      .  DG   A  1   2  ?  22.409  31.286   21.483  1.00  58.85  ?    2  DG   A  P      1  
+ATOM     18  O  OP1    .  DG   A  1   2  ?  23.536  32.157   21.851  1.00  57.82  ?    2  DG   A  OP1    1  
+ATOM     19  O  OP2    .  DG   A  1   2  ?  21.822  31.459   20.139  1.00  78.33  ?    2  DG   A  OP2    1  
+ATOM     20  O  "O5'"  .  DG   A  1   2  ?  22.840  29.751   21.498  1.00  40.36  ?    2  DG   A  "O5'"  1  
+ATOM     21  C  "C5'"  .  DG   A  1   2  ?  23.543  29.175   22.594  1.00  47.19  ?    2  DG   A  "C5'"  1  
+ATOM     22  C  "C4'"  .  DG   A  1   2  ?  23.494  27.709   22.279  1.00  47.81  ?    2  DG   A  "C4'"  1  
+ATOM     23  O  "O4'"  .  DG   A  1   2  ?  22.193  27.252   22.674  1.00  38.76  ?    2  DG   A  "O4'"  1  
+ATOM     24  C  "C3'"  .  DG   A  1   2  ?  23.693  27.325   20.807  1.00  28.58  ?    2  DG   A  "C3'"  1  
+ATOM     25  O  "O3'"  .  DG   A  1   2  ?  24.723  26.320   20.653  1.00  40.44  ?    2  DG   A  "O3'"  1  
+ATOM     26  C  "C2'"  .  DG   A  1   2  ?  22.273  26.885   20.416  1.00  21.14  ?    2  DG   A  "C2'"  1  
+ATOM     27  C  "C1'"  .  DG   A  1   2  ?  21.721  26.304   21.716  1.00  33.95  ?    2  DG   A  "C1'"  1  
+ATOM     28  N  N9     .  DG   A  1   2  ?  20.237  26.470   21.780  1.00  34.00  ?    2  DG   A  N9     1  
+ATOM     29  C  C8     .  DG   A  1   2  ?  19.526  27.584   21.429  1.00  36.47  ?    2  DG   A  C8     1  
+ATOM     30  N  N7     .  DG   A  1   2  ?  18.207  27.455   21.636  1.00  32.37  ?    2  DG   A  N7     1  
+ATOM     31  C  C5     .  DG   A  1   2  ?  18.083  26.212   22.142  1.00  15.06  ?    2  DG   A  C5     1  
+ATOM     32  C  C6     .  DG   A  1   2  ?  16.904  25.525   22.545  1.00  11.88  ?    2  DG   A  C6     1  
+ATOM     33  O  O6     .  DG   A  1   2  ?  15.739  25.916   22.518  1.00  21.30  ?    2  DG   A  O6     1  
+ATOM     34  N  N1     .  DG   A  1   2  ?  17.197  24.279   23.037  1.00  15.44  ?    2  DG   A  N1     1  
+ATOM     35  C  C2     .  DG   A  1   2  ?  18.434  23.717   23.155  1.00   9.63  ?    2  DG   A  C2     1  
+ATOM     36  N  N2     .  DG   A  1   2  ?  18.508  22.456   23.668  1.00  16.69  ?    2  DG   A  N2     1  
+ATOM     37  N  N3     .  DG   A  1   2  ?  19.537  24.360   22.770  1.00  30.98  ?    2  DG   A  N3     1  
+ATOM     38  C  C4     .  DG   A  1   2  ?  19.290  25.594   22.274  1.00  18.56  ?    2  DG   A  C4     1  
+ATOM     39  P  P      .  DC   A  1   3  ?  25.064  25.621   19.252  1.00  44.67  ?    3  DC   A  P      1  
+ATOM     40  O  OP1    .  DC   A  1   3  ?  26.506  25.316   19.220  1.00  53.89  ?    3  DC   A  OP1    1  
+ATOM     41  O  OP2    .  DC   A  1   3  ?  24.559  26.412   18.115  1.00  57.79  ?    3  DC   A  OP2    1  
+ATOM     42  O  "O5'"  .  DC   A  1   3  ?  24.260  24.246   19.327  1.00  35.42  ?    3  DC   A  "O5'"  1  
+ATOM     43  C  "C5'"  .  DC   A  1   3  ?  24.584  23.285   20.335  1.00  45.75  ?    3  DC   A  "C5'"  1  
+ATOM     44  C  "C4'"  .  DC   A  1   3  ?  23.523  22.233   20.245  1.00  43.02  ?    3  DC   A  "C4'"  1  
+ATOM     45  O  "O4'"  .  DC   A  1   3  ?  22.256  22.844   20.453  1.00  36.85  ?    3  DC   A  "O4'"  1  
+ATOM     46  C  "C3'"  .  DC   A  1   3  ?  23.424  21.557   18.903  1.00  40.14  ?    3  DC   A  "C3'"  1  
+ATOM     47  O  "O3'"  .  DC   A  1   3  ?  24.121  20.309   18.928  1.00  49.62  ?    3  DC   A  "O3'"  1  
+ATOM     48  C  "C2'"  .  DC   A  1   3  ?  21.930  21.406   18.661  1.00  53.79  ?    3  DC   A  "C2'"  1  
+ATOM     49  C  "C1'"  .  DC   A  1   3  ?  21.278  21.966   19.909  1.00  22.18  ?    3  DC   A  "C1'"  1  
+ATOM     50  N  N1     .  DC   A  1   3  ?  20.196  22.889   19.521  1.00  25.44  ?    3  DC   A  N1     1  
+ATOM     51  C  C2     .  DC   A  1   3  ?  18.909  22.584   19.816  1.00  19.81  ?    3  DC   A  C2     1  
+ATOM     52  O  O2     .  DC   A  1   3  ?  18.685  21.512   20.382  1.00  29.92  ?    3  DC   A  O2     1  
+ATOM     53  N  N3     .  DC   A  1   3  ?  17.935  23.447   19.502  1.00  21.59  ?    3  DC   A  N3     1  
+ATOM     54  C  C4     .  DC   A  1   3  ?  18.217  24.603   18.897  1.00  14.01  ?    3  DC   A  C4     1  
+ATOM     55  N  N4     .  DC   A  1   3  ?  17.221  25.499   18.629  1.00  26.88  ?    3  DC   A  N4     1  
+ATOM     56  C  C5     .  DC   A  1   3  ?  19.526  24.945   18.571  1.00  27.59  ?    3  DC   A  C5     1  
+ATOM     57  C  C6     .  DC   A  1   3  ?  20.537  24.048   18.899  1.00  27.05  ?    3  DC   A  C6     1  
+ATOM     58  P  P      .  DG   A  1   4  ?  24.249  19.412   17.617  1.00  44.54  ?    4  DG   A  P      1  
+ATOM     59  O  OP1    .  DG   A  1   4  ?  25.420  18.535   17.765  1.00  61.90  ?    4  DG   A  OP1    1  
+ATOM     60  O  OP2    .  DG   A  1   4  ?  24.208  20.296   16.440  1.00  37.36  ?    4  DG   A  OP2    1  
+ATOM     61  O  "O5'"  .  DG   A  1   4  ?  22.931  18.537   17.670  1.00  32.01  ?    4  DG   A  "O5'"  1  
+ATOM     62  C  "C5'"  .  DG   A  1   4  ?  22.714  17.625   18.753  1.00  37.89  ?    4  DG   A  "C5'"  1  
+ATOM     63  C  "C4'"  .  DG   A  1   4  ?  21.393  16.960   18.505  1.00  53.00  ?    4  DG   A  "C4'"  1  
+ATOM     64  O  "O4'"  .  DG   A  1   4  ?  20.353  17.952   18.496  1.00  38.79  ?    4  DG   A  "O4'"  1  
+ATOM     65  C  "C3'"  .  DG   A  1   4  ?  21.264  16.229   17.176  1.00  56.72  ?    4  DG   A  "C3'"  1  
+ATOM     66  O  "O3'"  .  DG   A  1   4  ?  20.284  15.214   17.238  1.00  64.12  ?    4  DG   A  "O3'"  1  
+ATOM     67  C  "C2'"  .  DG   A  1   4  ?  20.793  17.368   16.288  1.00  40.81  ?    4  DG   A  "C2'"  1  
+ATOM     68  C  "C1'"  .  DG   A  1   4  ?  19.716  17.901   17.218  1.00  30.52  ?    4  DG   A  "C1'"  1  
+ATOM     69  N  N9     .  DG   A  1   4  ?  19.305  19.281   16.869  1.00  28.53  ?    4  DG   A  N9     1  
+ATOM     70  C  C8     .  DG   A  1   4  ?  20.017  20.263   16.232  1.00  27.82  ?    4  DG   A  C8     1  
+ATOM     71  N  N7     .  DG   A  1   4  ?  19.313  21.394   16.077  1.00  28.01  ?    4  DG   A  N7     1  
+ATOM     72  C  C5     .  DG   A  1   4  ?  18.121  21.100   16.635  1.00  23.22  ?    4  DG   A  C5     1  
+ATOM     73  C  C6     .  DG   A  1   4  ?  16.952  21.904   16.749  1.00  29.21  ?    4  DG   A  C6     1  
+ATOM     74  O  O6     .  DG   A  1   4  ?  16.769  23.057   16.368  1.00  38.58  ?    4  DG   A  O6     1  
+ATOM     75  N  N1     .  DG   A  1   4  ?  15.933  21.214   17.352  1.00  27.94  ?    4  DG   A  N1     1  
+ATOM     76  C  C2     .  DG   A  1   4  ?  15.972  19.930   17.816  1.00  23.44  ?    4  DG   A  C2     1  
+ATOM     77  N  N2     .  DG   A  1   4  ?  14.831  19.416   18.353  1.00  42.64  ?    4  DG   A  N2     1  
+ATOM     78  N  N3     .  DG   A  1   4  ?  17.068  19.179   17.717  1.00  21.56  ?    4  DG   A  N3     1  
+ATOM     79  C  C4     .  DG   A  1   4  ?  18.084  19.825   17.121  1.00  23.44  ?    4  DG   A  C4     1  
+ATOM     80  P  P      .  DA   A  1   5  ?  20.356  13.969   16.245  1.00  57.01  ?    5  DA   A  P      1  
+ATOM     81  O  OP1    .  DA   A  1   5  ?  21.116  12.891   16.892  1.00  58.59  ?    5  DA   A  OP1    1  
+ATOM     82  O  OP2    .  DA   A  1   5  ?  20.837  14.423   14.910  1.00  51.96  ?    5  DA   A  OP2    1  
+ATOM     83  O  "O5'"  .  DA   A  1   5  ?  18.810  13.581   16.161  1.00  47.12  ?    5  DA   A  "O5'"  1  
+ATOM     84  C  "C5'"  .  DA   A  1   5  ?  18.015  13.569   17.362  1.00  47.67  ?    5  DA   A  "C5'"  1  
+ATOM     85  C  "C4'"  .  DA   A  1   5  ?  16.672  14.088   16.957  1.00  64.79  ?    5  DA   A  "C4'"  1  
+ATOM     86  O  "O4'"  .  DA   A  1   5  ?  16.842  15.447   16.561  1.00  47.60  ?    5  DA   A  "O4'"  1  
+ATOM     87  C  "C3'"  .  DA   A  1   5  ?  16.019  13.393   15.764  1.00  51.50  ?    5  DA   A  "C3'"  1  
+ATOM     88  O  "O3'"  .  DA   A  1   5  ?  14.762  12.796   16.120  1.00  52.18  ?    5  DA   A  "O3'"  1  
+ATOM     89  C  "C2'"  .  DA   A  1   5  ?  15.952  14.498   14.696  1.00  45.00  ?    5  DA   A  "C2'"  1  
+ATOM     90  C  "C1'"  .  DA   A  1   5  ?  15.851  15.732   15.569  1.00  26.88  ?    5  DA   A  "C1'"  1  
+ATOM     91  N  N9     .  DA   A  1   5  ?  16.391  16.916   14.867  1.00  16.69  ?    5  DA   A  N9     1  
+ATOM     92  C  C8     .  DA   A  1   5  ?  17.658  17.103   14.382  1.00  28.14  ?    5  DA   A  C8     1  
+ATOM     93  N  N7     .  DA   A  1   5  ?  17.863  18.346   13.913  1.00  34.85  ?    5  DA   A  N7     1  
+ATOM     94  C  C5     .  DA   A  1   5  ?  16.673  18.953   14.098  1.00  22.49  ?    5  DA   A  C5     1  
+ATOM     95  C  C6     .  DA   A  1   5  ?  16.230  20.279   13.819  1.00  18.12  ?    5  DA   A  C6     1  
+ATOM     96  N  N6     .  DA   A  1   5  ?  17.045  21.222   13.268  1.00  29.30  ?    5  DA   A  N6     1  
+ATOM     97  N  N1     .  DA   A  1   5  ?  14.966  20.578   14.118  1.00  27.61  ?    5  DA   A  N1     1  
+ATOM     98  C  C2     .  DA   A  1   5  ?  14.178  19.652   14.669  1.00  18.53  ?    5  DA   A  C2     1  
+ATOM     99  N  N3     .  DA   A  1   5  ?  14.463  18.392   14.984  1.00  29.16  ?    5  DA   A  N3     1  
+ATOM    100  C  C4     .  DA   A  1   5  ?  15.750  18.110   14.661  1.00  15.08  ?    5  DA   A  C4     1  
+ATOM    101  P  P      .  DA   A  1   6  ?  13.866  12.006   15.063  1.00  43.68  ?    6  DA   A  P      1  
+ATOM    102  O  OP1    .  DA   A  1   6  ?  13.028  11.039   15.800  1.00  42.55  ?    6  DA   A  OP1    1  
+ATOM    103  O  OP2    .  DA   A  1   6  ?  14.715  11.499   13.968  1.00  54.20  ?    6  DA   A  OP2    1  
+ATOM    104  O  "O5'"  .  DA   A  1   6  ?  12.879  13.111   14.480  1.00  28.20  ?    6  DA   A  "O5'"  1  
+ATOM    105  C  "C5'"  .  DA   A  1   6  ?  11.802  13.597   15.290  1.00  42.29  ?    6  DA   A  "C5'"  1  
+ATOM    106  C  "C4'"  .  DA   A  1   6  ?  11.111  14.603   14.435  1.00  33.23  ?    6  DA   A  "C4'"  1  
+ATOM    107  O  "O4'"  .  DA   A  1   6  ?  12.152  15.460   13.962  1.00  41.48  ?    6  DA   A  "O4'"  1  
+ATOM    108  C  "C3'"  .  DA   A  1   6  ?  10.417  14.070   13.187  1.00  18.16  ?    6  DA   A  "C3'"  1  
+ATOM    109  O  "O3'"  .  DA   A  1   6  ?   9.007  14.369   13.181  1.00  30.42  ?    6  DA   A  "O3'"  1  
+ATOM    110  C  "C2'"  .  DA   A  1   6  ?  11.240  14.692   12.061  1.00  52.97  ?    6  DA   A  "C2'"  1  
+ATOM    111  C  "C1'"  .  DA   A  1   6  ?  11.699  15.974   12.719  1.00  38.93  ?    6  DA   A  "C1'"  1  
+ATOM    112  N  N9     .  DA   A  1   6  ?  12.918  16.526   12.078  1.00  19.06  ?    6  DA   A  N9     1  
+ATOM    113  C  C8     .  DA   A  1   6  ?  14.115  15.899   11.868  1.00  17.83  ?    6  DA   A  C8     1  
+ATOM    114  N  N7     .  DA   A  1   6  ?  15.049  16.714   11.356  1.00  29.55  ?    6  DA   A  N7     1  
+ATOM    115  C  C5     .  DA   A  1   6  ?  14.416  17.901   11.246  1.00  19.88  ?    6  DA   A  C5     1  
+ATOM    116  C  C6     .  DA   A  1   6  ?  14.873  19.187   10.815  1.00  17.26  ?    6  DA   A  C6     1  
+ATOM    117  N  N6     .  DA   A  1   6  ?  16.161  19.418   10.427  1.00  19.85  ?    6  DA   A  N6     1  
+ATOM    118  N  N1     .  DA   A  1   6  ?  13.999  20.191   10.852  1.00  17.93  ?    6  DA   A  N1     1  
+ATOM    119  C  C2     .  DA   A  1   6  ?  12.753  19.962   11.272  1.00  23.00  ?    6  DA   A  C2     1  
+ATOM    120  N  N3     .  DA   A  1   6  ?  12.210  18.824   11.698  1.00  21.37  ?    6  DA   A  N3     1  
+ATOM    121  C  C4     .  DA   A  1   6  ?  13.116  17.823   11.657  1.00  15.93  ?    6  DA   A  C4     1  
+ATOM    122  P  P      .  DT   A  1   7  ?   8.081  14.050   11.915  1.00  40.72  ?    7  DT   A  P      1  
+ATOM    123  O  OP1    .  DT   A  1   7  ?   6.668  13.960   12.342  1.00  46.75  ?    7  DT   A  OP1    1  
+ATOM    124  O  OP2    .  DT   A  1   7  ?   8.600  12.894   11.137  1.00  42.53  ?    7  DT   A  OP2    1  
+ATOM    125  O  "O5'"  .  DT   A  1   7  ?   8.239  15.387   11.076  1.00  35.21  ?    7  DT   A  "O5'"  1  
+ATOM    126  C  "C5'"  .  DT   A  1   7  ?   7.907  16.635   11.686  1.00  34.88  ?    7  DT   A  "C5'"  1  
+ATOM    127  C  "C4'"  .  DT   A  1   7  ?   8.162  17.628   10.598  1.00  31.45  ?    7  DT   A  "C4'"  1  
+ATOM    128  O  "O4'"  .  DT   A  1   7  ?   9.543  17.580   10.279  1.00  46.82  ?    7  DT   A  "O4'"  1  
+ATOM    129  C  "C3'"  .  DT   A  1   7  ?   7.461  17.284    9.296  1.00  23.76  ?    7  DT   A  "C3'"  1  
+ATOM    130  O  "O3'"  .  DT   A  1   7  ?   6.251  18.034    9.162  1.00  44.27  ?    7  DT   A  "O3'"  1  
+ATOM    131  C  "C2'"  .  DT   A  1   7  ?   8.532  17.527    8.223  1.00  26.30  ?    7  DT   A  "C2'"  1  
+ATOM    132  C  "C1'"  .  DT   A  1   7  ?   9.644  18.209    9.019  1.00  28.96  ?    7  DT   A  "C1'"  1  
+ATOM    133  N  N1     .  DT   A  1   7  ?  11.021  17.903    8.565  1.00  20.47  ?    7  DT   A  N1     1  
+ATOM    134  C  C2     .  DT   A  1   7  ?  11.822  18.923    8.176  1.00  28.01  ?    7  DT   A  C2     1  
+ATOM    135  O  O2     .  DT   A  1   7  ?  11.383  20.077    8.143  1.00  40.01  ?    7  DT   A  O2     1  
+ATOM    136  N  N3     .  DT   A  1   7  ?  13.119  18.641    7.852  1.00  27.94  ?    7  DT   A  N3     1  
+ATOM    137  C  C4     .  DT   A  1   7  ?  13.633  17.372    7.882  1.00  15.14  ?    7  DT   A  C4     1  
+ATOM    138  O  O4     .  DT   A  1   7  ?  14.830  17.222    7.619  1.00  32.54  ?    7  DT   A  O4     1  
+ATOM    139  C  C5     .  DT   A  1   7  ?  12.781  16.325    8.235  1.00  10.83  ?    7  DT   A  C5     1  
+ATOM    140  C  C7     .  DT   A  1   7  ?  13.269  14.902    8.236  1.00  36.33  ?    7  DT   A  C7     1  
+ATOM    141  C  C6     .  DT   A  1   7  ?  11.465  16.616    8.594  1.00  12.19  ?    7  DT   A  C6     1  
+ATOM    142  P  P      .  DT   A  1   8  ?   5.384  17.990    7.824  1.00  49.10  ?    8  DT   A  P      1  
+ATOM    143  O  OP1    .  DT   A  1   8  ?   4.025  18.444    8.180  1.00  41.11  ?    8  DT   A  OP1    1  
+ATOM    144  O  OP2    .  DT   A  1   8  ?   5.458  16.668    7.160  1.00  39.21  ?    8  DT   A  OP2    1  
+ATOM    145  O  "O5'"  .  DT   A  1   8  ?   6.086  19.118    6.927  1.00  48.80  ?    8  DT   A  "O5'"  1  
+ATOM    146  C  "C5'"  .  DT   A  1   8  ?   6.146  20.478    7.418  1.00  34.73  ?    8  DT   A  "C5'"  1  
+ATOM    147  C  "C4'"  .  DT   A  1   8  ?   6.995  21.229    6.438  1.00  28.73  ?    8  DT   A  "C4'"  1  
+ATOM    148  O  "O4'"  .  DT   A  1   8  ?   8.188  20.458    6.284  1.00  39.07  ?    8  DT   A  "O4'"  1  
+ATOM    149  C  "C3'"  .  DT   A  1   8  ?   6.418  21.332    5.029  1.00  37.88  ?    8  DT   A  "C3'"  1  
+ATOM    150  O  "O3'"  .  DT   A  1   8  ?   5.967  22.667    4.696  1.00  52.04  ?    8  DT   A  "O3'"  1  
+ATOM    151  C  "C2'"  .  DT   A  1   8  ?   7.513  20.718    4.139  1.00  32.80  ?    8  DT   A  "C2'"  1  
+ATOM    152  C  "C1'"  .  DT   A  1   8  ?   8.736  20.855    5.034  1.00  36.58  ?    8  DT   A  "C1'"  1  
+ATOM    153  N  N1     .  DT   A  1   8  ?   9.823  19.876    4.759  1.00  24.57  ?    8  DT   A  N1     1  
+ATOM    154  C  C2     .  DT   A  1   8  ?  11.086  20.316    4.494  1.00  19.41  ?    8  DT   A  C2     1  
+ATOM    155  O  O2     .  DT   A  1   8  ?  11.324  21.516    4.389  1.00  32.74  ?    8  DT   A  O2     1  
+ATOM    156  N  N3     .  DT   A  1   8  ?  12.094  19.403    4.412  1.00  25.12  ?    8  DT   A  N3     1  
+ATOM    157  C  C4     .  DT   A  1   8  ?  11.876  18.060    4.551  1.00  31.35  ?    8  DT   A  C4     1  
+ATOM    158  O  O4     .  DT   A  1   8  ?  12.858  17.317    4.503  1.00  28.53  ?    8  DT   A  O4     1  
+ATOM    159  C  C5     .  DT   A  1   8  ?  10.569  17.611    4.765  1.00  22.80  ?    8  DT   A  C5     1  
+ATOM    160  C  C7     .  DT   A  1   8  ?  10.261  16.140    4.896  1.00  24.98  ?    8  DT   A  C7     1  
+ATOM    161  C  C6     .  DT   A  1   8  ?   9.545  18.548    4.904  1.00  20.28  ?    8  DT   A  C6     1  
+ATOM    162  P  P      .  DC   A  1   9  ?   5.531  23.071    3.209  1.00  48.97  ?    9  DC   A  P      1  
+ATOM    163  O  OP1    .  DC   A  1   9  ?   4.648  24.244    3.269  1.00  62.33  ?    9  DC   A  OP1    1  
+ATOM    164  O  OP2    .  DC   A  1   9  ?   5.010  21.905    2.470  1.00  51.53  ?    9  DC   A  OP2    1  
+ATOM    165  O  "O5'"  .  DC   A  1   9  ?   6.926  23.547    2.611  1.00  43.99  ?    9  DC   A  "O5'"  1  
+ATOM    166  C  "C5'"  .  DC   A  1   9  ?   7.636  24.627    3.249  1.00  50.86  ?    9  DC   A  "C5'"  1  
+ATOM    167  C  "C4'"  .  DC   A  1   9  ?   8.897  24.853    2.457  1.00  46.66  ?    9  DC   A  "C4'"  1  
+ATOM    168  O  "O4'"  .  DC   A  1   9  ?   9.638  23.627    2.448  1.00  42.69  ?    9  DC   A  "O4'"  1  
+ATOM    169  C  "C3'"  .  DC   A  1   9  ?   8.717  25.240    0.998  1.00  56.96  ?    9  DC   A  "C3'"  1  
+ATOM    170  O  "O3'"  .  DC   A  1   9  ?   9.470  26.414    0.667  1.00  63.54  ?    9  DC   A  "O3'"  1  
+ATOM    171  C  "C2'"  .  DC   A  1   9  ?   9.126  23.965    0.253  1.00  50.41  ?    9  DC   A  "C2'"  1  
+ATOM    172  C  "C1'"  .  DC   A  1   9  ?  10.241  23.483    1.157  1.00  41.08  ?    9  DC   A  "C1'"  1  
+ATOM    173  N  N1     .  DC   A  1   9  ?  10.524  22.022    1.015  1.00  37.23  ?    9  DC   A  N1     1  
+ATOM    174  C  C2     .  DC   A  1   9  ?  11.814  21.603    0.840  1.00  40.54  ?    9  DC   A  C2     1  
+ATOM    175  O  O2     .  DC   A  1   9  ?  12.691  22.447    0.670  1.00  43.89  ?    9  DC   A  O2     1  
+ATOM    176  N  N3     .  DC   A  1   9  ?  12.106  20.297    0.873  1.00  32.57  ?    9  DC   A  N3     1  
+ATOM    177  C  C4     .  DC   A  1   9  ?  11.141  19.395    1.046  1.00  24.65  ?    9  DC   A  C4     1  
+ATOM    178  N  N4     .  DC   A  1   9  ?  11.461  18.075    1.089  1.00  27.84  ?    9  DC   A  N4     1  
+ATOM    179  C  C5     .  DC   A  1   9  ?   9.803  19.775    1.177  1.00  17.61  ?    9  DC   A  C5     1  
+ATOM    180  C  C6     .  DC   A  1   9  ?   9.499  21.133    1.167  1.00  30.63  ?    9  DC   A  C6     1  
+ATOM    181  P  P      .  DG   A  1  10  ?   9.055  27.333   -0.581  1.00  65.48  ?   10  DG   A  P      1  
+ATOM    182  O  OP1    .  DG   A  1  10  ?   9.496  28.717   -0.258  1.00  59.09  ?   10  DG   A  OP1    1  
+ATOM    183  O  OP2    .  DG   A  1  10  ?   7.632  27.106   -0.947  1.00  45.71  ?   10  DG   A  OP2    1  
+ATOM    184  O  "O5'"  .  DG   A  1  10  ?   9.954  26.765   -1.771  1.00  70.30  ?   10  DG   A  "O5'"  1  
+ATOM    185  C  "C5'"  .  DG   A  1  10  ?  11.382  26.940   -1.720  1.00  71.73  ?   10  DG   A  "C5'"  1  
+ATOM    186  C  "C4'"  .  DG   A  1  10  ?  11.972  26.090   -2.802  1.00  58.69  ?   10  DG   A  "C4'"  1  
+ATOM    187  O  "O4'"  .  DG   A  1  10  ?  11.802  24.724   -2.404  1.00  41.03  ?   10  DG   A  "O4'"  1  
+ATOM    188  C  "C3'"  .  DG   A  1  10  ?  11.327  26.178   -4.188  1.00  45.61  ?   10  DG   A  "C3'"  1  
+ATOM    189  O  "O3'"  .  DG   A  1  10  ?  12.311  26.096   -5.214  1.00  52.70  ?   10  DG   A  "O3'"  1  
+ATOM    190  C  "C2'"  .  DG   A  1  10  ?  10.414  24.962   -4.186  1.00  36.02  ?   10  DG   A  "C2'"  1  
+ATOM    191  C  "C1'"  .  DG   A  1  10  ?  11.429  24.028   -3.587  1.00  50.90  ?   10  DG   A  "C1'"  1  
+ATOM    192  N  N9     .  DG   A  1  10  ?  10.890  22.713   -3.200  1.00  45.86  ?   10  DG   A  N9     1  
+ATOM    193  C  C8     .  DG   A  1  10  ?   9.616  22.315   -2.910  1.00  44.49  ?   10  DG   A  C8     1  
+ATOM    194  N  N7     .  DG   A  1  10  ?   9.541  21.009   -2.613  1.00  39.96  ?   10  DG   A  N7     1  
+ATOM    195  C  C5     .  DG   A  1  10  ?  10.818  20.588   -2.718  1.00  38.99  ?   10  DG   A  C5     1  
+ATOM    196  C  C6     .  DG   A  1  10  ?  11.376  19.292   -2.511  1.00  35.78  ?   10  DG   A  C6     1  
+ATOM    197  O  O6     .  DG   A  1  10  ?  10.813  18.252   -2.179  1.00  34.90  ?   10  DG   A  O6     1  
+ATOM    198  N  N1     .  DG   A  1  10  ?  12.729  19.299   -2.720  1.00  23.54  ?   10  DG   A  N1     1  
+ATOM    199  C  C2     .  DG   A  1  10  ?  13.498  20.365   -3.082  1.00   8.73  ?   10  DG   A  C2     1  
+ATOM    200  N  N2     .  DG   A  1  10  ?  14.834  20.169   -3.237  1.00  23.15  ?   10  DG   A  N2     1  
+ATOM    201  N  N3     .  DG   A  1  10  ?  12.982  21.573   -3.267  1.00  24.68  ?   10  DG   A  N3     1  
+ATOM    202  C  C4     .  DG   A  1  10  ?  11.656  21.601   -3.061  1.00  31.53  ?   10  DG   A  C4     1  
+ATOM    203  P  P      .  DC   A  1  11  ?  12.763  27.421   -5.980  1.00  60.62  ?   11  DC   A  P      1  
+ATOM    204  O  OP1    .  DC   A  1  11  ?  12.796  28.572   -5.049  1.00  63.74  ?   11  DC   A  OP1    1  
+ATOM    205  O  OP2    .  DC   A  1  11  ?  11.886  27.542   -7.164  1.00  52.44  ?   11  DC   A  OP2    1  
+ATOM    206  O  "O5'"  .  DC   A  1  11  ?  14.272  27.086   -6.366  1.00  57.57  ?   11  DC   A  "O5'"  1  
+ATOM    207  C  "C5'"  .  DC   A  1  11  ?  15.275  27.108   -5.318  1.00  54.70  ?   11  DC   A  "C5'"  1  
+ATOM    208  C  "C4'"  .  DC   A  1  11  ?  16.222  25.946   -5.510  1.00  72.51  ?   11  DC   A  "C4'"  1  
+ATOM    209  O  "O4'"  .  DC   A  1  11  ?  15.443  24.754   -5.397  1.00  47.18  ?   11  DC   A  "O4'"  1  
+ATOM    210  C  "C3'"  .  DC   A  1  11  ?  16.942  25.827   -6.848  1.00  29.82  ?   11  DC   A  "C3'"  1  
+ATOM    211  O  "O3'"  .  DC   A  1  11  ?  18.340  25.511   -6.701  1.00  43.53  ?   11  DC   A  "O3'"  1  
+ATOM    212  C  "C2'"  .  DC   A  1  11  ?  16.118  24.767   -7.578  1.00  51.34  ?   11  DC   A  "C2'"  1  
+ATOM    213  C  "C1'"  .  DC   A  1  11  ?  15.856  23.836   -6.414  1.00  30.07  ?   11  DC   A  "C1'"  1  
+ATOM    214  N  N1     .  DC   A  1  11  ?  14.672  22.975   -6.637  1.00  23.25  ?   11  DC   A  N1     1  
+ATOM    215  C  C2     .  DC   A  1  11  ?  14.802  21.628   -6.529  1.00  20.38  ?   11  DC   A  C2     1  
+ATOM    216  O  O2     .  DC   A  1  11  ?  15.924  21.178   -6.314  1.00  38.77  ?   11  DC   A  O2     1  
+ATOM    217  N  N3     .  DC   A  1  11  ?  13.723  20.842   -6.627  1.00  15.92  ?   11  DC   A  N3     1  
+ATOM    218  C  C4     .  DC   A  1  11  ?  12.515  21.373   -6.836  1.00  15.82  ?   11  DC   A  C4     1  
+ATOM    219  N  N4     .  DC   A  1  11  ?  11.410  20.574   -6.872  1.00  28.04  ?   11  DC   A  N4     1  
+ATOM    220  C  C5     .  DC   A  1  11  ?  12.348  22.744   -6.978  1.00  26.17  ?   11  DC   A  C5     1  
+ATOM    221  C  C6     .  DC   A  1  11  ?  13.470  23.558   -6.869  1.00  35.50  ?   11  DC   A  C6     1  
+ATOM    222  P  P      .  DG   A  1  12  ?  19.331  25.774   -7.925  1.00  55.98  ?   12  DG   A  P      1  
+ATOM    223  O  OP1    .  DG   A  1  12  ?  20.704  25.976   -7.408  1.00  45.83  ?   12  DG   A  OP1    1  
+ATOM    224  O  OP2    .  DG   A  1  12  ?  18.763  26.851   -8.758  1.00  44.26  ?   12  DG   A  OP2    1  
+ATOM    225  O  "O5'"  .  DG   A  1  12  ?  19.302  24.412   -8.763  1.00  62.63  ?   12  DG   A  "O5'"  1  
+ATOM    226  C  "C5'"  .  DG   A  1  12  ?  20.109  23.284   -8.359  1.00  69.50  ?   12  DG   A  "C5'"  1  
+ATOM    227  C  "C4'"  .  DG   A  1  12  ?  19.748  22.167   -9.299  1.00  39.92  ?   12  DG   A  "C4'"  1  
+ATOM    228  O  "O4'"  .  DG   A  1  12  ?  18.350  21.969   -9.139  1.00  32.00  ?   12  DG   A  "O4'"  1  
+ATOM    229  C  "C3'"  .  DG   A  1  12  ?  19.921  22.404  -10.815  1.00  50.39  ?   12  DG   A  "C3'"  1  
+ATOM    230  O  "O3'"  .  DG   A  1  12  ?  20.985  21.635  -11.401  1.00  64.13  ?   12  DG   A  "O3'"  1  
+ATOM    231  C  "C2'"  .  DG   A  1  12  ?  18.535  22.062  -11.381  1.00  36.18  ?   12  DG   A  "C2'"  1  
+ATOM    232  C  "C1'"  .  DG   A  1  12  ?  17.965  21.200  -10.269  1.00  24.79  ?   12  DG   A  "C1'"  1  
+ATOM    233  N  N9     .  DG   A  1  12  ?  16.493  21.220  -10.265  1.00  28.44  ?   12  DG   A  N9     1  
+ATOM    234  C  C8     .  DG   A  1  12  ?  15.663  22.289  -10.478  1.00  31.85  ?   12  DG   A  C8     1  
+ATOM    235  N  N7     .  DG   A  1  12  ?  14.368  21.958  -10.390  1.00  38.26  ?   12  DG   A  N7     1  
+ATOM    236  C  C5     .  DG   A  1  12  ?  14.388  20.640  -10.102  1.00  28.99  ?   12  DG   A  C5     1  
+ATOM    237  C  C6     .  DG   A  1  12  ?  13.301  19.742   -9.856  1.00  42.63  ?   12  DG   A  C6     1  
+ATOM    238  O  O6     .  DG   A  1  12  ?  12.091  19.967   -9.857  1.00  49.17  ?   12  DG   A  O6     1  
+ATOM    239  N  N1     .  DG   A  1  12  ?  13.750  18.466   -9.625  1.00  40.15  ?   12  DG   A  N1     1  
+ATOM    240  C  C2     .  DG   A  1  12  ?  15.042  18.043   -9.605  1.00  33.42  ?   12  DG   A  C2     1  
+ATOM    241  N  N2     .  DG   A  1  12  ?  15.259  16.717   -9.406  1.00  40.53  ?   12  DG   A  N2     1  
+ATOM    242  N  N3     .  DG   A  1  12  ?  16.061  18.885   -9.792  1.00  37.34  ?   12  DG   A  N3     1  
+ATOM    243  C  C4     .  DG   A  1  12  ?  15.660  20.156  -10.027  1.00  31.14  ?   12  DG   A  C4     1  
+ATOM    244  O  "O5'"  .  DC   B  1   1  ?   7.458  11.884   -9.070  1.00  66.23  ?   13  DC   B  "O5'"  1  
+ATOM    245  C  "C5'"  .  DC   B  1   1  ?   8.252  10.968   -9.854  1.00  71.49  ?   13  DC   B  "C5'"  1  
+ATOM    246  C  "C4'"  .  DC   B  1   1  ?   9.714  11.141   -9.512  1.00  56.82  ?   13  DC   B  "C4'"  1  
+ATOM    247  O  "O4'"  .  DC   B  1   1  ?  10.144  12.455   -9.908  1.00  57.92  ?   13  DC   B  "O4'"  1  
+ATOM    248  C  "C3'"  .  DC   B  1   1  ?  10.103  10.989   -8.055  1.00  34.34  ?   13  DC   B  "C3'"  1  
+ATOM    249  O  "O3'"  .  DC   B  1   1  ?  11.293  10.221   -7.904  1.00  42.11  ?   13  DC   B  "O3'"  1  
+ATOM    250  C  "C2'"  .  DC   B  1   1  ?  10.254  12.437   -7.607  1.00  29.08  ?   13  DC   B  "C2'"  1  
+ATOM    251  C  "C1'"  .  DC   B  1   1  ?  10.896  13.044   -8.837  1.00  38.40  ?   13  DC   B  "C1'"  1  
+ATOM    252  N  N1     .  DC   B  1   1  ?  10.575  14.487   -8.944  1.00  34.33  ?   13  DC   B  N1     1  
+ATOM    253  C  C2     .  DC   B  1   1  ?  11.559  15.430   -9.006  1.00  22.98  ?   13  DC   B  C2     1  
+ATOM    254  O  O2     .  DC   B  1   1  ?  12.725  15.066   -8.932  1.00  50.83  ?   13  DC   B  O2     1  
+ATOM    255  N  N3     .  DC   B  1   1  ?  11.246  16.714   -9.193  1.00  37.14  ?   13  DC   B  N3     1  
+ATOM    256  C  C4     .  DC   B  1   1  ?   9.980  17.088   -9.334  1.00  42.60  ?   13  DC   B  C4     1  
+ATOM    257  N  N4     .  DC   B  1   1  ?   9.698  18.395   -9.589  1.00  54.91  ?   13  DC   B  N4     1  
+ATOM    258  C  C5     .  DC   B  1   1  ?   8.939  16.162   -9.274  1.00  56.67  ?   13  DC   B  C5     1  
+ATOM    259  C  C6     .  DC   B  1   1  ?   9.265  14.824   -9.080  1.00  49.21  ?   13  DC   B  C6     1  
+ATOM    260  P  P      .  DG   B  1   2  ?  11.602   9.510   -6.502  1.00  60.42  ?   14  DG   B  P      1  
+ATOM    261  O  OP1    .  DG   B  1   2  ?  11.666   8.032   -6.664  1.00  57.44  ?   14  DG   B  OP1    1  
+ATOM    262  O  OP2    .  DG   B  1   2  ?  10.644  10.010   -5.494  1.00  46.07  ?   14  DG   B  OP2    1  
+ATOM    263  O  "O5'"  .  DG   B  1   2  ?  13.051  10.094   -6.177  1.00  50.94  ?   14  DG   B  "O5'"  1  
+ATOM    264  C  "C5'"  .  DG   B  1   2  ?  14.100  10.021   -7.156  1.00  34.84  ?   14  DG   B  "C5'"  1  
+ATOM    265  C  "C4'"  .  DG   B  1   2  ?  15.113  10.992   -6.657  1.00  48.06  ?   14  DG   B  "C4'"  1  
+ATOM    266  O  "O4'"  .  DG   B  1   2  ?  14.556  12.300   -6.755  1.00  37.01  ?   14  DG   B  "O4'"  1  
+ATOM    267  C  "C3'"  .  DG   B  1   2  ?  15.445  10.806   -5.189  1.00  50.58  ?   14  DG   B  "C3'"  1  
+ATOM    268  O  "O3'"  .  DG   B  1   2  ?  16.836  10.560   -5.013  1.00  51.98  ?   14  DG   B  "O3'"  1  
+ATOM    269  C  "C2'"  .  DG   B  1   2  ?  14.937  12.100   -4.529  1.00  40.32  ?   14  DG   B  "C2'"  1  
+ATOM    270  C  "C1'"  .  DG   B  1   2  ?  15.058  13.086   -5.671  1.00  46.69  ?   14  DG   B  "C1'"  1  
+ATOM    271  N  N9     .  DG   B  1   2  ?  14.036  14.140   -5.536  1.00  29.17  ?   14  DG   B  N9     1  
+ATOM    272  C  C8     .  DG   B  1   2  ?  12.710  13.957   -5.259  1.00  23.48  ?   14  DG   B  C8     1  
+ATOM    273  N  N7     .  DG   B  1   2  ?  12.016  15.103   -5.269  1.00  37.54  ?   14  DG   B  N7     1  
+ATOM    274  C  C5     .  DG   B  1   2  ?  12.937  16.041   -5.558  1.00  26.27  ?   14  DG   B  C5     1  
+ATOM    275  C  C6     .  DG   B  1   2  ?  12.761  17.451   -5.710  1.00  40.82  ?   14  DG   B  C6     1  
+ATOM    276  O  O6     .  DG   B  1   2  ?  11.723  18.111   -5.630  1.00  44.39  ?   14  DG   B  O6     1  
+ATOM    277  N  N1     .  DG   B  1   2  ?  13.952  18.079   -5.973  1.00  19.52  ?   14  DG   B  N1     1  
+ATOM    278  C  C2     .  DG   B  1   2  ?  15.171  17.485   -6.107  1.00  18.48  ?   14  DG   B  C2     1  
+ATOM    279  N  N2     .  DG   B  1   2  ?  16.244  18.292   -6.325  1.00  36.58  ?   14  DG   B  N2     1  
+ATOM    280  N  N3     .  DG   B  1   2  ?  15.329  16.161   -5.986  1.00  46.96  ?   14  DG   B  N3     1  
+ATOM    281  C  C4     .  DG   B  1   2  ?  14.179  15.499   -5.721  1.00  35.70  ?   14  DG   B  C4     1  
+ATOM    282  P  P      .  DC   B  1   3  ?  17.478  10.380   -3.569  1.00  46.26  ?   15  DC   B  P      1  
+ATOM    283  O  OP1    .  DC   B  1   3  ?  18.665   9.516   -3.729  1.00  46.07  ?   15  DC   B  OP1    1  
+ATOM    284  O  OP2    .  DC   B  1   3  ?  16.427   9.940   -2.633  1.00  40.43  ?   15  DC   B  OP2    1  
+ATOM    285  O  "O5'"  .  DC   B  1   3  ?  17.957  11.865   -3.208  1.00  40.97  ?   15  DC   B  "O5'"  1  
+ATOM    286  C  "C5'"  .  DC   B  1   3  ?  18.963  12.531   -3.996  1.00  28.78  ?   15  DC   B  "C5'"  1  
+ATOM    287  C  "C4'"  .  DC   B  1   3  ?  18.936  13.958   -3.536  1.00  32.84  ?   15  DC   B  "C4'"  1  
+ATOM    288  O  "O4'"  .  DC   B  1   3  ?  17.592  14.409   -3.622  1.00  37.24  ?   15  DC   B  "O4'"  1  
+ATOM    289  C  "C3'"  .  DC   B  1   3  ?  19.253  14.139   -2.066  1.00  43.98  ?   15  DC   B  "C3'"  1  
+ATOM    290  O  "O3'"  .  DC   B  1   3  ?  20.659  14.219   -1.858  1.00  40.90  ?   15  DC   B  "O3'"  1  
+ATOM    291  C  "C2'"  .  DC   B  1   3  ?  18.520  15.417   -1.728  1.00  36.26  ?   15  DC   B  "C2'"  1  
+ATOM    292  C  "C1'"  .  DC   B  1   3  ?  17.545  15.602   -2.872  1.00  20.54  ?   15  DC   B  "C1'"  1  
+ATOM    293  N  N1     .  DC   B  1   3  ?  16.145  15.696   -2.428  1.00  23.10  ?   15  DC   B  N1     1  
+ATOM    294  C  C2     .  DC   B  1   3  ?  15.507  16.886   -2.558  1.00  32.12  ?   15  DC   B  C2     1  
+ATOM    295  O  O2     .  DC   B  1   3  ?  16.162  17.846   -2.957  1.00  30.04  ?   15  DC   B  O2     1  
+ATOM    296  N  N3     .  DC   B  1   3  ?  14.209  16.983   -2.264  1.00  32.94  ?   15  DC   B  N3     1  
+ATOM    297  C  C4     .  DC   B  1   3  ?  13.536  15.919   -1.825  1.00  16.43  ?   15  DC   B  C4     1  
+ATOM    298  N  N4     .  DC   B  1   3  ?  12.205  16.017   -1.553  1.00  34.91  ?   15  DC   B  N4     1  
+ATOM    299  C  C5     .  DC   B  1   3  ?  14.164  14.689   -1.652  1.00  22.75  ?   15  DC   B  C5     1  
+ATOM    300  C  C6     .  DC   B  1   3  ?  15.509  14.584   -1.979  1.00  26.42  ?   15  DC   B  C6     1  
+ATOM    301  P  P      .  DG   B  1   4  ?  21.304  14.529   -0.436  1.00  42.39  ?   16  DG   B  P      1  
+ATOM    302  O  OP1    .  DG   B  1   4  ?  22.696  14.087   -0.524  1.00  60.41  ?   16  DG   B  OP1    1  
+ATOM    303  O  OP2    .  DG   B  1   4  ?  20.488  13.954    0.650  1.00  51.09  ?   16  DG   B  OP2    1  
+ATOM    304  O  "O5'"  .  DG   B  1   4  ?  21.306  16.117   -0.363  1.00  45.08  ?   16  DG   B  "O5'"  1  
+ATOM    305  C  "C5'"  .  DG   B  1   4  ?  22.177  16.876   -1.212  1.00  33.20  ?   16  DG   B  "C5'"  1  
+ATOM    306  C  "C4'"  .  DG   B  1   4  ?  21.739  18.292   -1.021  1.00  24.95  ?   16  DG   B  "C4'"  1  
+ATOM    307  O  "O4'"  .  DG   B  1   4  ?  20.305  18.225   -1.048  1.00  32.83  ?   16  DG   B  "O4'"  1  
+ATOM    308  C  "C3'"  .  DG   B  1   4  ?  22.101  18.959    0.293  1.00  41.12  ?   16  DG   B  "C3'"  1  
+ATOM    309  O  "O3'"  .  DG   B  1   4  ?  22.592  20.293    0.097  1.00  53.45  ?   16  DG   B  "O3'"  1  
+ATOM    310  C  "C2'"  .  DG   B  1   4  ?  20.820  18.829    1.121  1.00  28.93  ?   16  DG   B  "C2'"  1  
+ATOM    311  C  "C1'"  .  DG   B  1   4  ?  19.765  18.985    0.046  1.00  37.44  ?   16  DG   B  "C1'"  1  
+ATOM    312  N  N9     .  DG   B  1   4  ?  18.513  18.299    0.468  1.00  17.75  ?   16  DG   B  N9     1  
+ATOM    313  C  C8     .  DG   B  1   4  ?  18.363  17.062    1.039  1.00  17.96  ?   16  DG   B  C8     1  
+ATOM    314  N  N7     .  DG   B  1   4  ?  17.080  16.744    1.281  1.00  24.14  ?   16  DG   B  N7     1  
+ATOM    315  C  C5     .  DG   B  1   4  ?  16.400  17.832    0.868  1.00   9.96  ?   16  DG   B  C5     1  
+ATOM    316  C  C6     .  DG   B  1   4  ?  14.996  18.090    0.882  1.00  18.10  ?   16  DG   B  C6     1  
+ATOM    317  O  O6     .  DG   B  1   4  ?  14.082  17.378    1.280  1.00  31.13  ?   16  DG   B  O6     1  
+ATOM    318  N  N1     .  DG   B  1   4  ?  14.712  19.349    0.418  1.00  17.72  ?   16  DG   B  N1     1  
+ATOM    319  C  C2     .  DG   B  1   4  ?  15.606  20.268   -0.027  1.00  16.23  ?   16  DG   B  C2     1  
+ATOM    320  N  N2     .  DG   B  1   4  ?  15.134  21.493   -0.382  1.00  33.42  ?   16  DG   B  N2     1  
+ATOM    321  N  N3     .  DG   B  1   4  ?  16.912  20.017   -0.072  1.00  26.37  ?   16  DG   B  N3     1  
+ATOM    322  C  C4     .  DG   B  1   4  ?  17.236  18.794    0.384  1.00  31.72  ?   16  DG   B  C4     1  
+ATOM    323  P  P      .  DA   B  1   5  ?  22.904  21.238    1.339  1.00  46.87  ?   17  DA   B  P      1  
+ATOM    324  O  OP1    .  DA   B  1   5  ?  23.994  22.183    1.025  1.00  47.75  ?   17  DA   B  OP1    1  
+ATOM    325  O  OP2    .  DA   B  1   5  ?  23.104  20.390    2.538  1.00  46.81  ?   17  DA   B  OP2    1  
+ATOM    326  O  "O5'"  .  DA   B  1   5  ?  21.577  22.107    1.390  1.00  39.51  ?   17  DA   B  "O5'"  1  
+ATOM    327  C  "C5'"  .  DA   B  1   5  ?  21.216  22.833    0.200  1.00  30.37  ?   17  DA   B  "C5'"  1  
+ATOM    328  C  "C4'"  .  DA   B  1   5  ?  20.101  23.788    0.484  1.00  35.43  ?   17  DA   B  "C4'"  1  
+ATOM    329  O  "O4'"  .  DA   B  1   5  ?  18.913  23.054    0.816  1.00  43.05  ?   17  DA   B  "O4'"  1  
+ATOM    330  C  "C3'"  .  DA   B  1   5  ?  20.347  24.743    1.633  1.00  44.50  ?   17  DA   B  "C3'"  1  
+ATOM    331  O  "O3'"  .  DA   B  1   5  ?  19.732  26.010    1.411  1.00  78.59  ?   17  DA   B  "O3'"  1  
+ATOM    332  C  "C2'"  .  DA   B  1   5  ?  19.752  23.945    2.791  1.00  44.42  ?   17  DA   B  "C2'"  1  
+ATOM    333  C  "C1'"  .  DA   B  1   5  ?  18.497  23.393    2.145  1.00  42.55  ?   17  DA   B  "C1'"  1  
+ATOM    334  N  N9     .  DA   B  1   5  ?  18.079  22.095    2.758  1.00  34.56  ?   17  DA   B  N9     1  
+ATOM    335  C  C8     .  DA   B  1   5  ?  18.847  21.020    3.133  1.00  20.07  ?   17  DA   B  C8     1  
+ATOM    336  N  N7     .  DA   B  1   5  ?  18.114  19.984    3.584  1.00  27.60  ?   17  DA   B  N7     1  
+ATOM    337  C  C5     .  DA   B  1   5  ?  16.842  20.424    3.488  1.00  18.80  ?   17  DA   B  C5     1  
+ATOM    338  C  C6     .  DA   B  1   5  ?  15.577  19.817    3.786  1.00  32.58  ?   17  DA   B  C6     1  
+ATOM    339  N  N6     .  DA   B  1   5  ?  15.448  18.537    4.242  1.00  29.54  ?   17  DA   B  N6     1  
+ATOM    340  N  N1     .  DA   B  1   5  ?  14.482  20.557    3.593  1.00  35.01  ?   17  DA   B  N1     1  
+ATOM    341  C  C2     .  DA   B  1   5  ?  14.597  21.801    3.118  1.00  36.47  ?   17  DA   B  C2     1  
+ATOM    342  N  N3     .  DA   B  1   5  ?  15.700  22.472    2.783  1.00  38.96  ?   17  DA   B  N3     1  
+ATOM    343  C  C4     .  DA   B  1   5  ?  16.791  21.706    3.002  1.00  28.24  ?   17  DA   B  C4     1  
+ATOM    344  P  P      .  DA   B  1   6  ?  19.803  27.141    2.526  1.00  46.11  ?   18  DA   B  P      1  
+ATOM    345  O  OP1    .  DA   B  1   6  ?  19.796  28.478    1.888  1.00  49.20  ?   18  DA   B  OP1    1  
+ATOM    346  O  OP2    .  DA   B  1   6  ?  20.953  26.858    3.426  1.00  43.48  ?   18  DA   B  OP2    1  
+ATOM    347  O  "O5'"  .  DA   B  1   6  ?  18.396  26.939    3.241  1.00  40.83  ?   18  DA   B  "O5'"  1  
+ATOM    348  C  "C5'"  .  DA   B  1   6  ?  17.203  27.028    2.452  1.00  40.72  ?   18  DA   B  "C5'"  1  
+ATOM    349  C  "C4'"  .  DA   B  1   6  ?  16.035  26.958    3.388  1.00  66.52  ?   18  DA   B  "C4'"  1  
+ATOM    350  O  "O4'"  .  DA   B  1   6  ?  15.856  25.612    3.850  1.00  44.25  ?   18  DA   B  "O4'"  1  
+ATOM    351  C  "C3'"  .  DA   B  1   6  ?  16.101  27.861    4.615  1.00  63.34  ?   18  DA   B  "C3'"  1  
+ATOM    352  O  "O3'"  .  DA   B  1   6  ?  14.890  28.608    4.757  1.00  55.65  ?   18  DA   B  "O3'"  1  
+ATOM    353  C  "C2'"  .  DA   B  1   6  ?  16.368  26.844    5.724  1.00  34.49  ?   18  DA   B  "C2'"  1  
+ATOM    354  C  "C1'"  .  DA   B  1   6  ?  15.561  25.655    5.243  1.00  29.45  ?   18  DA   B  "C1'"  1  
+ATOM    355  N  N9     .  DA   B  1   6  ?  16.104  24.373    5.755  1.00  20.03  ?   18  DA   B  N9     1  
+ATOM    356  C  C8     .  DA   B  1   6  ?  17.411  23.967    5.830  1.00  16.51  ?   18  DA   B  C8     1  
+ATOM    357  N  N7     .  DA   B  1   6  ?  17.539  22.706    6.276  1.00  20.58  ?   18  DA   B  N7     1  
+ATOM    358  C  C5     .  DA   B  1   6  ?  16.266  22.309    6.480  1.00  21.66  ?   18  DA   B  C5     1  
+ATOM    359  C  C6     .  DA   B  1   6  ?  15.715  21.073    6.933  1.00  17.93  ?   18  DA   B  C6     1  
+ATOM    360  N  N6     .  DA   B  1   6  ?  16.483  19.994    7.243  1.00  20.37  ?   18  DA   B  N6     1  
+ATOM    361  N  N1     .  DA   B  1   6  ?  14.389  20.994    7.036  1.00  20.81  ?   18  DA   B  N1     1  
+ATOM    362  C  C2     .  DA   B  1   6  ?  13.636  22.041    6.708  1.00  26.77  ?   18  DA   B  C2     1  
+ATOM    363  N  N3     .  DA   B  1   6  ?  14.019  23.234    6.265  1.00  26.83  ?   18  DA   B  N3     1  
+ATOM    364  C  C4     .  DA   B  1   6  ?  15.367  23.291    6.174  1.00  27.48  ?   18  DA   B  C4     1  
+ATOM    365  P  P      .  DT   B  1   7  ?  14.604  29.545    6.020  1.00  48.40  ?   19  DT   B  P      1  
+ATOM    366  O  OP1    .  DT   B  1   7  ?  13.792  30.696    5.582  1.00  50.18  ?   19  DT   B  OP1    1  
+ATOM    367  O  OP2    .  DT   B  1   7  ?  15.852  29.836    6.749  1.00  44.42  ?   19  DT   B  OP2    1  
+ATOM    368  O  "O5'"  .  DT   B  1   7  ?  13.633  28.628    6.885  1.00  53.86  ?   19  DT   B  "O5'"  1  
+ATOM    369  C  "C5'"  .  DT   B  1   7  ?  12.398  28.171    6.303  1.00  55.04  ?   19  DT   B  "C5'"  1  
+ATOM    370  C  "C4'"  .  DT   B  1   7  ?  11.809  27.217    7.302  1.00  44.86  ?   19  DT   B  "C4'"  1  
+ATOM    371  O  "O4'"  .  DT   B  1   7  ?  12.767  26.184    7.534  1.00  48.52  ?   19  DT   B  "O4'"  1  
+ATOM    372  C  "C3'"  .  DT   B  1   7  ?  11.515  27.822    8.669  1.00  41.77  ?   19  DT   B  "C3'"  1  
+ATOM    373  O  "O3'"  .  DT   B  1   7  ?  10.103  27.952    8.891  1.00  57.02  ?   19  DT   B  "O3'"  1  
+ATOM    374  C  "C2'"  .  DT   B  1   7  ?  12.267  26.906    9.630  1.00  39.28  ?   19  DT   B  "C2'"  1  
+ATOM    375  C  "C1'"  .  DT   B  1   7  ?  12.426  25.645    8.799  1.00  27.68  ?   19  DT   B  "C1'"  1  
+ATOM    376  N  N1     .  DT   B  1   7  ?  13.609  24.850    9.205  1.00  21.67  ?   19  DT   B  N1     1  
+ATOM    377  C  C2     .  DT   B  1   7  ?  13.442  23.575    9.656  1.00  31.71  ?   19  DT   B  C2     1  
+ATOM    378  O  O2     .  DT   B  1   7  ?  12.311  23.101    9.802  1.00  36.00  ?   19  DT   B  O2     1  
+ATOM    379  N  N3     .  DT   B  1   7  ?  14.551  22.825    9.913  1.00  24.66  ?   19  DT   B  N3     1  
+ATOM    380  C  C4     .  DT   B  1   7  ?  15.815  23.321    9.777  1.00  40.64  ?   19  DT   B  C4     1  
+ATOM    381  O  O4     .  DT   B  1   7  ?  16.755  22.570   10.029  1.00  31.47  ?   19  DT   B  O4     1  
+ATOM    382  C  C5     .  DT   B  1   7  ?  15.972  24.647    9.362  1.00  31.79  ?   19  DT   B  C5     1  
+ATOM    383  C  C7     .  DT   B  1   7  ?  17.345  25.239    9.234  1.00  30.05  ?   19  DT   B  C7     1  
+ATOM    384  C  C6     .  DT   B  1   7  ?  14.844  25.405    9.048  1.00  14.35  ?   19  DT   B  C6     1  
+ATOM    385  P  P      .  DT   B  1   8  ?   9.513  28.533   10.260  1.00  48.24  ?   20  DT   B  P      1  
+ATOM    386  O  OP1    .  DT   B  1   8  ?   8.145  29.007    9.998  1.00  41.28  ?   20  DT   B  OP1    1  
+ATOM    387  O  OP2    .  DT   B  1   8  ?  10.455  29.513   10.841  1.00  53.39  ?   20  DT   B  OP2    1  
+ATOM    388  O  "O5'"  .  DT   B  1   8  ?   9.395  27.223   11.153  1.00  36.57  ?   20  DT   B  "O5'"  1  
+ATOM    389  C  "C5'"  .  DT   B  1   8  ?   8.576  26.148   10.664  1.00  50.41  ?   20  DT   B  "C5'"  1  
+ATOM    390  C  "C4'"  .  DT   B  1   8  ?   8.655  25.060   11.678  1.00  32.08  ?   20  DT   B  "C4'"  1  
+ATOM    391  O  "O4'"  .  DT   B  1   8  ?  10.003  24.615   11.764  1.00  48.38  ?   20  DT   B  "O4'"  1  
+ATOM    392  C  "C3'"  .  DT   B  1   8  ?   8.272  25.471   13.087  1.00  29.99  ?   20  DT   B  "C3'"  1  
+ATOM    393  O  "O3'"  .  DT   B  1   8  ?   7.199  24.657   13.553  1.00  45.14  ?   20  DT   B  "O3'"  1  
+ATOM    394  C  "C2'"  .  DT   B  1   8  ?   9.586  25.307   13.860  1.00  32.42  ?   20  DT   B  "C2'"  1  
+ATOM    395  C  "C1'"  .  DT   B  1   8  ?  10.190  24.148   13.089  1.00  39.56  ?   20  DT   B  "C1'"  1  
+ATOM    396  N  N1     .  DT   B  1   8  ?  11.660  24.070   13.205  1.00  20.36  ?   20  DT   B  N1     1  
+ATOM    397  C  C2     .  DT   B  1   8  ?  12.257  22.880   13.486  1.00  27.55  ?   20  DT   B  C2     1  
+ATOM    398  O  O2     .  DT   B  1   8  ?  11.583  21.866   13.691  1.00  38.33  ?   20  DT   B  O2     1  
+ATOM    399  N  N3     .  DT   B  1   8  ?  13.620  22.829   13.497  1.00  29.60  ?   20  DT   B  N3     1  
+ATOM    400  C  C4     .  DT   B  1   8  ?  14.402  23.914   13.225  1.00  30.11  ?   20  DT   B  C4     1  
+ATOM    401  O  O4     .  DT   B  1   8  ?  15.625  23.764   13.252  1.00  32.92  ?   20  DT   B  O4     1  
+ATOM    402  C  C5     .  DT   B  1   8  ?  13.774  25.126   12.933  1.00  24.11  ?   20  DT   B  C5     1  
+ATOM    403  C  C7     .  DT   B  1   8  ?  14.563  26.358   12.612  1.00  23.96  ?   20  DT   B  C7     1  
+ATOM    404  C  C6     .  DT   B  1   8  ?  12.385  25.187   12.926  1.00  19.78  ?   20  DT   B  C6     1  
+ATOM    405  P  P      .  DC   B  1   9  ?   6.594  24.823   15.016  1.00  54.73  ?   21  DC   B  P      1  
+ATOM    406  O  OP1    .  DC   B  1   9  ?   5.169  24.424   14.987  1.00  53.98  ?   21  DC   B  OP1    1  
+ATOM    407  O  OP2    .  DC   B  1   9  ?   6.870  26.189   15.511  1.00  65.53  ?   21  DC   B  OP2    1  
+ATOM    408  O  "O5'"  .  DC   B  1   9  ?   7.409  23.731   15.839  1.00  50.67  ?   21  DC   B  "O5'"  1  
+ATOM    409  C  "C5'"  .  DC   B  1   9  ?   7.331  22.352   15.433  1.00  60.86  ?   21  DC   B  "C5'"  1  
+ATOM    410  C  "C4'"  .  DC   B  1   9  ?   8.100  21.598   16.461  1.00  40.86  ?   21  DC   B  "C4'"  1  
+ATOM    411  O  "O4'"  .  DC   B  1   9  ?   9.478  21.902   16.263  1.00  36.88  ?   21  DC   B  "O4'"  1  
+ATOM    412  C  "C3'"  .  DC   B  1   9  ?   7.766  22.045   17.879  1.00  53.80  ?   21  DC   B  "C3'"  1  
+ATOM    413  O  "O3'"  .  DC   B  1   9  ?   7.036  21.041   18.611  1.00  79.04  ?   21  DC   B  "O3'"  1  
+ATOM    414  C  "C2'"  .  DC   B  1   9  ?   9.123  22.414   18.469  1.00  48.43  ?   21  DC   B  "C2'"  1  
+ATOM    415  C  "C1'"  .  DC   B  1   9  ?  10.107  21.743   17.523  1.00  36.51  ?   21  DC   B  "C1'"  1  
+ATOM    416  N  N1     .  DC   B  1   9  ?  11.328  22.556   17.331  1.00  24.72  ?   21  DC   B  N1     1  
+ATOM    417  C  C2     .  DC   B  1   9  ?  12.534  21.939   17.329  1.00  30.96  ?   21  DC   B  C2     1  
+ATOM    418  O  O2     .  DC   B  1   9  ?  12.560  20.731   17.579  1.00  34.53  ?   21  DC   B  O2     1  
+ATOM    419  N  N3     .  DC   B  1   9  ?  13.639  22.639   17.035  1.00  31.69  ?   21  DC   B  N3     1  
+ATOM    420  C  C4     .  DC   B  1   9  ?  13.560  23.938   16.739  1.00  21.53  ?   21  DC   B  C4     1  
+ATOM    421  N  N4     .  DC   B  1   9  ?  14.685  24.628   16.404  1.00  23.72  ?   21  DC   B  N4     1  
+ATOM    422  C  C5     .  DC   B  1   9  ?  12.338  24.609   16.736  1.00  30.74  ?   21  DC   B  C5     1  
+ATOM    423  C  C6     .  DC   B  1   9  ?  11.193  23.878   17.035  1.00  27.58  ?   21  DC   B  C6     1  
+ATOM    424  P  P      .  DG   B  1  10  ?   6.509  21.324   20.099  1.00  56.50  ?   22  DG   B  P      1  
+ATOM    425  O  OP1    .  DG   B  1  10  ?   5.387  20.397   20.396  1.00  50.81  ?   22  DG   B  OP1    1  
+ATOM    426  O  OP2    .  DG   B  1  10  ?   6.235  22.774   20.306  1.00  53.84  ?   22  DG   B  OP2    1  
+ATOM    427  O  "O5'"  .  DG   B  1  10  ?   7.767  20.924   20.993  1.00  66.30  ?   22  DG   B  "O5'"  1  
+ATOM    428  C  "C5'"  .  DG   B  1  10  ?   8.216  19.559   21.073  1.00  73.42  ?   22  DG   B  "C5'"  1  
+ATOM    429  C  "C4'"  .  DG   B  1  10  ?   9.422  19.557   21.977  1.00  42.96  ?   22  DG   B  "C4'"  1  
+ATOM    430  O  "O4'"  .  DG   B  1  10  ?  10.493  20.260   21.319  1.00  52.87  ?   22  DG   B  "O4'"  1  
+ATOM    431  C  "C3'"  .  DG   B  1  10  ?   9.267  20.267   23.325  1.00  38.51  ?   22  DG   B  "C3'"  1  
+ATOM    432  O  "O3'"  .  DG   B  1  10  ?  10.088  19.657   24.293  1.00  60.28  ?   22  DG   B  "O3'"  1  
+ATOM    433  C  "C2'"  .  DG   B  1  10  ?   9.751  21.670   22.990  1.00  22.00  ?   22  DG   B  "C2'"  1  
+ATOM    434  C  "C1'"  .  DG   B  1  10  ?  10.988  21.226   22.256  1.00  24.85  ?   22  DG   B  "C1'"  1  
+ATOM    435  N  N9     .  DG   B  1  10  ?  11.599  22.357   21.543  1.00  25.91  ?   22  DG   B  N9     1  
+ATOM    436  C  C8     .  DG   B  1  10  ?  11.037  23.545   21.159  1.00  23.91  ?   22  DG   B  C8     1  
+ATOM    437  N  N7     .  DG   B  1  10  ?  11.921  24.362   20.566  1.00  39.18  ?   22  DG   B  N7     1  
+ATOM    438  C  C5     .  DG   B  1  10  ?  13.072  23.653   20.580  1.00  25.66  ?   22  DG   B  C5     1  
+ATOM    439  C  C6     .  DG   B  1  10  ?  14.370  24.003   20.102  1.00  28.34  ?   22  DG   B  C6     1  
+ATOM    440  O  O6     .  DG   B  1  10  ?  14.747  25.057   19.585  1.00  31.85  ?   22  DG   B  O6     1  
+ATOM    441  N  N1     .  DG   B  1  10  ?  15.268  22.983   20.308  1.00  25.22  ?   22  DG   B  N1     1  
+ATOM    442  C  C2     .  DG   B  1  10  ?  15.023  21.776   20.891  1.00  11.07  ?   22  DG   B  C2     1  
+ATOM    443  N  N2     .  DG   B  1  10  ?  16.066  20.914   21.038  1.00  25.92  ?   22  DG   B  N2     1  
+ATOM    444  N  N3     .  DG   B  1  10  ?  13.815  21.452   21.350  1.00  19.05  ?   22  DG   B  N3     1  
+ATOM    445  C  C4     .  DG   B  1  10  ?  12.902  22.429   21.151  1.00  23.69  ?   22  DG   B  C4     1  
+ATOM    446  P  P      .  DC   B  1  11  ?   9.477  18.627   25.340  1.00  55.93  ?   23  DC   B  P      1  
+ATOM    447  O  OP1    .  DC   B  1  11  ?   8.767  17.534   24.627  1.00  45.14  ?   23  DC   B  OP1    1  
+ATOM    448  O  OP2    .  DC   B  1  11  ?   8.670  19.409   26.312  1.00  41.61  ?   23  DC   B  OP2    1  
+ATOM    449  O  "O5'"  .  DC   B  1  11  ?  10.807  18.067   26.034  1.00  59.70  ?   23  DC   B  "O5'"  1  
+ATOM    450  C  "C5'"  .  DC   B  1  11  ?  11.688  17.170   25.310  1.00  63.13  ?   23  DC   B  "C5'"  1  
+ATOM    451  C  "C4'"  .  DC   B  1  11  ?  13.115  17.573   25.593  1.00  27.86  ?   23  DC   B  "C4'"  1  
+ATOM    452  O  "O4'"  .  DC   B  1  11  ?  13.284  18.804   24.893  1.00  50.51  ?   23  DC   B  "O4'"  1  
+ATOM    453  C  "C3'"  .  DC   B  1  11  ?  13.441  17.879   27.059  1.00  46.45  ?   23  DC   B  "C3'"  1  
+ATOM    454  O  "O3'"  .  DC   B  1  11  ?  14.341  16.938   27.677  1.00  57.21  ?   23  DC   B  "O3'"  1  
+ATOM    455  C  "C2'"  .  DC   B  1  11  ?  13.928  19.322   27.025  1.00  68.01  ?   23  DC   B  "C2'"  1  
+ATOM    456  C  "C1'"  .  DC   B  1  11  ?  14.312  19.508   25.568  1.00  32.05  ?   23  DC   B  "C1'"  1  
+ATOM    457  N  N1     .  DC   B  1  11  ?  14.144  20.932   25.170  1.00  23.28  ?   23  DC   B  N1     1  
+ATOM    458  C  C2     .  DC   B  1  11  ?  15.199  21.595   24.630  1.00  20.62  ?   23  DC   B  C2     1  
+ATOM    459  O  O2     .  DC   B  1  11  ?  16.257  20.984   24.504  1.00  29.62  ?   23  DC   B  O2     1  
+ATOM    460  N  N3     .  DC   B  1  11  ?  15.067  22.877   24.257  1.00  39.00  ?   23  DC   B  N3     1  
+ATOM    461  C  C4     .  DC   B  1  11  ?  13.898  23.510   24.404  1.00  30.44  ?   23  DC   B  C4     1  
+ATOM    462  N  N4     .  DC   B  1  11  ?  13.771  24.813   24.018  1.00  34.66  ?   23  DC   B  N4     1  
+ATOM    463  C  C5     .  DC   B  1  11  ?  12.795  22.866   24.967  1.00  27.74  ?   23  DC   B  C5     1  
+ATOM    464  C  C6     .  DC   B  1  11  ?  12.935  21.540   25.359  1.00  24.58  ?   23  DC   B  C6     1  
+ATOM    465  P  P      .  DG   B  1  12  ?  14.658  17.064   29.247  1.00  53.70  ?   24  DG   B  P      1  
+ATOM    466  O  OP1    .  DG   B  1  12  ?  14.863  15.717   29.825  1.00  61.79  ?   24  DG   B  OP1    1  
+ATOM    467  O  OP2    .  DG   B  1  12  ?  13.633  17.912   29.920  1.00  36.06  ?   24  DG   B  OP2    1  
+ATOM    468  O  "O5'"  .  DG   B  1  12  ?  16.033  17.880   29.284  1.00  34.06  ?   24  DG   B  "O5'"  1  
+ATOM    469  C  "C5'"  .  DG   B  1  12  ?  17.243  17.320   28.742  1.00  46.57  ?   24  DG   B  "C5'"  1  
+ATOM    470  C  "C4'"  .  DG   B  1  12  ?  18.208  18.464   28.758  1.00  50.89  ?   24  DG   B  "C4'"  1  
+ATOM    471  O  "O4'"  .  DG   B  1  12  ?  17.716  19.428   27.829  1.00  32.02  ?   24  DG   B  "O4'"  1  
+ATOM    472  C  "C3'"  .  DG   B  1  12  ?  18.230  19.236   30.058  1.00  30.38  ?   24  DG   B  "C3'"  1  
+ATOM    473  O  "O3'"  .  DG   B  1  12  ?  18.978  18.583   31.084  1.00  61.06  ?   24  DG   B  "O3'"  1  
+ATOM    474  C  "C2'"  .  DG   B  1  12  ?  18.885  20.519   29.578  1.00  53.33  ?   24  DG   B  "C2'"  1  
+ATOM    475  C  "C1'"  .  DG   B  1  12  ?  18.276  20.693   28.188  1.00  35.03  ?   24  DG   B  "C1'"  1  
+ATOM    476  N  N9     .  DG   B  1  12  ?  17.164  21.659   28.139  1.00  30.25  ?   24  DG   B  N9     1  
+ATOM    477  C  C8     .  DG   B  1  12  ?  15.874  21.536   28.580  1.00  30.86  ?   24  DG   B  C8     1  
+ATOM    478  N  N7     .  DG   B  1  12  ?  15.129  22.614   28.308  1.00  44.08  ?   24  DG   B  N7     1  
+ATOM    479  C  C5     .  DG   B  1  12  ?  15.990  23.436   27.673  1.00  16.87  ?   24  DG   B  C5     1  
+ATOM    480  C  C6     .  DG   B  1  12  ?  15.765  24.729   27.117  1.00  19.36  ?   24  DG   B  C6     1  
+ATOM    481  O  O6     .  DG   B  1  12  ?  14.719  25.373   27.067  1.00  33.30  ?   24  DG   B  O6     1  
+ATOM    482  N  N1     .  DG   B  1  12  ?  16.926  25.257   26.604  1.00  15.78  ?   24  DG   B  N1     1  
+ATOM    483  C  C2     .  DG   B  1  12  ?  18.157  24.666   26.579  1.00  11.92  ?   24  DG   B  C2     1  
+ATOM    484  N  N2     .  DG   B  1  12  ?  19.208  25.386   26.096  1.00  29.76  ?   24  DG   B  N2     1  
+ATOM    485  N  N3     .  DG   B  1  12  ?  18.350  23.438   27.053  1.00  21.95  ?   24  DG   B  N3     1  
+ATOM    486  C  C4     .  DG   B  1  12  ?  17.231  22.893   27.570  1.00  13.89  ?   24  DG   B  C4     1  
+HETATM  487  O  O      .  HOH  C  2   .  ?  19.736  30.706   18.656  1.00  51.86  ?   25  HOH  A  O      1  
+HETATM  488  O  O      .  HOH  C  2   .  ?  10.879  26.039   -8.906  1.00  47.07  ?   31  HOH  A  O      1  
+HETATM  489  O  O      .  HOH  C  2   .  ?  18.320  24.816   14.948  1.00  47.72  ?   32  HOH  A  O      1  
+HETATM  490  O  O      .  HOH  C  2   .  ?   9.821  13.442    8.572  1.00  45.76  ?   36  HOH  A  O      1  
+HETATM  491  O  O      .  HOH  C  2   .  ?   8.915  15.602   -3.388  1.00  50.97  ?   38  HOH  A  O      1  
+HETATM  492  O  O      .  HOH  C  2   .  ?  17.505  26.340  -10.581  1.00  51.90  ?   39  HOH  A  O      1  
+HETATM  493  O  O      .  HOH  C  2   .  ?  28.496  23.515   18.349  1.00  45.37  ?   40  HOH  A  O      1  
+HETATM  494  O  O      .  HOH  C  2   .  ?  11.346  24.175    4.920  1.00  45.03  ?   41  HOH  A  O      1  
+HETATM  495  O  O      .  HOH  C  2   .  ?   9.098  16.119    1.277  1.00  51.80  ?   50  HOH  A  O      1  
+HETATM  496  O  O      .  HOH  C  2   .  ?  16.488  29.195   19.861  1.00  54.92  ?   54  HOH  A  O      1  
+HETATM  497  O  O      .  HOH  C  2   .  ?  22.078  25.894   15.396  1.00  62.20  ?   55  HOH  A  O      1  
+HETATM  498  O  O      .  HOH  C  2   .  ?   7.133  14.448    4.647  1.00  57.15  ?   58  HOH  A  O      1  
+HETATM  499  O  O      .  HOH  C  2   .  ?  14.095  28.151   21.614  1.00  53.85  ?   62  HOH  A  O      1  
+HETATM  500  O  O      .  HOH  C  2   .  ?  27.164  31.710   20.331  1.00  56.84  ?   64  HOH  A  O      1  
+HETATM  501  O  O      .  HOH  C  2   .  ?  15.295  11.873   12.209  1.00  57.34  ?   65  HOH  A  O      1  
+HETATM  502  O  O      .  HOH  C  2   .  ?  18.180  16.604    9.966  1.00  61.52  ?   66  HOH  A  O      1  
+HETATM  503  O  O      .  HOH  C  2   .  ?   6.216  17.035    1.672  1.00  62.91  ?   67  HOH  A  O      1  
+HETATM  504  O  O      .  HOH  C  2   .  ?   7.055  25.519   -2.053  1.00  55.96  ?   70  HOH  A  O      1  
+HETATM  505  O  O      .  HOH  C  2   .  ?  12.454  11.354    9.415  1.00  68.40  ?   74  HOH  A  O      1  
+HETATM  506  O  O      .  HOH  C  2   .  ?  11.492  29.103   20.090  1.00  67.46  ?   76  HOH  A  O      1  
+HETATM  507  O  O      .  HOH  C  2   .  ?  14.220  29.189   20.392  1.00  48.22  ?   77  HOH  A  O      1  
+HETATM  508  O  O      .  HOH  C  2   .  ?   6.138  19.149   13.844  1.00  62.26  ?   78  HOH  A  O      1  
+HETATM  509  O  O      .  HOH  C  2   .  ?  17.315   9.638   13.392  1.00  65.70  ?   79  HOH  A  O      1  
+HETATM  510  O  O      .  HOH  C  2   .  ?  18.951  25.757   12.989  1.00  66.47  ?   80  HOH  A  O      1  
+HETATM  511  O  O      .  HOH  C  2   .  ?  20.460  18.861   12.664  1.00  63.00  ?   81  HOH  A  O      1  
+HETATM  512  O  O      .  HOH  C  2   .  ?   3.529  19.338   12.599  1.00  65.32  ?   82  HOH  A  O      1  
+HETATM  513  O  O      .  HOH  C  2   .  ?  16.223  12.351    9.406  1.00  63.59  ?   84  HOH  A  O      1  
+HETATM  514  O  O      .  HOH  C  2   .  ?  12.989  29.901   -9.282  1.00  64.97  ?   85  HOH  A  O      1  
+HETATM  515  O  O      .  HOH  C  2   .  ?  17.510  30.569   18.702  1.00  61.79  ?   86  HOH  A  O      1  
+HETATM  516  O  O      .  HOH  C  2   .  ?  25.377  12.891   19.011  1.00  73.80  ?   87  HOH  A  O      1  
+HETATM  517  O  O      .  HOH  C  2   .  ?  13.610  15.742   18.593  1.00  69.48  ?   88  HOH  A  O      1  
+HETATM  518  O  O      .  HOH  C  2   .  ?  18.012  32.598   15.262  1.00  67.52  ?   89  HOH  A  O      1  
+HETATM  519  O  O      .  HOH  C  2   .  ?   8.723  13.216    6.359  1.00  70.66  ?   92  HOH  A  O      1  
+HETATM  520  O  O      .  HOH  C  2   .  ?  18.779  13.814   11.704  1.00  71.14  ?   97  HOH  A  O      1  
+HETATM  521  O  O      .  HOH  C  2   .  ?  12.227  25.192  -10.299  1.00  70.46  ?   99  HOH  A  O      1  
+HETATM  522  O  O      .  HOH  C  2   .  ?  12.292  30.291   27.102  1.00  73.04  ?  100  HOH  A  O      1  
+HETATM  523  O  O      .  HOH  C  2   .  ?  20.170  23.000   12.999  1.00  73.63  ?  102  HOH  A  O      1  
+HETATM  524  O  O      .  HOH  D  2   .  ?  14.354  27.683   16.369  1.00  40.92  ?   26  HOH  B  O      1  
+HETATM  525  O  O      .  HOH  D  2   .  ?   9.864  22.509    9.123  1.00  39.67  ?   27  HOH  B  O      1  
+HETATM  526  O  O      .  HOH  D  2   .  ?  19.526  19.144    7.481  1.00  51.15  ?   28  HOH  B  O      1  
+HETATM  527  O  O      .  HOH  D  2   .  ?  25.754  12.744   -1.835  1.00  51.80  ?   29  HOH  B  O      1  
+HETATM  528  O  O      .  HOH  D  2   .  ?   7.478  20.604   -9.000  1.00  44.82  ?   30  HOH  B  O      1  
+HETATM  529  O  O      .  HOH  D  2   .  ?   9.012  24.586    7.009  1.00  43.42  ?   33  HOH  B  O      1  
+HETATM  530  O  O      .  HOH  D  2   .  ?  10.152  19.917   13.381  1.00  48.04  ?   34  HOH  B  O      1  
+HETATM  531  O  O      .  HOH  D  2   .  ?   7.764  21.397   11.075  1.00  41.41  ?   35  HOH  B  O      1  
+HETATM  532  O  O      .  HOH  D  2   .  ?  13.239  14.428    2.049  1.00  55.54  ?   37  HOH  B  O      1  
+HETATM  533  O  O      .  HOH  D  2   .  ?  12.601  23.000   29.167  1.00  51.36  ?   42  HOH  B  O      1  
+HETATM  534  O  O      .  HOH  D  2   .  ?  10.440  25.542   24.443  1.00  56.79  ?   43  HOH  B  O      1  
+HETATM  535  O  O      .  HOH  D  2   .  ?  16.979  28.689   16.284  1.00  50.41  ?   44  HOH  B  O      1  
+HETATM  536  O  O      .  HOH  D  2   .  ?   4.794  22.966   13.368  1.00  45.95  ?   45  HOH  B  O      1  
+HETATM  537  O  O      .  HOH  D  2   .  ?   4.208  25.591   10.828  1.00  51.06  ?   46  HOH  B  O      1  
+HETATM  538  O  O      .  HOH  D  2   .  ?   6.362  24.374    9.188  1.00  51.85  ?   47  HOH  B  O      1  
+HETATM  539  O  O      .  HOH  D  2   .  ?   7.688  28.411    7.883  1.00  49.33  ?   48  HOH  B  O      1  
+HETATM  540  O  O      .  HOH  D  2   .  ?  18.379  17.074    4.809  1.00  50.72  ?   49  HOH  B  O      1  
+HETATM  541  O  O      .  HOH  D  2   .  ?  26.464  23.826    1.396  1.00  53.21  ?   51  HOH  B  O      1  
+HETATM  542  O  O      .  HOH  D  2   .  ?  11.014  11.318   -2.909  1.00  51.36  ?   52  HOH  B  O      1  
+HETATM  543  O  O      .  HOH  D  2   .  ?   9.476  27.782   26.498  1.00  60.04  ?   53  HOH  B  O      1  
+HETATM  544  O  O      .  HOH  D  2   .  ?   5.522  27.411    9.017  1.00  62.36  ?   56  HOH  B  O      1  
+HETATM  545  O  O      .  HOH  D  2   .  ?  18.456  28.409    8.821  1.00  59.63  ?   57  HOH  B  O      1  
+HETATM  546  O  O      .  HOH  D  2   .  ?  22.610  15.544    3.846  1.00  57.52  ?   59  HOH  B  O      1  
+HETATM  547  O  O      .  HOH  D  2   .  ?  24.407  13.162    2.229  1.00  52.30  ?   60  HOH  B  O      1  
+HETATM  548  O  O      .  HOH  D  2   .  ?   7.988  11.556   -2.976  1.00  59.14  ?   61  HOH  B  O      1  
+HETATM  549  O  O      .  HOH  D  2   .  ?  14.213  27.722   18.905  1.00  57.29  ?   63  HOH  B  O      1  
+HETATM  550  O  O      .  HOH  D  2   .  ?  19.101  11.433    1.080  1.00  59.79  ?   68  HOH  B  O      1  
+HETATM  551  O  O      .  HOH  D  2   .  ?  12.607  10.967    0.261  1.00  60.87  ?   69  HOH  B  O      1  
+HETATM  552  O  O      .  HOH  D  2   .  ?  15.062  26.024   -0.766  1.00  56.35  ?   71  HOH  B  O      1  
+HETATM  553  O  O      .  HOH  D  2   .  ?  16.380   6.413   -4.784  1.00  59.07  ?   72  HOH  B  O      1  
+HETATM  554  O  O      .  HOH  D  2   .  ?  14.059   5.751   -6.198  1.00  56.68  ?   73  HOH  B  O      1  
+HETATM  555  O  O      .  HOH  D  2   .  ?   9.613  17.039   29.793  1.00  63.48  ?   75  HOH  B  O      1  
+HETATM  556  O  O      .  HOH  D  2   .  ?  25.276  15.890   -1.301  1.00  64.53  ?   83  HOH  B  O      1  
+HETATM  557  O  O      .  HOH  D  2   .  ?   2.622  23.030   10.332  1.00  68.01  ?   90  HOH  B  O      1  
+HETATM  558  O  O      .  HOH  D  2   .  ?  19.701  22.518    9.511  1.00  70.25  ?   91  HOH  B  O      1  
+HETATM  559  O  O      .  HOH  D  2   .  ?  19.727  29.488    6.155  1.00  69.43  ?   93  HOH  B  O      1  
+HETATM  560  O  O      .  HOH  D  2   .  ?  17.241  11.563    4.511  1.00  72.18  ?   94  HOH  B  O      1  
+HETATM  561  O  O      .  HOH  D  2   .  ?  26.545  19.404   -1.091  1.00  70.14  ?   95  HOH  B  O      1  
+HETATM  562  O  O      .  HOH  D  2   .  ?   9.697  18.315   14.885  1.00  69.10  ?   96  HOH  B  O      1  
+HETATM  563  O  O      .  HOH  D  2   .  ?  14.292  25.159    2.287  1.00  68.44  ?   98  HOH  B  O      1  
+HETATM  564  O  O      .  HOH  D  2   .  ?   9.396  27.092   16.993  1.00  72.98  ?  101  HOH  B  O      1  
+HETATM  565  O  O      .  HOH  D  2   .  ?  19.987  21.691    6.802  1.00  72.66  ?  103  HOH  B  O      1  
+HETATM  566  O  O      .  HOH  D  2   .  ?  18.692  31.584    4.596  1.00  72.98  ?  104  HOH  B  O      1  
+##
+loop_
+_pdbx_poly_seq_scheme.asym_id
+_pdbx_poly_seq_scheme.entity_id
+_pdbx_poly_seq_scheme.seq_id
+_pdbx_poly_seq_scheme.mon_id
+_pdbx_poly_seq_scheme.ndb_seq_num
+_pdbx_poly_seq_scheme.pdb_seq_num
+_pdbx_poly_seq_scheme.auth_seq_num
+_pdbx_poly_seq_scheme.pdb_mon_id
+_pdbx_poly_seq_scheme.auth_mon_id
+_pdbx_poly_seq_scheme.pdb_strand_id
+_pdbx_poly_seq_scheme.pdb_ins_code
+_pdbx_poly_seq_scheme.hetero
+A  1   1  DC   1   1   1  DC  C  A  .  n  
+A  1   2  DG   2   2   2  DG  G  A  .  n  
+A  1   3  DC   3   3   3  DC  C  A  .  n  
+A  1   4  DG   4   4   4  DG  G  A  .  n  
+A  1   5  DA   5   5   5  DA  A  A  .  n  
+A  1   6  DA   6   6   6  DA  A  A  .  n  
+A  1   7  DT   7   7   7  DT  T  A  .  n  
+A  1   8  DT   8   8   8  DT  T  A  .  n  
+A  1   9  DC   9   9   9  DC  C  A  .  n  
+A  1  10  DG  10  10  10  DG  G  A  .  n  
+A  1  11  DC  11  11  11  DC  C  A  .  n  
+A  1  12  DG  12  12  12  DG  G  A  .  n  
+B  1   1  DC   1  13  13  DC  C  B  .  n  
+B  1   2  DG   2  14  14  DG  G  B  .  n  
+B  1   3  DC   3  15  15  DC  C  B  .  n  
+B  1   4  DG   4  16  16  DG  G  B  .  n  
+B  1   5  DA   5  17  17  DA  A  B  .  n  
+B  1   6  DA   6  18  18  DA  A  B  .  n  
+B  1   7  DT   7  19  19  DT  T  B  .  n  
+B  1   8  DT   8  20  20  DT  T  B  .  n  
+B  1   9  DC   9  21  21  DC  C  B  .  n  
+B  1  10  DG  10  22  22  DG  G  B  .  n  
+B  1  11  DC  11  23  23  DC  C  B  .  n  
+B  1  12  DG  12  24  24  DG  G  B  .  n  
+##
+loop_
+_pdbx_nonpoly_scheme.asym_id
+_pdbx_nonpoly_scheme.entity_id
+_pdbx_nonpoly_scheme.mon_id
+_pdbx_nonpoly_scheme.ndb_seq_num
+_pdbx_nonpoly_scheme.pdb_seq_num
+_pdbx_nonpoly_scheme.auth_seq_num
+_pdbx_nonpoly_scheme.pdb_mon_id
+_pdbx_nonpoly_scheme.auth_mon_id
+_pdbx_nonpoly_scheme.pdb_strand_id
+_pdbx_nonpoly_scheme.pdb_ins_code
+C  2  HOH   1   25   25  HOH  HOH  A  .  
+C  2  HOH   2   31   31  HOH  HOH  A  .  
+C  2  HOH   3   32   32  HOH  HOH  A  .  
+C  2  HOH   4   36   36  HOH  HOH  A  .  
+C  2  HOH   5   38   38  HOH  HOH  A  .  
+C  2  HOH   6   39   39  HOH  HOH  A  .  
+C  2  HOH   7   40   40  HOH  HOH  A  .  
+C  2  HOH   8   41   41  HOH  HOH  A  .  
+C  2  HOH   9   50   50  HOH  HOH  A  .  
+C  2  HOH  10   54   54  HOH  HOH  A  .  
+C  2  HOH  11   55   55  HOH  HOH  A  .  
+C  2  HOH  12   58   58  HOH  HOH  A  .  
+C  2  HOH  13   62   62  HOH  HOH  A  .  
+C  2  HOH  14   64   64  HOH  HOH  A  .  
+C  2  HOH  15   65   65  HOH  HOH  A  .  
+C  2  HOH  16   66   66  HOH  HOH  A  .  
+C  2  HOH  17   67   67  HOH  HOH  A  .  
+C  2  HOH  18   70   70  HOH  HOH  A  .  
+C  2  HOH  19   74   74  HOH  HOH  A  .  
+C  2  HOH  20   76   76  HOH  HOH  A  .  
+C  2  HOH  21   77   77  HOH  HOH  A  .  
+C  2  HOH  22   78   78  HOH  HOH  A  .  
+C  2  HOH  23   79   79  HOH  HOH  A  .  
+C  2  HOH  24   80   80  HOH  HOH  A  .  
+C  2  HOH  25   81   81  HOH  HOH  A  .  
+C  2  HOH  26   82   82  HOH  HOH  A  .  
+C  2  HOH  27   84   84  HOH  HOH  A  .  
+C  2  HOH  28   85   85  HOH  HOH  A  .  
+C  2  HOH  29   86   86  HOH  HOH  A  .  
+C  2  HOH  30   87   87  HOH  HOH  A  .  
+C  2  HOH  31   88   88  HOH  HOH  A  .  
+C  2  HOH  32   89   89  HOH  HOH  A  .  
+C  2  HOH  33   92   92  HOH  HOH  A  .  
+C  2  HOH  34   97   97  HOH  HOH  A  .  
+C  2  HOH  35   99   99  HOH  HOH  A  .  
+C  2  HOH  36  100  100  HOH  HOH  A  .  
+C  2  HOH  37  102  102  HOH  HOH  A  .  
+D  2  HOH   1   26   26  HOH  HOH  B  .  
+D  2  HOH   2   27   27  HOH  HOH  B  .  
+D  2  HOH   3   28   28  HOH  HOH  B  .  
+D  2  HOH   4   29   29  HOH  HOH  B  .  
+D  2  HOH   5   30   30  HOH  HOH  B  .  
+D  2  HOH   6   33   33  HOH  HOH  B  .  
+D  2  HOH   7   34   34  HOH  HOH  B  .  
+D  2  HOH   8   35   35  HOH  HOH  B  .  
+D  2  HOH   9   37   37  HOH  HOH  B  .  
+D  2  HOH  10   42   42  HOH  HOH  B  .  
+D  2  HOH  11   43   43  HOH  HOH  B  .  
+D  2  HOH  12   44   44  HOH  HOH  B  .  
+D  2  HOH  13   45   45  HOH  HOH  B  .  
+D  2  HOH  14   46   46  HOH  HOH  B  .  
+D  2  HOH  15   47   47  HOH  HOH  B  .  
+D  2  HOH  16   48   48  HOH  HOH  B  .  
+D  2  HOH  17   49   49  HOH  HOH  B  .  
+D  2  HOH  18   51   51  HOH  HOH  B  .  
+D  2  HOH  19   52   52  HOH  HOH  B  .  
+D  2  HOH  20   53   53  HOH  HOH  B  .  
+D  2  HOH  21   56   56  HOH  HOH  B  .  
+D  2  HOH  22   57   57  HOH  HOH  B  .  
+D  2  HOH  23   59   59  HOH  HOH  B  .  
+D  2  HOH  24   60   60  HOH  HOH  B  .  
+D  2  HOH  25   61   61  HOH  HOH  B  .  
+D  2  HOH  26   63   63  HOH  HOH  B  .  
+D  2  HOH  27   68   68  HOH  HOH  B  .  
+D  2  HOH  28   69   69  HOH  HOH  B  .  
+D  2  HOH  29   71   71  HOH  HOH  B  .  
+D  2  HOH  30   72   72  HOH  HOH  B  .  
+D  2  HOH  31   73   73  HOH  HOH  B  .  
+D  2  HOH  32   75   75  HOH  HOH  B  .  
+D  2  HOH  33   83   83  HOH  HOH  B  .  
+D  2  HOH  34   90   90  HOH  HOH  B  .  
+D  2  HOH  35   91   91  HOH  HOH  B  .  
+D  2  HOH  36   93   93  HOH  HOH  B  .  
+D  2  HOH  37   94   94  HOH  HOH  B  .  
+D  2  HOH  38   95   95  HOH  HOH  B  .  
+D  2  HOH  39   96   96  HOH  HOH  B  .  
+D  2  HOH  40   98   98  HOH  HOH  B  .  
+D  2  HOH  41  101  101  HOH  HOH  B  .  
+D  2  HOH  42  103  103  HOH  HOH  B  .  
+D  2  HOH  43  104  104  HOH  HOH  B  .  
+##
+_pdbx_struct_assembly.id                  1
+_pdbx_struct_assembly.details             author_defined_assembly
+_pdbx_struct_assembly.method_details      ?
+_pdbx_struct_assembly.oligomeric_details  dimeric
+_pdbx_struct_assembly.oligomeric_count    2
+##
+_pdbx_struct_assembly_gen.assembly_id      1
+_pdbx_struct_assembly_gen.oper_expression  1
+_pdbx_struct_assembly_gen.asym_id_list     A,B,C,D
+##
+_pdbx_struct_oper_list.id                  1
+_pdbx_struct_oper_list.type                'identity operation'
+_pdbx_struct_oper_list.name                1_555
+_pdbx_struct_oper_list.symmetry_operation  x,y,z
+_pdbx_struct_oper_list.matrix[1][1]        1.0000000000
+_pdbx_struct_oper_list.matrix[1][2]        0.0000000000
+_pdbx_struct_oper_list.matrix[1][3]        0.0000000000
+_pdbx_struct_oper_list.vector[1]           0.0000000000
+_pdbx_struct_oper_list.matrix[2][1]        0.0000000000
+_pdbx_struct_oper_list.matrix[2][2]        1.0000000000
+_pdbx_struct_oper_list.matrix[2][3]        0.0000000000
+_pdbx_struct_oper_list.vector[2]           0.0000000000
+_pdbx_struct_oper_list.matrix[3][1]        0.0000000000
+_pdbx_struct_oper_list.matrix[3][2]        0.0000000000
+_pdbx_struct_oper_list.matrix[3][3]        1.0000000000
+_pdbx_struct_oper_list.vector[3]           0.0000000000
+##
+loop_
+_pdbx_audit_revision_history.ordinal
+_pdbx_audit_revision_history.data_content_type
+_pdbx_audit_revision_history.major_revision
+_pdbx_audit_revision_history.minor_revision
+_pdbx_audit_revision_history.revision_date
+1  'Structure model'  1  0  1981-05-21  
+2  'Structure model'  1  1  2008-05-22  
+3  'Structure model'  1  2  2011-07-13  
+##
+_pdbx_audit_revision_details.ordinal            1
+_pdbx_audit_revision_details.revision_ordinal   1
+_pdbx_audit_revision_details.data_content_type  'Structure model'
+_pdbx_audit_revision_details.provider           repository
+_pdbx_audit_revision_details.type               'Initial release'
+_pdbx_audit_revision_details.description        ?
+##
+loop_
+_pdbx_audit_revision_group.ordinal
+_pdbx_audit_revision_group.revision_ordinal
+_pdbx_audit_revision_group.data_content_type
+_pdbx_audit_revision_group.group
+1  2  'Structure model'  'Version format compliance'  
+2  3  'Structure model'  'Version format compliance'  
+##
+loop_
+_refine_B_iso.class
+_refine_B_iso.details
+_refine_B_iso.treatment
+_refine_B_iso.pdbx_refine_id
+'ALL ATOMS'   TR  isotropic  'X-RAY DIFFRACTION'  
+'ALL WATERS'  TR  isotropic  'X-RAY DIFFRACTION'  
+##
+loop_
+_refine_occupancy.class
+_refine_occupancy.treatment
+_refine_occupancy.pdbx_refine_id
+'ALL ATOMS'   fix  'X-RAY DIFFRACTION'  
+'ALL WATERS'  fix  'X-RAY DIFFRACTION'  
+##
+_software.name            JACK-LEVITT
+_software.classification  refinement
+_software.version         .
+_software.citation_id     ?
+_software.pdbx_ordinal    1
+##
+loop_
+_pdbx_validate_close_contact.id
+_pdbx_validate_close_contact.PDB_model_num
+_pdbx_validate_close_contact.auth_atom_id_1
+_pdbx_validate_close_contact.auth_asym_id_1
+_pdbx_validate_close_contact.auth_comp_id_1
+_pdbx_validate_close_contact.auth_seq_id_1
+_pdbx_validate_close_contact.PDB_ins_code_1
+_pdbx_validate_close_contact.label_alt_id_1
+_pdbx_validate_close_contact.auth_atom_id_2
+_pdbx_validate_close_contact.auth_asym_id_2
+_pdbx_validate_close_contact.auth_comp_id_2
+_pdbx_validate_close_contact.auth_seq_id_2
+_pdbx_validate_close_contact.PDB_ins_code_2
+_pdbx_validate_close_contact.label_alt_id_2
+_pdbx_validate_close_contact.dist
+1  1  O    A  HOH  62  ?  ?  O  A  HOH  77  ?  ?  1.61  
+2  1  OP2  A  DA    6  ?  ?  O  A  HOH  65  ?  ?  1.89  
+3  1  OP2  A  DG   10  ?  ?  O  A  HOH  70  ?  ?  2.02  
+4  1  O    A  HOH  54  ?  ?  O  A  HOH  86  ?  ?  2.07  
+5  1  O    A  HOH  77  ?  ?  O  B  HOH  63  ?  ?  2.09  
+6  1  O    A  HOH  31  ?  ?  O  A  HOH  99  ?  ?  2.12  
+##
+loop_
+_pdbx_validate_rmsd_bond.id
+_pdbx_validate_rmsd_bond.PDB_model_num
+_pdbx_validate_rmsd_bond.auth_atom_id_1
+_pdbx_validate_rmsd_bond.auth_asym_id_1
+_pdbx_validate_rmsd_bond.auth_comp_id_1
+_pdbx_validate_rmsd_bond.auth_seq_id_1
+_pdbx_validate_rmsd_bond.PDB_ins_code_1
+_pdbx_validate_rmsd_bond.label_alt_id_1
+_pdbx_validate_rmsd_bond.auth_atom_id_2
+_pdbx_validate_rmsd_bond.auth_asym_id_2
+_pdbx_validate_rmsd_bond.auth_comp_id_2
+_pdbx_validate_rmsd_bond.auth_seq_id_2
+_pdbx_validate_rmsd_bond.PDB_ins_code_2
+_pdbx_validate_rmsd_bond.label_alt_id_2
+_pdbx_validate_rmsd_bond.bond_value
+_pdbx_validate_rmsd_bond.bond_target_value
+_pdbx_validate_rmsd_bond.bond_deviation
+_pdbx_validate_rmsd_bond.bond_standard_deviation
+_pdbx_validate_rmsd_bond.linker_flag
+ 1  1  C5  A  DC   1  ?  ?  C6  A  DC   1  ?  ?  1.390  1.339   0.051  0.008  N  
+ 2  1  C5  A  DG   2  ?  ?  N7  A  DG   2  ?  ?  1.348  1.388  -0.040  0.006  N  
+ 3  1  N7  A  DG   2  ?  ?  C8  A  DG   2  ?  ?  1.341  1.305   0.036  0.006  N  
+ 4  1  C5  A  DC   3  ?  ?  C6  A  DC   3  ?  ?  1.391  1.339   0.052  0.008  N  
+ 5  1  C5  A  DG   4  ?  ?  N7  A  DG   4  ?  ?  1.349  1.388  -0.039  0.006  N  
+ 6  1  N7  A  DG   4  ?  ?  C8  A  DG   4  ?  ?  1.341  1.305   0.036  0.006  N  
+ 7  1  C5  A  DA   5  ?  ?  N7  A  DA   5  ?  ?  1.349  1.388  -0.039  0.006  N  
+ 8  1  C5  A  DA   6  ?  ?  N7  A  DA   6  ?  ?  1.350  1.388  -0.038  0.006  N  
+ 9  1  C5  A  DT   7  ?  ?  C6  A  DT   7  ?  ?  1.395  1.339   0.056  0.007  N  
+10  1  C5  A  DT   8  ?  ?  C6  A  DT   8  ?  ?  1.395  1.339   0.056  0.007  N  
+11  1  C5  A  DC   9  ?  ?  C6  A  DC   9  ?  ?  1.392  1.339   0.053  0.008  N  
+12  1  C5  A  DG  10  ?  ?  N7  A  DG  10  ?  ?  1.349  1.388  -0.039  0.006  N  
+13  1  N7  A  DG  10  ?  ?  C8  A  DG  10  ?  ?  1.341  1.305   0.036  0.006  N  
+14  1  C5  A  DC  11  ?  ?  C6  A  DC  11  ?  ?  1.390  1.339   0.051  0.008  N  
+15  1  C5  A  DG  12  ?  ?  N7  A  DG  12  ?  ?  1.349  1.388  -0.039  0.006  N  
+16  1  C5  B  DC  13  ?  ?  C6  B  DC  13  ?  ?  1.391  1.339   0.052  0.008  N  
+17  1  C5  B  DG  14  ?  ?  N7  B  DG  14  ?  ?  1.346  1.388  -0.042  0.006  N  
+18  1  C5  B  DC  15  ?  ?  C6  B  DC  15  ?  ?  1.388  1.339   0.049  0.008  N  
+19  1  C5  B  DG  16  ?  ?  N7  B  DG  16  ?  ?  1.348  1.388  -0.040  0.006  N  
+20  1  N7  B  DG  16  ?  ?  C8  B  DG  16  ?  ?  1.344  1.305   0.039  0.006  N  
+21  1  C5  B  DA  17  ?  ?  N7  B  DA  17  ?  ?  1.349  1.388  -0.039  0.006  N  
+22  1  C5  B  DA  18  ?  ?  N7  B  DA  18  ?  ?  1.349  1.388  -0.039  0.006  N  
+23  1  C5  B  DT  19  ?  ?  C6  B  DT  19  ?  ?  1.395  1.339   0.056  0.007  N  
+24  1  C5  B  DT  20  ?  ?  C6  B  DT  20  ?  ?  1.390  1.339   0.051  0.007  N  
+25  1  C5  B  DC  21  ?  ?  C6  B  DC  21  ?  ?  1.391  1.339   0.052  0.008  N  
+26  1  C5  B  DG  22  ?  ?  N7  B  DG  22  ?  ?  1.352  1.388  -0.036  0.006  N  
+27  1  N7  B  DG  22  ?  ?  C8  B  DG  22  ?  ?  1.342  1.305   0.037  0.006  N  
+28  1  C5  B  DC  23  ?  ?  C6  B  DC  23  ?  ?  1.390  1.339   0.051  0.008  N  
+29  1  C5  B  DG  24  ?  ?  N7  B  DG  24  ?  ?  1.349  1.388  -0.039  0.006  N  
+##
+loop_
+_pdbx_validate_rmsd_angle.id
+_pdbx_validate_rmsd_angle.PDB_model_num
+_pdbx_validate_rmsd_angle.auth_atom_id_1
+_pdbx_validate_rmsd_angle.auth_asym_id_1
+_pdbx_validate_rmsd_angle.auth_comp_id_1
+_pdbx_validate_rmsd_angle.auth_seq_id_1
+_pdbx_validate_rmsd_angle.PDB_ins_code_1
+_pdbx_validate_rmsd_angle.label_alt_id_1
+_pdbx_validate_rmsd_angle.auth_atom_id_2
+_pdbx_validate_rmsd_angle.auth_asym_id_2
+_pdbx_validate_rmsd_angle.auth_comp_id_2
+_pdbx_validate_rmsd_angle.auth_seq_id_2
+_pdbx_validate_rmsd_angle.PDB_ins_code_2
+_pdbx_validate_rmsd_angle.label_alt_id_2
+_pdbx_validate_rmsd_angle.auth_atom_id_3
+_pdbx_validate_rmsd_angle.auth_asym_id_3
+_pdbx_validate_rmsd_angle.auth_comp_id_3
+_pdbx_validate_rmsd_angle.auth_seq_id_3
+_pdbx_validate_rmsd_angle.PDB_ins_code_3
+_pdbx_validate_rmsd_angle.label_alt_id_3
+_pdbx_validate_rmsd_angle.angle_value
+_pdbx_validate_rmsd_angle.angle_target_value
+_pdbx_validate_rmsd_angle.angle_deviation
+_pdbx_validate_rmsd_angle.angle_standard_deviation
+_pdbx_validate_rmsd_angle.linker_flag
+ 1  1  "C4'"  A  DC   1  ?  ?  "C3'"  A  DC   1  ?  ?  "C2'"  A  DC   1  ?  ?   97.42  102.20  -4.78  0.70  N  
+ 2  1  "C3'"  A  DC   1  ?  ?  "C2'"  A  DC   1  ?  ?  "C1'"  A  DC   1  ?  ?   96.43  102.40  -5.97  0.80  N  
+ 3  1  "O4'"  A  DC   1  ?  ?  "C1'"  A  DC   1  ?  ?  "C2'"  A  DC   1  ?  ?  100.54  105.90  -5.36  0.80  N  
+ 4  1  "O5'"  A  DG   2  ?  ?  "C5'"  A  DG   2  ?  ?  "C4'"  A  DG   2  ?  ?  102.57  109.40  -6.83  0.80  N  
+ 5  1  "O4'"  A  DG   2  ?  ?  "C1'"  A  DG   2  ?  ?  N9     A  DG   2  ?  ?  103.05  108.00  -4.95  0.70  N  
+ 6  1  "O4'"  A  DC   3  ?  ?  "C1'"  A  DC   3  ?  ?  N1     A  DC   3  ?  ?  102.64  108.00  -5.36  0.70  N  
+ 7  1  "C3'"  A  DG   4  ?  ?  "C2'"  A  DG   4  ?  ?  "C1'"  A  DG   4  ?  ?   97.18  102.40  -5.22  0.80  N  
+ 8  1  "O4'"  A  DA   5  ?  ?  "C1'"  A  DA   5  ?  ?  "C2'"  A  DA   5  ?  ?  101.02  105.90  -4.88  0.80  N  
+ 9  1  "O4'"  A  DA   5  ?  ?  "C1'"  A  DA   5  ?  ?  N9     A  DA   5  ?  ?  103.63  108.00  -4.37  0.70  N  
+10  1  "O4'"  A  DA   6  ?  ?  "C1'"  A  DA   6  ?  ?  "C2'"  A  DA   6  ?  ?   99.84  105.90  -6.06  0.80  N  
+11  1  "O5'"  A  DT   7  ?  ?  "C5'"  A  DT   7  ?  ?  "C4'"  A  DT   7  ?  ?  103.29  109.40  -6.11  0.80  N  
+12  1  N1     A  DT   7  ?  ?  C2     A  DT   7  ?  ?  N3     A  DT   7  ?  ?  118.31  114.60   3.71  0.60  N  
+13  1  C2     A  DT   7  ?  ?  N3     A  DT   7  ?  ?  C4     A  DT   7  ?  ?  122.84  127.20  -4.36  0.60  N  
+14  1  C5     A  DT   7  ?  ?  C6     A  DT   7  ?  ?  N1     A  DT   7  ?  ?  119.96  123.70  -3.74  0.60  N  
+15  1  "O4'"  A  DT   8  ?  ?  "C1'"  A  DT   8  ?  ?  "C2'"  A  DT   8  ?  ?  100.50  105.90  -5.40  0.80  N  
+16  1  N1     A  DT   8  ?  ?  C2     A  DT   8  ?  ?  N3     A  DT   8  ?  ?  118.74  114.60   4.14  0.60  N  
+17  1  C2     A  DT   8  ?  ?  N3     A  DT   8  ?  ?  C4     A  DT   8  ?  ?  122.27  127.20  -4.93  0.60  N  
+18  1  C5     A  DT   8  ?  ?  C6     A  DT   8  ?  ?  N1     A  DT   8  ?  ?  119.58  123.70  -4.12  0.60  N  
+19  1  "C3'"  A  DG  10  ?  ?  "C2'"  A  DG  10  ?  ?  "C1'"  A  DG  10  ?  ?   95.28  102.40  -7.12  0.80  N  
+20  1  "O4'"  A  DG  12  ?  ?  "C1'"  A  DG  12  ?  ?  "C2'"  A  DG  12  ?  ?  100.00  105.90  -5.90  0.80  N  
+21  1  "O4'"  B  DC  13  ?  ?  "C1'"  B  DC  13  ?  ?  N1     B  DC  13  ?  ?  103.43  108.00  -4.57  0.70  N  
+22  1  "O5'"  B  DG  14  ?  ?  "C5'"  B  DG  14  ?  ?  "C4'"  B  DG  14  ?  ?  103.60  109.40  -5.80  0.80  N  
+23  1  "O4'"  B  DG  14  ?  ?  "C1'"  B  DG  14  ?  ?  "C2'"  B  DG  14  ?  ?  100.71  105.90  -5.19  0.80  N  
+24  1  "O4'"  B  DG  14  ?  ?  "C1'"  B  DG  14  ?  ?  N9     B  DG  14  ?  ?  102.65  108.00  -5.35  0.70  N  
+25  1  "O5'"  B  DG  16  ?  ?  "C5'"  B  DG  16  ?  ?  "C4'"  B  DG  16  ?  ?  104.35  109.40  -5.05  0.80  N  
+26  1  "O4'"  B  DA  18  ?  ?  "C1'"  B  DA  18  ?  ?  N9     B  DA  18  ?  ?  103.63  108.00  -4.37  0.70  N  
+27  1  N1     B  DT  19  ?  ?  C2     B  DT  19  ?  ?  N3     B  DT  19  ?  ?  118.52  114.60   3.92  0.60  N  
+28  1  C2     B  DT  19  ?  ?  N3     B  DT  19  ?  ?  C4     B  DT  19  ?  ?  122.33  127.20  -4.87  0.60  N  
+29  1  C5     B  DT  19  ?  ?  C6     B  DT  19  ?  ?  N1     B  DT  19  ?  ?  119.05  123.70  -4.65  0.60  N  
+30  1  "O4'"  B  DT  20  ?  ?  "C1'"  B  DT  20  ?  ?  "C2'"  B  DT  20  ?  ?   99.84  105.90  -6.06  0.80  N  
+31  1  "O4'"  B  DT  20  ?  ?  "C1'"  B  DT  20  ?  ?  N1     B  DT  20  ?  ?  102.84  108.00  -5.16  0.70  N  
+32  1  N1     B  DT  20  ?  ?  C2     B  DT  20  ?  ?  N3     B  DT  20  ?  ?  118.22  114.60   3.62  0.60  N  
+33  1  C2     B  DT  20  ?  ?  N3     B  DT  20  ?  ?  C4     B  DT  20  ?  ?  122.77  127.20  -4.43  0.60  N  
+34  1  C5     B  DT  20  ?  ?  C6     B  DT  20  ?  ?  N1     B  DT  20  ?  ?  119.69  123.70  -4.01  0.60  N  
+35  1  C6     B  DT  20  ?  ?  C5     B  DT  20  ?  ?  C7     B  DT  20  ?  ?  119.28  122.90  -3.62  0.60  N  
+36  1  "O4'"  B  DC  21  ?  ?  "C1'"  B  DC  21  ?  ?  N1     B  DC  21  ?  ?  100.91  108.00  -7.09  0.70  N  
+37  1  "C3'"  B  DG  22  ?  ?  "C2'"  B  DG  22  ?  ?  "C1'"  B  DG  22  ?  ?   95.55  102.40  -6.85  0.80  N  
+38  1  "O4'"  B  DG  22  ?  ?  "C1'"  B  DG  22  ?  ?  N9     B  DG  22  ?  ?  110.17  108.30   1.87  0.30  N  
+39  1  "O5'"  B  DG  24  ?  ?  "C5'"  B  DG  24  ?  ?  "C4'"  B  DG  24  ?  ?  103.92  109.40  -5.48  0.80  N  
+##
+_ndb_struct_conf_na.entry_id  1BNA
+_ndb_struct_conf_na.feature   'b-form double helix'
+##
+loop_
+_ndb_struct_na_base_pair.model_number
+_ndb_struct_na_base_pair.i_label_asym_id
+_ndb_struct_na_base_pair.i_label_comp_id
+_ndb_struct_na_base_pair.i_label_seq_id
+_ndb_struct_na_base_pair.i_symmetry
+_ndb_struct_na_base_pair.j_label_asym_id
+_ndb_struct_na_base_pair.j_label_comp_id
+_ndb_struct_na_base_pair.j_label_seq_id
+_ndb_struct_na_base_pair.j_symmetry
+_ndb_struct_na_base_pair.shear
+_ndb_struct_na_base_pair.stretch
+_ndb_struct_na_base_pair.stagger
+_ndb_struct_na_base_pair.buckle
+_ndb_struct_na_base_pair.propeller
+_ndb_struct_na_base_pair.opening
+_ndb_struct_na_base_pair.pair_number
+_ndb_struct_na_base_pair.pair_name
+_ndb_struct_na_base_pair.i_auth_asym_id
+_ndb_struct_na_base_pair.i_auth_seq_id
+_ndb_struct_na_base_pair.i_PDB_ins_code
+_ndb_struct_na_base_pair.j_auth_asym_id
+_ndb_struct_na_base_pair.j_auth_seq_id
+_ndb_struct_na_base_pair.j_PDB_ins_code
+_ndb_struct_na_base_pair.hbond_type_28
+_ndb_struct_na_base_pair.hbond_type_12
+1  A  DC   1  1_555  B  DG  12  1_555  -0.422  -0.268   0.060    2.762  -14.200  -3.666   1  A_DC1:DG24_B   A   1  ?  B  24  ?  19  1  
+1  A  DG   2  1_555  B  DC  11  1_555  -0.024  -0.266   0.249   -4.455  -10.846  -4.022   2  A_DG2:DC23_B   A   2  ?  B  23  ?  19  1  
+1  A  DC   3  1_555  B  DG  10  1_555   0.003  -0.248   0.213   -6.940   -3.928  -2.346   3  A_DC3:DG22_B   A   3  ?  B  22  ?  19  1  
+1  A  DG   4  1_555  B  DC   9  1_555  -0.371  -0.442  -0.180    9.308  -10.394  -1.297   4  A_DG4:DC21_B   A   4  ?  B  21  ?  19  1  
+1  A  DA   5  1_555  B  DT   8  1_555   0.272  -0.222   0.035    5.035  -16.362   1.835   5  A_DA5:DT20_B   A   5  ?  B  20  ?  20  1  
+1  A  DA   6  1_555  B  DT   7  1_555  -0.092  -0.042   0.166    3.544  -18.130   5.558   6  A_DA6:DT19_B   A   6  ?  B  19  ?  20  1  
+1  A  DT   7  1_555  B  DA   6  1_555   0.317  -0.117   0.133    0.829  -17.701   7.931   7  A_DT7:DA18_B   A   7  ?  B  18  ?  20  1  
+1  A  DT   8  1_555  B  DA   5  1_555   0.249  -0.215  -0.099   -1.329  -17.674   0.828   8  A_DT8:DA17_B   A   8  ?  B  17  ?  20  1  
+1  A  DC   9  1_555  B  DG   4  1_555  -0.019  -0.251  -0.060  -10.176  -17.254  -0.867   9  A_DC9:DG16_B   A   9  ?  B  16  ?  19  1  
+1  A  DG  10  1_555  B  DC   3  1_555   0.087  -0.278   0.272    1.665   -5.307  -1.129  10  A_DG10:DC15_B  A  10  ?  B  15  ?  19  1  
+1  A  DC  11  1_555  B  DG   2  1_555   0.069  -0.284   0.586   -3.958  -18.046  -5.616  11  A_DC11:DG14_B  A  11  ?  B  14  ?  19  1  
+1  A  DG  12  1_555  B  DC   1  1_555  -0.529  -0.109   0.261    6.598    1.957  -3.864  12  A_DG12:DC13_B  A  12  ?  B  13  ?  19  1  
+##
+loop_
+_ndb_struct_na_base_pair_step.model_number
+_ndb_struct_na_base_pair_step.i_label_asym_id_1
+_ndb_struct_na_base_pair_step.i_label_comp_id_1
+_ndb_struct_na_base_pair_step.i_label_seq_id_1
+_ndb_struct_na_base_pair_step.i_symmetry_1
+_ndb_struct_na_base_pair_step.j_label_asym_id_1
+_ndb_struct_na_base_pair_step.j_label_comp_id_1
+_ndb_struct_na_base_pair_step.j_label_seq_id_1
+_ndb_struct_na_base_pair_step.j_symmetry_1
+_ndb_struct_na_base_pair_step.i_label_asym_id_2
+_ndb_struct_na_base_pair_step.i_label_comp_id_2
+_ndb_struct_na_base_pair_step.i_label_seq_id_2
+_ndb_struct_na_base_pair_step.i_symmetry_2
+_ndb_struct_na_base_pair_step.j_label_asym_id_2
+_ndb_struct_na_base_pair_step.j_label_comp_id_2
+_ndb_struct_na_base_pair_step.j_label_seq_id_2
+_ndb_struct_na_base_pair_step.j_symmetry_2
+_ndb_struct_na_base_pair_step.shift
+_ndb_struct_na_base_pair_step.slide
+_ndb_struct_na_base_pair_step.rise
+_ndb_struct_na_base_pair_step.tilt
+_ndb_struct_na_base_pair_step.roll
+_ndb_struct_na_base_pair_step.twist
+_ndb_struct_na_base_pair_step.x_displacement
+_ndb_struct_na_base_pair_step.y_displacement
+_ndb_struct_na_base_pair_step.helical_rise
+_ndb_struct_na_base_pair_step.inclination
+_ndb_struct_na_base_pair_step.tip
+_ndb_struct_na_base_pair_step.helical_twist
+_ndb_struct_na_base_pair_step.step_number
+_ndb_struct_na_base_pair_step.step_name
+_ndb_struct_na_base_pair_step.i_auth_asym_id_1
+_ndb_struct_na_base_pair_step.i_auth_seq_id_1
+_ndb_struct_na_base_pair_step.i_PDB_ins_code_1
+_ndb_struct_na_base_pair_step.j_auth_asym_id_1
+_ndb_struct_na_base_pair_step.j_auth_seq_id_1
+_ndb_struct_na_base_pair_step.j_PDB_ins_code_1
+_ndb_struct_na_base_pair_step.i_auth_asym_id_2
+_ndb_struct_na_base_pair_step.i_auth_seq_id_2
+_ndb_struct_na_base_pair_step.i_PDB_ins_code_2
+_ndb_struct_na_base_pair_step.j_auth_asym_id_2
+_ndb_struct_na_base_pair_step.j_auth_seq_id_2
+_ndb_struct_na_base_pair_step.j_PDB_ins_code_2
+1  A  DC   1  1_555  B  DG  12  1_555  A  DG   2  1_555  B  DC  11  1_555  -0.362   0.149  3.524  -3.397    6.425  40.311  -0.551   0.114  3.524    9.231   4.881  40.934   1  AA_DC1DG2:DC23DG24_BB    A   1  ?  B  24  ?  A   2  ?  B  23  ?  
+1  A  DG   2  1_555  B  DC  11  1_555  A  DC   3  1_555  B  DG  10  1_555   0.498   0.227  3.523   0.805   -4.734  38.147   0.977  -0.648  3.480   -7.209  -1.225  38.437   2  AA_DG2DC3:DG22DC23_BB    A   2  ?  B  23  ?  A   3  ?  B  22  ?  
+1  A  DC   3  1_555  B  DG  10  1_555  A  DG   4  1_555  B  DC   9  1_555  -0.324   0.689  3.041   3.631    7.947  24.466  -0.563   1.680  3.033   18.033  -8.240  25.957   3  AA_DC3DG4:DC21DG22_BB    A   3  ?  B  22  ?  A   4  ?  B  21  ?  
+1  A  DG   4  1_555  B  DC   9  1_555  A  DA   5  1_555  B  DT   8  1_555   0.008   0.071  3.360  -2.678    3.162  40.897  -0.252  -0.310  3.349    4.511   3.821  41.097   4  AA_DG4DA5:DT20DC21_BB    A   4  ?  B  21  ?  A   5  ?  B  20  ?  
+1  A  DA   5  1_555  B  DT   8  1_555  A  DA   6  1_555  B  DT   7  1_555   0.101  -0.312  3.318  -0.705    0.950  35.351  -0.655  -0.272  3.306    1.564   1.160  35.370   5  AA_DA5DA6:DT19DT20_BB    A   5  ?  B  20  ?  A   6  ?  B  19  ?  
+1  A  DA   6  1_555  B  DT   7  1_555  A  DT   7  1_555  B  DA   6  1_555   0.329  -0.603  3.341   1.827   -2.755  34.760  -0.576  -0.264  3.390   -4.598  -3.049  34.912   6  AA_DA6DT7:DA18DT19_BB    A   6  ?  B  19  ?  A   7  ?  B  18  ?  
+1  A  DT   7  1_555  B  DA   6  1_555  A  DT   8  1_555  B  DA   5  1_555  -0.306  -0.175  3.318   2.964    0.725  35.393  -0.395   0.939  3.279    1.190  -4.864  35.520   7  AA_DT7DT8:DA17DA18_BB    A   7  ?  B  18  ?  A   8  ?  B  17  ?  
+1  A  DT   8  1_555  B  DA   5  1_555  A  DC   9  1_555  B  DG   4  1_555   0.020  -0.033  3.394   0.331   -0.053  39.272  -0.042   0.011  3.394   -0.079  -0.493  39.273   8  AA_DT8DC9:DG16DA17_BB    A   8  ?  B  17  ?  A   9  ?  B  16  ?  
+1  A  DC   9  1_555  B  DG   4  1_555  A  DG  10  1_555  B  DC   3  1_555   0.381   0.864  3.239  -3.294    3.860  29.397   0.874  -1.427  3.262    7.535   6.431  29.823   9  AA_DC9DG10:DC15DG16_BB   A   9  ?  B  16  ?  A  10  ?  B  15  ?  
+1  A  DG  10  1_555  B  DC   3  1_555  A  DC  11  1_555  B  DG   2  1_555  -1.303   0.418  3.682  -4.681  -12.201  40.779   1.959   1.257  3.543  -16.990   6.519  42.737  10  AA_DG10DC11:DG14DC15_BB  A  10  ?  B  15  ?  A  11  ?  B  14  ?  
+1  A  DC  11  1_555  B  DG   2  1_555  A  DG  12  1_555  B  DC   1  1_555   0.773   0.057  3.226   3.143   -3.090  32.624   0.626  -0.830  3.265   -5.469  -5.562  32.912  11  AA_DC11DG12:DC13DG14_BB  A  11  ?  B  14  ?  A  12  ?  B  13  ?  
+##
+_pdbx_entity_nonpoly.entity_id  2
+_pdbx_entity_nonpoly.name       water
+_pdbx_entity_nonpoly.comp_id    HOH
+##
+_ndb_struct_ntc_overall.entry_id                     1BNA
+_ndb_struct_ntc_overall.confal_score                 0
+_ndb_struct_ntc_overall.confal_percentile            0
+_ndb_struct_ntc_overall.ntc_version                  6.6.6
+_ndb_struct_ntc_overall.cana_version                 6.6.6
+_ndb_struct_ntc_overall.num_steps                    22
+_ndb_struct_ntc_overall.num_classified               22
+_ndb_struct_ntc_overall.num_unclassified             0
+_ndb_struct_ntc_overall.num_unclassified_rmsd_close  0
+##
+loop_
+_ndb_struct_ntc_step.id
+_ndb_struct_ntc_step.name
+_ndb_struct_ntc_step.PDB_model_number
+_ndb_struct_ntc_step.label_entity_id_1
+_ndb_struct_ntc_step.label_asym_id_1
+_ndb_struct_ntc_step.label_seq_id_1
+_ndb_struct_ntc_step.label_comp_id_1
+_ndb_struct_ntc_step.label_alt_id_1
+_ndb_struct_ntc_step.label_entity_id_2
+_ndb_struct_ntc_step.label_asym_id_2
+_ndb_struct_ntc_step.label_seq_id_2
+_ndb_struct_ntc_step.label_comp_id_2
+_ndb_struct_ntc_step.label_alt_id_2
+_ndb_struct_ntc_step.auth_asym_id_1
+_ndb_struct_ntc_step.auth_seq_id_1
+_ndb_struct_ntc_step.auth_asym_id_2
+_ndb_struct_ntc_step.auth_seq_id_2
+_ndb_struct_ntc_step.PDB_ins_code_1
+_ndb_struct_ntc_step.PDB_ins_code_2
+ 1    1bna_A_DC_1_DG_2  1  1  A   1  DC  .  1  A   2  DG  .  A   1  A   2  .  .  
+ 2    1bna_A_DG_2_DC_3  1  1  A   2  DG  .  1  A   3  DC  .  A   2  A   3  .  .  
+ 3    1bna_A_DC_3_DG_4  1  1  A   3  DC  .  1  A   4  DG  .  A   3  A   4  .  .  
+ 4    1bna_A_DG_4_DA_5  1  1  A   4  DG  .  1  A   5  DA  .  A   4  A   5  .  .  
+ 5    1bna_A_DA_5_DA_6  1  1  A   5  DA  .  1  A   6  DA  .  A   5  A   6  .  .  
+ 6    1bna_A_DA_6_DT_7  1  1  A   6  DA  .  1  A   7  DT  .  A   6  A   7  .  .  
+ 7    1bna_A_DT_7_DT_8  1  1  A   7  DT  .  1  A   8  DT  .  A   7  A   8  .  .  
+ 8    1bna_A_DT_8_DC_9  1  1  A   8  DT  .  1  A   9  DC  .  A   8  A   9  .  .  
+ 9   1bna_A_DC_9_DG_10  1  1  A   9  DC  .  1  A  10  DG  .  A   9  A  10  .  .  
+10  1bna_A_DG_10_DC_11  1  1  A  10  DG  .  1  A  11  DC  .  A  10  A  11  .  .  
+11  1bna_A_DC_11_DG_12  1  1  A  11  DC  .  1  A  12  DG  .  A  11  A  12  .  .  
+12  1bna_B_DC_13_DG_14  1  1  B   1  DC  .  1  B   2  DG  .  B  13  B  14  .  .  
+13  1bna_B_DG_14_DC_15  1  1  B   2  DG  .  1  B   3  DC  .  B  14  B  15  .  .  
+14  1bna_B_DC_15_DG_16  1  1  B   3  DC  .  1  B   4  DG  .  B  15  B  16  .  .  
+15  1bna_B_DG_16_DA_17  1  1  B   4  DG  .  1  B   5  DA  .  B  16  B  17  .  .  
+16  1bna_B_DA_17_DA_18  1  1  B   5  DA  .  1  B   6  DA  .  B  17  B  18  .  .  
+17  1bna_B_DA_18_DT_19  1  1  B   6  DA  .  1  B   7  DT  .  B  18  B  19  .  .  
+18  1bna_B_DT_19_DT_20  1  1  B   7  DT  .  1  B   8  DT  .  B  19  B  20  .  .  
+19  1bna_B_DT_20_DC_21  1  1  B   8  DT  .  1  B   9  DC  .  B  20  B  21  .  .  
+20  1bna_B_DC_21_DG_22  1  1  B   9  DC  .  1  B  10  DG  .  B  21  B  22  .  .  
+21  1bna_B_DG_22_DC_23  1  1  B  10  DG  .  1  B  11  DC  .  B  22  B  23  .  .  
+22  1bna_B_DC_23_DG_24  1  1  B  11  DC  .  1  B  12  DG  .  B  23  B  24  .  .  
+##
+loop_
+_ndb_struct_ntc_step_summary.step_id
+_ndb_struct_ntc_step_summary.assigned_CANA
+_ndb_struct_ntc_step_summary.assigned_NtC
+_ndb_struct_ntc_step_summary.confal_score
+_ndb_struct_ntc_step_summary.euclidean_distance_NtC_ideal
+_ndb_struct_ntc_step_summary.cartesian_rmsd_closest_NtC_representative
+_ndb_struct_ntc_step_summary.closest_CANA
+_ndb_struct_ntc_step_summary.closest_NtC
+_ndb_struct_ntc_step_summary.closest_step_golden
+ 1  B12  BB04  33  40.0  0.367  B12  BB04      1mjo_F_DG_10_DA_11  
+ 2  B-A  BA05  80  21.0  0.310  B-A  BA05      4i2o_W_DA_11_DT_12  
+ 3  A-B  AB01  36  25.1  0.177  A-B  AB01    1n1o_B_DC_115_DG_116  
+ 4  B12  BB04  16  39.9  0.216  B12  BB04    1kx5_J_DG_-56_DA_-55  
+ 5  BBB  BB01  68  30.9  0.190  BBB  BB01    3opi_B_DT_219_DT_220  
+ 6  B-A  BA05  69  25.9  0.274  B-A  BA05        1g8n_A_DA_6_DT_7  
+ 7  BBB  BB01  41  43.1  0.284  BBB  BB01    3opi_A_DT_107_DT_108  
+ 8  BBB  BB01  67  32.4  0.248  BBB  BB01      4f3u_B_DT_19_DT_20  
+ 9  BBB  BB00  77  33.4  0.373  BBB  BB00       1edr_A_DC_9_DG_10  
+10  BB2  BB07  70  37.3  0.387  BB2  BB07    1s32_J_DC_193_DT_194  
+11  BBB  BB01  54  36.1  0.382  BBB  BB01        1aay_B_DG_4_DT_5  
+12  BBB  BB00  48  49.0  0.277  BBB  BB00       1eo4_C_DC_9_DT_10  
+13  B-A  BA05  59  29.7  0.288  B-A  BA05      1g8n_B_DG_14_DC_15  
+14  A-B  AB01  63  32.0  0.260  A-B  AB01  1dsz_D_DC_1543_DT_1544  
+15  BBB  BB00  84  23.0  0.229  BBB  BB00    1p3l_J_DT_237_DT_238  
+16  BBB  BB00  88  18.5  0.133  BBB  BB00      1mus_B_DA_15_DG_16  
+17  BBB  BB01  64  24.6  0.197  BBB  BB01        463d_A_DA_6_DT_7  
+18  BBB  BB01  67  32.2  0.183  BBB  BB01        4mkw_A_DT_7_DT_8  
+19  BBB  BB01  78  19.4  0.144  BBB  BB01        1g75_A_DA_5_DA_6  
+20  BBB  BB00  16  39.3  0.271  BBB  BB00      1dpn_B_DC_21_DG_22  
+21  B-A  BA17  91  21.2  0.185  B-A  BA17      3utb_J_DC_-5_DT_-4  
+22  B-A  BA05  38  40.9  0.373  B-A  BA05    1qna_F_DC_218_DC_219  
+##
+loop_
+_ndb_struct_ntc_step_parameters.step_id
+_ndb_struct_ntc_step_parameters.tor_delta_1
+_ndb_struct_ntc_step_parameters.tor_epsilon_1
+_ndb_struct_ntc_step_parameters.tor_zeta_1
+_ndb_struct_ntc_step_parameters.tor_alpha_2
+_ndb_struct_ntc_step_parameters.tor_beta_2
+_ndb_struct_ntc_step_parameters.tor_gamma_2
+_ndb_struct_ntc_step_parameters.tor_delta_2
+_ndb_struct_ntc_step_parameters.tor_chi_1
+_ndb_struct_ntc_step_parameters.tor_chi_2
+_ndb_struct_ntc_step_parameters.dist_NN
+_ndb_struct_ntc_step_parameters.dist_CC
+_ndb_struct_ntc_step_parameters.tor_NCCN
+_ndb_struct_ntc_step_parameters.diff_tor_delta_1
+_ndb_struct_ntc_step_parameters.diff_tor_epsilon_1
+_ndb_struct_ntc_step_parameters.diff_tor_zeta_1
+_ndb_struct_ntc_step_parameters.diff_tor_alpha_2
+_ndb_struct_ntc_step_parameters.diff_tor_beta_2
+_ndb_struct_ntc_step_parameters.diff_tor_gamma_2
+_ndb_struct_ntc_step_parameters.diff_tor_delta_2
+_ndb_struct_ntc_step_parameters.diff_tor_chi_1
+_ndb_struct_ntc_step_parameters.diff_tor_chi_2
+_ndb_struct_ntc_step_parameters.diff_dist_NN
+_ndb_struct_ntc_step_parameters.diff_dist_CC
+_ndb_struct_ntc_step_parameters.diff_tor_NCCN
+_ndb_struct_ntc_step_parameters.confal_tor_delta_1
+_ndb_struct_ntc_step_parameters.confal_tor_epsilon_1
+_ndb_struct_ntc_step_parameters.confal_tor_zeta_1
+_ndb_struct_ntc_step_parameters.confal_tor_alpha_2
+_ndb_struct_ntc_step_parameters.confal_tor_beta_2
+_ndb_struct_ntc_step_parameters.confal_tor_gamma_2
+_ndb_struct_ntc_step_parameters.confal_tor_delta_2
+_ndb_struct_ntc_step_parameters.confal_tor_chi_1
+_ndb_struct_ntc_step_parameters.confal_tor_chi_2
+_ndb_struct_ntc_step_parameters.confal_dist_NN
+_ndb_struct_ntc_step_parameters.confal_dist_CC
+_ndb_struct_ntc_step_parameters.confal_tor_NCCN
+_ndb_struct_ntc_step_parameters.details
+ 1  156.8  218.7  216.1  294.4  169.8  40.1  128.1  255.0  249.5  4.65  5.35  32.3   16.7   17.4    1.9  -20.4   17.2  -6.0  -11.9   -7.5   -3.1   0.00   0.23   3.7    5.5   54.2   99.2   31.0   47.8   85.4  26.1   87.5  97.7  100.0  80.6  94.5  .  
+ 2  128.1  174.2  262.2  297.4  171.8  58.8   98.3  249.5  224.9  4.23  4.72  31.8   -3.3  -10.2   -6.5    1.5    3.0   6.5   -5.9   -1.4  -10.5   0.13   0.08   7.6   97.9   55.2   75.3   99.4   92.9   84.2  87.5   99.4  74.3   74.0  88.4  58.9  .  
+ 3   98.3  183.3  272.4  297.1  180.1  57.2  155.7  224.9  266.6  4.57  5.12  22.2   12.1   -2.9   -8.8   -3.9    1.6   2.8   13.8    2.1   10.6  -0.12  -0.16   4.5    8.9   96.9   77.0   94.8   99.1   96.7   8.7   98.8  70.1   93.1  90.4  89.4  .  
+ 4  155.7  204.7  207.5  317.0  142.8  52.4  119.6  266.6  233.7  4.25  4.73  35.8   15.6    3.4   -6.7    2.2   -9.9   6.3  -20.4    4.1  -18.8  -0.39  -0.39   7.2    8.0   97.7   90.4   98.6   78.3   83.7   1.9   96.2  42.1   58.4  53.1  80.6  .  
+ 5  119.6  179.9  267.8  286.7  179.7  66.0  121.1  233.7  237.8  4.47  5.04  27.7  -11.1   -1.0    2.2  -14.7    3.5  17.4    1.0  -13.8   -6.0   0.17   0.19   2.2   74.5   99.6   97.0   50.5   92.8   27.4  99.0   51.5  81.9   83.0  74.6  94.1  .  
+ 6  121.1  173.7  271.5  303.4  180.8  52.2   98.9  237.8  232.7  4.22  4.79  25.8  -10.3  -10.7    2.8    7.5   12.0  -0.1   -5.3  -13.0   -2.7   0.12   0.14   1.7   81.1   51.8   95.1   85.3   30.2  100.0  89.7   57.2  98.0   77.7  65.4  97.5  .  
+ 7   98.9  173.6  274.1  300.8  173.4  64.1  108.9  232.7  234.3  4.45  4.87  23.8  -31.8   -7.4    8.6   -0.6   -2.9  15.5  -11.3  -14.9   -9.6   0.15   0.02  -1.8    9.0   82.7   64.4   99.9   95.1   36.0  23.4   46.2  60.3   86.5  99.8  96.0  .  
+ 8  108.9  170.6  270.7  301.5  180.5  60.5  128.7  234.3  240.5  4.37  4.92  29.0  -21.9  -10.3    5.1    0.1    4.3  11.9    8.5  -13.2   -3.4   0.07   0.07   3.4   32.2   68.8   85.6  100.0   89.4   54.7  43.4   54.2  93.9   96.9  96.4  85.6  .  
+ 9  128.7  203.1  266.0  292.7  169.1  47.2  142.9  240.5  270.4  4.29  4.92  28.2   -9.1   20.0    7.8  -11.0  -10.5   3.0    4.7  -12.1   12.2  -0.09  -0.03   2.6   63.3   40.2   89.6   76.0   80.5   95.6  84.8   71.2  75.4   97.6  99.8  96.3  .  
+10  142.9  256.7  150.2  286.1  139.3  56.3  135.7  270.4  234.9  5.12  5.26  44.3   -0.8    9.4  -19.3  -10.4   -1.7  10.2   -5.5   -0.3  -25.5   0.16   0.05  -1.7   99.4   83.3   39.6   74.7   99.0   58.0  83.5  100.0  31.7   90.7  98.5  99.5  .  
+11  135.7  198.2  270.4  278.5  175.7  57.2  110.7  234.9  248.0  4.42  5.12  24.0    4.9   17.2    4.9  -22.9   -0.6   8.5   -9.5  -12.6    4.1   0.12   0.27  -1.6   94.4   35.4   86.8   18.9   99.8   73.3  35.6   57.3  91.2   91.0  54.7  96.7  .  
+12  136.7  201.4  235.1  308.7  163.9  49.0  121.9  232.4  243.6  4.87  5.23  30.1   -1.1   18.2  -23.1    5.0  -15.7   4.8  -16.3  -20.2  -14.7   0.50   0.28   4.6   99.3   46.8   38.2   94.6   61.4   88.8  14.3   38.7  66.3   45.3  77.2  89.4  .  
+13  121.9  177.7  267.0  297.0  168.8  60.4   85.7  243.6  226.2  4.07  4.51  37.5   -9.5   -6.7   -1.8    1.1    0.0   8.1  -18.6   -7.3   -9.2  -0.04  -0.13  13.3   83.6   77.5   97.9   99.6  100.0   76.6  26.6   84.0  79.9   97.0  69.8  19.9  .  
+14   85.7  174.8  274.5  290.8  171.1  73.2  135.9  226.2  245.2  4.56  4.99  26.2   -0.6  -11.4   -6.7  -10.2   -7.5  18.8   -5.9    3.5  -10.7  -0.13  -0.29   8.4   99.4   61.8   85.9   68.6   81.8   21.3  64.1   96.7  69.8   92.2  72.2  67.5  .  
+15  135.9  174.1  261.6  303.4  190.5  53.8  146.6  245.2  253.6  4.45  5.04  33.3   -1.9   -9.0    3.4   -0.3   10.9   9.6    8.5   -7.3   -4.7   0.08   0.10   7.7   98.0   83.2   97.9  100.0   79.2   61.9  59.1   88.2  95.9   97.9  97.0  72.5  .  
+16  146.6  176.9  262.9  302.9  186.4  47.7  130.2  253.6  251.7  4.25  4.83  28.9    8.8   -6.2    4.7   -0.8    6.8   3.5   -7.9    1.0   -6.5  -0.12  -0.12   3.3   65.6   91.7   96.1   99.9   91.3   93.9  63.0   99.8  92.1   95.3  95.7  94.2  .  
+17  130.2  174.4  258.7  301.7  173.6  60.0  109.2  251.7  228.7  4.28  4.74  25.3   -0.5   -6.5   -6.8    0.3   -2.6  11.4  -10.9    4.2  -15.2  -0.02  -0.11  -0.3   99.9   86.1   75.6  100.0   95.9   57.3  25.3   94.1  28.3   99.8  90.2  99.9  .  
+18  109.2  178.8  271.7  301.4  179.5  55.3  122.4  228.7  239.5  4.52  5.06  23.1  -21.5   -2.2    6.2   -0.0    3.3   6.7    2.2  -18.8   -4.3   0.21   0.21  -2.4   33.2   98.4   79.7  100.0   93.7   82.5  94.4   29.1  90.2   73.9  69.4  92.6  .  
+19  122.4  178.5  265.5  300.9  184.6  45.0  110.3  239.5  245.7  4.41  5.04  27.7   -8.4   -2.4   -0.1   -0.5    8.4  -3.7   -9.8   -8.0    1.8   0.10   0.19   2.2   84.7   98.0  100.0   99.9   65.3   94.4  33.2   80.1  98.2   93.1  73.9  94.1  .  
+20  110.3  183.3  273.5  293.2  179.1  50.2  149.7  245.7  271.6  4.23  4.84  22.1  -27.5    0.2   15.4  -10.5   -0.4   6.0   11.6   -6.8   13.4  -0.15  -0.11  -3.4    1.6  100.0   65.3   78.1  100.0   83.1  37.3   89.7  71.1   93.2  96.5  93.9  .  
+21  149.7  259.9  171.6  287.8  138.5  44.6  112.8  271.6  234.7  4.65  5.00  34.3    0.4    6.6   -5.0   -7.1    7.8   0.4   15.0    0.4    2.1   0.11   0.10  -3.9  100.0   91.2   94.0   89.3   86.2   99.9  57.4  100.0  98.9   96.4  96.2  96.2  .  
+22  112.8  185.6  263.2  295.0  170.6  46.6   78.7  234.7  224.8  4.30  4.90  35.4  -18.6    1.3   -5.5   -0.9    1.8  -5.7  -25.5  -16.2  -10.6   0.19   0.25  11.2   50.3   99.1   81.7   99.8   97.5   87.7   8.2   42.1  74.2   51.2  26.3  31.8  .  
+##
+loop_
+_ndb_struct_sugar_step_parameters.step_id
+_ndb_struct_sugar_step_parameters.P_1
+_ndb_struct_sugar_step_parameters.tau_1
+_ndb_struct_sugar_step_parameters.Pn_1
+_ndb_struct_sugar_step_parameters.P_2
+_ndb_struct_sugar_step_parameters.tau_2
+_ndb_struct_sugar_step_parameters.Pn_2
+_ndb_struct_sugar_step_parameters.nu_1_1
+_ndb_struct_sugar_step_parameters.nu_1_2
+_ndb_struct_sugar_step_parameters.nu_1_3
+_ndb_struct_sugar_step_parameters.nu_1_4
+_ndb_struct_sugar_step_parameters.nu_1_5
+_ndb_struct_sugar_step_parameters.nu_2_1
+_ndb_struct_sugar_step_parameters.nu_2_2
+_ndb_struct_sugar_step_parameters.nu_2_3
+_ndb_struct_sugar_step_parameters.nu_2_4
+_ndb_struct_sugar_step_parameters.nu_2_5
+_ndb_struct_sugar_step_parameters.diff_nu_1_1
+_ndb_struct_sugar_step_parameters.diff_nu_1_2
+_ndb_struct_sugar_step_parameters.diff_nu_1_3
+_ndb_struct_sugar_step_parameters.diff_nu_1_4
+_ndb_struct_sugar_step_parameters.diff_nu_1_5
+_ndb_struct_sugar_step_parameters.diff_nu_2_1
+_ndb_struct_sugar_step_parameters.diff_nu_2_2
+_ndb_struct_sugar_step_parameters.diff_nu_2_3
+_ndb_struct_sugar_step_parameters.diff_nu_2_4
+_ndb_struct_sugar_step_parameters.diff_nu_2_5
+ 1  161.2  55.3  C2end  139.5  41.7  C1exo  326.0  54.5  307.6   33.7    0.0  323.9   41.9  328.3   10.8   16.1   -6.6  16.4  -18.5   14.8  -5.1  -12.8    6.9    1.1   -9.2   14.2  
+ 2  139.5  41.7  C1exo   92.8  38.3  O4end  323.9  41.9  328.3   10.8   16.1  322.4   23.3  358.1  339.7   36.6  -10.4   9.2   -4.3   -2.2   8.3    2.1   -5.3    5.9   -5.1    2.4  
+ 3   92.8  38.3  O4end  166.3  48.5  C2end  322.4  23.3  358.1  339.7   36.6  333.3   46.3  312.9   33.4  355.6  -17.8  25.5  -23.5   13.6   3.0   -5.9   12.9  -14.3   12.0   -3.9  
+ 4  166.3  48.5  C2end  128.6  46.3  C1exo  333.3  46.3  312.9   33.4  355.6  315.3   44.3  331.1    3.4   25.8    0.6   8.2  -13.3   14.5  -9.5  -21.4    9.3    3.9  -16.6   23.9  
+ 5  128.6  46.3  C1exo  126.9  49.8  C1exo  315.3  44.3  331.1    3.4   25.8  311.9   47.5  330.1    2.2   29.0  -19.6  12.1   -1.8   -9.6  18.4  -14.7   14.1   -8.5   -0.1    9.5  
+ 6  126.9  49.8  C1exo  101.3  47.3  O4end  311.9  47.5  330.1    2.2   29.0  311.9   34.1  350.7  341.5   42.1  -22.4  14.9   -2.5  -10.8  21.1   -8.4    5.5   -1.5   -3.4    7.8  
+ 7  101.3  47.3  O4end  115.6  49.2  C1exo  311.9  34.1  350.7  341.5   42.1  309.9   42.8  338.7  352.6   36.2  -23.0   2.0   17.8  -31.5  34.6  -16.7    9.4    0.1   -9.7   16.8  
+ 8  115.6  49.2  C1exo  140.4  46.4  C1exo  309.9  42.8  338.7  352.6   36.2  319.6   46.3  324.3   12.9   17.1  -25.0  10.7    5.8  -20.4  28.8   -6.9   12.8  -14.4   10.6   -2.4  
+ 9  140.4  46.4  C1exo  145.8  53.4  C2end  319.6  46.3  324.3   12.9   17.1  315.9   54.5  315.9   20.4   14.1  -17.9  13.0   -4.7   -5.7  14.8  -17.3   17.5  -11.5    2.5    8.7  
+10  145.8  53.4  C2end  147.5  47.0  C2end  315.9  54.5  315.9   20.4   14.1  322.3   47.0  320.3   18.6   11.7  -13.3  12.3   -7.0    0.0   7.8  -18.6   14.7   -6.8   -3.8   14.0  
+11  147.5  47.0  C2end  113.8  51.7  C1exo  322.3  47.0  320.3   18.6   11.7  307.1   44.2  339.1  350.6   39.1  -12.6  14.9  -12.6    5.6   4.3  -19.5   10.7    0.5  -11.7   19.6  
+12  153.2  43.1  C2end  128.5  45.0  C1exo  328.7  42.9  321.5   20.8    6.4  316.6   42.7  332.0    3.1   25.4   -8.8   9.7   -7.5    2.3   4.1  -16.6    5.7    4.6  -14.8   19.9  
+13  128.5  45.0  C1exo   67.7  43.8  C4exo  316.6  42.7  332.0    3.1   25.4  326.1    9.4   16.6  323.0   44.6  -17.8  10.0   -0.7   -9.9  17.6    5.7  -19.2   24.4  -21.8   10.4  
+14   67.7  43.8  C4exo  149.3  41.0  C2end  326.1   9.4   16.6  323.0   44.6  328.2   40.8  324.7   17.3    9.0  -14.2  11.6   -5.1   -3.1  11.0  -11.0    7.5   -2.5   -4.2    9.5  
+15  149.3  41.0  C2end  169.3  42.3  C2end  328.2  40.8  324.7   17.3    9.0  338.9   39.2  318.4   30.1  354.0   -9.3   7.6   -4.3   -1.3   6.6    5.8    2.2   -8.9   12.2  -11.5  
+16  169.3  42.3  C2end  147.0  42.3  C2end  338.9  39.2  318.4   30.1  354.0  326.3   43.1  324.5   16.5   10.6    1.4   6.0  -10.6   11.5  -8.3   -6.8    6.1   -2.8   -1.4    5.1  
+17  147.0  42.3  C2end  116.3  47.5  C1exo  326.3  43.1  324.5   16.5   10.6  312.0   42.0  339.0  353.4   34.5   -8.6  11.0   -8.4    3.5   3.1  -14.6    8.5    0.3   -8.9   15.0  
+18  116.3  47.5  C1exo  129.6  49.9  C1exo  312.0  42.0  339.0  353.4   34.5  312.2   48.2  328.2    4.7   27.1  -22.9   9.9    6.0  -19.6  27.0  -14.4   14.7  -10.5    2.5    7.6  
+19  129.6  49.9  C1exo  113.6  42.9  C1exo  312.2  48.2  328.2    4.7   27.1  316.4   36.8  342.8  352.0   32.7  -22.7  16.0   -4.7   -8.3  19.6  -10.1    3.3    4.2  -10.3   13.2  
+20  113.6  42.9  C1exo  156.3  51.0  C2end  316.4  36.8  342.8  352.0   32.7  324.4   50.9  313.3   27.9    4.3  -21.0   3.6   13.8  -26.6  30.3   -8.8   14.0  -14.0   10.0   -1.2  
+21  156.3  51.0  C2end  116.9  44.2  C1exo  324.4  50.9  313.3   27.9    4.3  315.3   39.3  340.0  354.3   31.7   -5.1   6.9   -6.2    3.4   0.8  -16.4   25.5  -24.9   15.8    0.5  
+22  116.9  44.2  C1exo   34.7  45.5  C3end  315.3  39.3  340.0  354.3   31.7  347.2  343.3   37.4  313.7   37.4  -19.0   6.6    7.3  -18.7  23.9   26.9  -45.3   45.2  -31.2    3.2  
+##
+#
+

File diff suppressed because it is too large
+ 283 - 261
package-lock.json


+ 40 - 35
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "3.11.0",
+  "version": "3.23.0",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -20,7 +20,7 @@
     "rebuild": "npm run clean && npm run build",
     "build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
     "build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
-    "build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
+    "build-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/",
     "build-webpack": "webpack --mode production --config ./webpack.config.production.js",
     "build-webpack-viewer": "webpack --mode production --config ./webpack.config.viewer.js",
     "watch": "concurrently -c \"green,green,gray,gray\" --names \"tsc,srv,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-servers\" \"npm:watch-extra\" \"npm:watch-webpack\"",
@@ -28,7 +28,7 @@
     "watch-viewer-debug": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer-debug\"",
     "watch-tsc": "tsc --watch --incremental",
     "watch-servers": "tsc --build tsconfig.commonjs.json --watch --incremental",
-    "watch-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/ --watch",
+    "watch-extra": "cpx \"src/**/*.{scss,html,ico,jpg}\" lib/ --watch",
     "watch-webpack": "webpack -w --mode development --stats minimal",
     "watch-webpack-viewer": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.js",
     "watch-webpack-viewer-debug": "webpack -w --mode development --stats minimal --config ./webpack.config.viewer.debug.js",
@@ -75,7 +75,9 @@
       "node_modules",
       "lib"
     ],
-    "testURL": "http://localhost/",
+    "testEnvironmentOptions": {
+      "url": "http://localhost/"
+    },
     "testRegex": "\\.spec\\.ts$"
   },
   "author": "Mol* Contributors",
@@ -87,70 +89,73 @@
     "Ludovic Autin <autin@scripps.edu>",
     "Michal Malý <michal.maly@ibt.cas.cz>",
     "Jiří Černý <jiri.cerny@ibt.cas.cz>",
-    "Panagiotis Tourlas <panagiot_tourlov@hotmail.com>"
+    "Panagiotis Tourlas <panagiot_tourlov@hotmail.com>",
+    "Adam Midlik <midlik@gmail.com>",
+    "Koya Sakuma <koya.sakuma.work@gmail.com>",
+    "Gianluca Tomasello <giagitom@gmail.com>"
   ],
   "license": "MIT",
   "devDependencies": {
-    "@graphql-codegen/add": "^3.1.1",
-    "@graphql-codegen/cli": "^2.6.2",
-    "@graphql-codegen/time": "^3.1.1",
-    "@graphql-codegen/typescript": "^2.5.1",
-    "@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
-    "@graphql-codegen/typescript-graphql-request": "^4.4.10",
-    "@graphql-codegen/typescript-operations": "^2.4.2",
+    "@graphql-codegen/add": "^3.2.1",
+    "@graphql-codegen/cli": "^2.13.7",
+    "@graphql-codegen/time": "^3.2.1",
+    "@graphql-codegen/typescript": "^2.7.4",
+    "@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
+    "@graphql-codegen/typescript-graphql-request": "^4.5.6",
+    "@graphql-codegen/typescript-operations": "^2.5.4",
     "@types/cors": "^2.8.12",
-    "@types/gl": "^4.1.0",
-    "@types/jest": "^28.1.3",
-    "@types/react": "^18.0.14",
-    "@types/react-dom": "^18.0.5",
-    "@typescript-eslint/eslint-plugin": "^5.29.0",
-    "@typescript-eslint/parser": "^5.29.0",
+    "@types/gl": "^4.1.1",
+    "@types/jest": "^29.1.2",
+    "@types/react": "^18.0.21",
+    "@types/react-dom": "^18.0.6",
+    "@typescript-eslint/eslint-plugin": "^5.40.0",
+    "@typescript-eslint/parser": "^5.40.0",
     "benchmark": "^2.1.4",
-    "concurrently": "^7.2.2",
+    "concurrently": "^7.4.0",
     "cpx2": "^4.2.0",
     "crypto-browserify": "^3.12.0",
     "css-loader": "^6.7.1",
-    "eslint": "^8.18.0",
+    "eslint": "^8.25.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.2.0",
     "fs-extra": "^10.1.0",
-    "graphql": "^16.5.0",
+    "graphql": "^16.6.0",
     "http-server": "^14.1.1",
-    "jest": "^28.1.1",
+    "jest": "^29.2.0",
     "mini-css-extract-plugin": "^2.6.1",
     "path-browserify": "^1.0.1",
     "raw-loader": "^4.0.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "sass": "^1.53.0",
-    "sass-loader": "^13.0.1",
-    "simple-git": "^3.10.0",
+    "sass": "^1.55.0",
+    "sass-loader": "^13.1.0",
+    "simple-git": "^3.14.1",
     "stream-browserify": "^3.0.0",
     "style-loader": "^3.3.1",
-    "ts-jest": "^28.0.5",
-    "typescript": "^4.7.4",
-    "webpack": "^5.73.0",
+    "ts-jest": "^29.0.3",
+    "typescript": "^4.8.4",
+    "webpack": "^5.74.0",
     "webpack-cli": "^4.10.0"
   },
   "dependencies": {
     "@types/argparse": "^2.0.10",
-    "@types/benchmark": "^2.1.1",
+    "@types/benchmark": "^2.1.2",
     "@types/compression": "1.7.2",
-    "@types/express": "^4.17.13",
-    "@types/node": "^16.11.41",
+    "@types/express": "^4.17.14",
+    "@types/node": "^16.11.66",
     "@types/node-fetch": "^2.6.2",
     "@types/swagger-ui-dist": "3.30.1",
     "argparse": "^2.0.1",
-    "body-parser": "^1.20.0",
+    "body-parser": "^1.20.1",
     "compression": "^1.7.4",
     "cors": "^2.8.5",
-    "express": "^4.18.1",
+    "express": "^4.18.2",
     "h264-mp4-encoder": "^1.0.12",
     "immer": "^9.0.15",
     "immutable": "^4.1.0",
     "node-fetch": "^2.6.7",
-    "rxjs": "^7.5.5",
-    "swagger-ui-dist": "^4.12.0",
+    "rxjs": "^7.5.7",
+    "swagger-ui-dist": "^4.14.3",
     "tslib": "^2.4.0",
     "util.promisify": "^1.1.1",
     "xhr2": "^0.2.1"

+ 27 - 27
src/apps/docking-viewer/index.ts

@@ -58,20 +58,22 @@ class Viewer {
     }
 
     static async create(elementOrId: string | HTMLElement, colors = [Color(0x992211), Color(0xDDDDDD)], showButtons = true) {
-        const o = { ...DefaultViewerOptions, ...{
-            layoutIsExpanded: false,
-            layoutShowControls: false,
-            layoutShowRemoteState: false,
-            layoutShowSequence: true,
-            layoutShowLog: false,
-            layoutShowLeftPanel: true,
-
-            viewportShowExpand: true,
-            viewportShowControls: false,
-            viewportShowSettings: false,
-            viewportShowSelectionMode: false,
-            viewportShowAnimation: false,
-        } };
+        const o = {
+            ...DefaultViewerOptions, ...{
+                layoutIsExpanded: false,
+                layoutShowControls: false,
+                layoutShowRemoteState: false,
+                layoutShowSequence: true,
+                layoutShowLog: false,
+                layoutShowLeftPanel: true,
+
+                viewportShowExpand: true,
+                viewportShowControls: false,
+                viewportShowSettings: false,
+                viewportShowSelectionMode: false,
+                viewportShowAnimation: false,
+            }
+        };
         const defaultSpec = DefaultPluginUISpec();
 
         const spec: PluginUISpec = {
@@ -135,18 +137,16 @@ class Viewer {
             }
         };
 
-        plugin.behaviors.canvas3d.initialized.subscribe(v => {
-            if (v) {
-                PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
-                    renderer: {
-                        ...plugin.canvas3d!.props.renderer,
-                        backgroundColor: ColorNames.white,
-                    },
-                    camera: {
-                        ...plugin.canvas3d!.props.camera,
-                        helper: { axes: { name: 'off', params: {} } }
-                    }
-                } });
+        PluginCommands.Canvas3D.SetSettings(plugin, {
+            settings: {
+                renderer: {
+                    ...plugin.canvas3d!.props.renderer,
+                    backgroundColor: ColorNames.white,
+                },
+                camera: {
+                    ...plugin.canvas3d!.props.camera,
+                    helper: { axes: { name: 'off', params: {} } }
+                }
             }
         });
 
@@ -166,7 +166,7 @@ class Viewer {
             structures.push({ ref: structureProperties?.ref || structure.ref });
         }
 
-        // remove current structuresfrom hierarchy as they will be merged
+        // remove current structures from hierarchy as they will be merged
         // TODO only works with using loadStructuresFromUrlsAndMerge once
         //      need some more API metho to work with the hierarchy
         this.plugin.managers.structure.hierarchy.updateCurrent(this.plugin.managers.structure.hierarchy.current.structures, 'remove');

+ 3 - 3
src/apps/docking-viewer/viewport.tsx

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -202,14 +202,14 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
         const components = {
             ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
             surroundings: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandSurroundings, `surroundings`),
-            interactions: await plugin.builders.structure.tryCreateComponentFromSelection(structureCell, ligandPlusSurroundings, `interactions`)
+            interactions: await presetStaticComponent(plugin, structureCell, 'ligand'),
         };
 
         const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
         const representations = {
             ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
             ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
-            interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
+            interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, parentDisplay: 'between' }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
             label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
         };
 

+ 9 - 2
src/apps/viewer/app.ts

@@ -46,6 +46,7 @@ import { Color } from '../../mol-util/color';
 import '../../mol-util/polyfill';
 import { ObjectKeys } from '../../mol-util/type-helpers';
 import { SaccharideCompIdMapType } from '../../mol-model/structure/structure/carbohydrates/constants';
+import { Backgrounds } from '../../extensions/backgrounds';
 
 export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
 export { setDebugMode, setProductionMode, setTimingMode } from '../../mol-util/debug';
@@ -55,6 +56,7 @@ const CustomFormats = [
 ];
 
 const Extensions = {
+    'backgrounds': PluginSpec.Behavior(Backgrounds),
     'cellpack': PluginSpec.Behavior(CellPack),
     'dnatco-confal-pyramids': PluginSpec.Behavior(DnatcoConfalPyramids),
     'pdbe-structure-quality-report': PluginSpec.Behavior(PDBeStructureQualityReport),
@@ -86,7 +88,9 @@ const DefaultViewerOptions = {
     pickScale: PluginConfig.General.PickScale.defaultValue,
     pickPadding: PluginConfig.General.PickPadding.defaultValue,
     enableWboit: PluginConfig.General.EnableWboit.defaultValue,
+    enableDpoit: PluginConfig.General.EnableDpoit.defaultValue,
     preferWebgl1: PluginConfig.General.PreferWebGl1.defaultValue,
+    allowMajorPerformanceCaveat: PluginConfig.General.AllowMajorPerformanceCaveat.defaultValue,
 
     viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
     viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
@@ -156,7 +160,9 @@ export class Viewer {
                 [PluginConfig.General.PickScale, o.pickScale],
                 [PluginConfig.General.PickPadding, o.pickPadding],
                 [PluginConfig.General.EnableWboit, o.enableWboit],
+                [PluginConfig.General.EnableDpoit, o.enableDpoit],
                 [PluginConfig.General.PreferWebGl1, o.preferWebgl1],
+                [PluginConfig.General.AllowMajorPerformanceCaveat, o.allowMajorPerformanceCaveat],
                 [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
                 [PluginConfig.Viewport.ShowControls, o.viewportShowControls],
                 [PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
@@ -197,7 +203,7 @@ export class Viewer {
         return PluginCommands.State.Snapshots.OpenUrl(this.plugin, { url, type });
     }
 
-    loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions) {
+    loadStructureFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false, options?: LoadStructureOptions & { label?: string }) {
         const params = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
         return this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
             source: {
@@ -206,6 +212,7 @@ export class Viewer {
                     url: Asset.Url(url),
                     format: format as any,
                     isBinary,
+                    label: options?.label,
                     options: { ...params.source.params.options, representationParams: options?.representationParams as any },
                 }
             }
@@ -494,4 +501,4 @@ export const ViewerAutoPreset = StructureRepresentationPresetProvider({
             return await PresetStructureRepresentations.auto.apply(ref, params, plugin);
         }
     }
-});
+});

+ 9 - 0
src/apps/viewer/embedded.html

@@ -38,6 +38,15 @@
                 viewer.loadPdb('7bv2');
                 viewer.loadEmdb('EMD-30210', { detail: 6 });
                 // viewer.loadAllModelsOrAssemblyFromUrl('https://cs.litemol.org/5ire/full', 'mmcif', false, { representationParams: { theme: { globalName: 'operator-name' } } })
+                // viewer.loadStructureFromUrl('my url', 'pdb', false, {
+                //     representationParams: {
+                //         theme: {
+                //             globalName: 'uniform',
+                //             globalColorParams: { value: 0xff0000 }
+                //         }
+                //     },
+                //     label: 'my structure'
+                // });
             });
         </script>
     </body>

+ 5 - 1
src/apps/viewer/index.html

@@ -60,7 +60,9 @@
             var pickScale = getParam('pick-scale', '[^&]+').trim();
             var pickPadding = getParam('pick-padding', '[^&]+').trim();
             var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
+            var enableDpoit = getParam('enable-dpoit', '[^&]+').trim() === '1';
             var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
+            var allowMajorPerformanceCaveat = getParam('allow-major-performance-caveat', '[^&]+').trim() === '1';
 
             molstar.Viewer.create('app', {
                 layoutShowControls: !hideControls,
@@ -74,8 +76,10 @@
                 pixelScale: parseFloat(pixelScale) || 1,
                 pickScale: parseFloat(pickScale) || 0.25,
                 pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
-                enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
+                enableWboit: (disableWboit || enableDpoit) ? false : void 0, // use default value if disable-wboit is not set
+                enableDpoit: enableDpoit ? true : void 0,
                 preferWebgl1: preferWebgl1,
+                allowMajorPerformanceCaveat: allowMajorPerformanceCaveat,
             }).then(viewer => {
                 var snapshotId = getParam('snapshot-id', '[^&]+').trim();
                 if (snapshotId) viewer.setRemoteSnapshot(snapshotId);

+ 9 - 4
src/cli/chem-comp-dict/create-ions.ts

@@ -15,7 +15,7 @@ const writeFile = util.promisify(fs.writeFile);
 
 import { DatabaseCollection } from '../../mol-data/db';
 import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
-import { ensureDataAvailable, readCCD } from './util';
+import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
 
 function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
     const ionNames: string[] = [];
@@ -44,8 +44,8 @@ export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").re
     writeFile(filePath, output);
 }
 
-async function run(out: string, forceDownload = false) {
-    await ensureDataAvailable(forceDownload);
+async function run(out: string, options = DefaultDataOptions) {
+    await ensureDataAvailable(options);
     const ccd = await readCCD();
     const ionNames = extractIonNames(ccd);
     if (!fs.existsSync(path.dirname(out))) {
@@ -65,10 +65,15 @@ parser.add_argument('--forceDownload', '-f', {
     action: 'store_true',
     help: 'Force download of CCD and PVCD.'
 });
+parser.add_argument('--ccdUrl', '-c', {
+    help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
+    required: false
+});
 interface Args {
     out: string,
     forceDownload?: boolean,
+    ccdUrl?: string
 }
 const args: Args = parser.parse_args();
 
-run(args.out, args.forceDownload);
+run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });

+ 9 - 4
src/cli/chem-comp-dict/create-saccharides.ts

@@ -14,7 +14,7 @@ const writeFile = util.promisify(fs.writeFile);
 
 import { DatabaseCollection } from '../../mol-data/db';
 import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
-import { ensureDataAvailable, readCCD } from './util';
+import { DefaultDataOptions, ensureDataAvailable, readCCD } from './util';
 
 function extractSaccharideNames(ccd: DatabaseCollection<CCD_Schema>) {
     const saccharideNames: string[] = [];
@@ -47,8 +47,8 @@ export const SaccharideNames = new Set(${JSON.stringify(ionNames).replace(/"/g,
     writeFile(filePath, output);
 }
 
-async function run(out: string, forceDownload = false) {
-    await ensureDataAvailable(forceDownload);
+async function run(out: string, options = DefaultDataOptions) {
+    await ensureDataAvailable(options);
     const ccd = await readCCD();
     const saccharideNames = extractSaccharideNames(ccd);
     if (!fs.existsSync(path.dirname(out))) {
@@ -68,10 +68,15 @@ parser.add_argument('--forceDownload', '-f', {
     action: 'store_true',
     help: 'Force download of CCD and PVCD.'
 });
+parser.add_argument('--ccdUrl', '-c', {
+    help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
+    required: false
+});
 interface Args {
     out: string,
     forceDownload?: boolean,
+    ccdUrl?: string
 }
 const args: Args = parser.parse_args();
 
-run(args.out, args.forceDownload);
+run(args.out, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl });

+ 15 - 5
src/cli/chem-comp-dict/create-table.ts

@@ -18,7 +18,7 @@ import { SetUtils } from '../../mol-util/set';
 import { DefaultMap } from '../../mol-util/map';
 import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
 import { ccd_chemCompAtom_schema } from '../../mol-io/reader/cif/schema/ccd-extras';
-import { ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
+import { DefaultDataOptions, ensureDataAvailable, getEncodedCif, readCCD, readPVCD } from './util';
 
 type CCB = Table<CCD_Schema['chem_comp_bond']>
 type CCA = Table<CCD_Schema['chem_comp_atom']>
@@ -239,8 +239,8 @@ function createAtoms(ccd: DatabaseCollection<CCD_Schema>, pvcd: DatabaseCollecti
     );
 }
 
-async function run(out: string, binary = false, forceDownload = false, ccaOut?: string) {
-    await ensureDataAvailable(forceDownload);
+async function run(out: string, binary = false, options = DefaultDataOptions, ccaOut?: string) {
+    await ensureDataAvailable(options);
     const ccd = await readCCD();
     const pvcd = await readPVCD();
 
@@ -283,12 +283,22 @@ parser.add_argument('--ccaOut', '-a', {
     help: 'Optional generated file output path for chem_comp_atom data.',
     required: false
 });
+parser.add_argument('--ccdUrl', '-c', {
+    help: 'Fetch the CCD from a custom URL. This forces download of the CCD.',
+    required: false
+});
+parser.add_argument('--pvcdUrl', '-p', {
+    help: 'Fetch the PVCD from a custom URL. This forces download of the PVCD.',
+    required: false
+});
 interface Args {
     out: string,
     forceDownload?: boolean,
     binary?: boolean,
-    ccaOut?: string
+    ccaOut?: string,
+    ccdUrl?: string,
+    pvcdUrl?: string
 }
 const args: Args = parser.parse_args();
 
-run(args.out, args.binary, args.forceDownload, args.ccaOut);
+run(args.out, args.binary, { forceDownload: args.forceDownload, ccdUrl: args.ccdUrl, pvcdUrl: args.pvcdUrl }, args.ccaOut);

+ 13 - 3
src/cli/chem-comp-dict/util.ts

@@ -35,9 +35,9 @@ export async function ensureAvailable(path: string, url: string, forceDownload =
     }
 }
 
-export async function ensureDataAvailable(forceDownload = false) {
-    await ensureAvailable(CCD_PATH, CCD_URL, forceDownload);
-    await ensureAvailable(PVCD_PATH, PVCD_URL, forceDownload);
+export async function ensureDataAvailable(options: DataOptions) {
+    await ensureAvailable(CCD_PATH, options.ccdUrl || CCD_URL, !!options.ccdUrl || options.forceDownload);
+    await ensureAvailable(PVCD_PATH, options.pvcdUrl || PVCD_URL, !!options.pvcdUrl || options.forceDownload);
 }
 
 export async function readFileAsCollection<S extends Database.Schema>(path: string, schema: S) {
@@ -68,6 +68,16 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
     return encoder.getData();
 }
 
+export type DataOptions = {
+    ccdUrl?: string,
+    pvcdUrl?: string,
+    forceDownload?: boolean
+}
+
+export const DefaultDataOptions: DataOptions = {
+    forceDownload: false
+};
+
 const DATA_DIR = path.join(__dirname, '..', '..', '..', '..', 'build/data');
 const CCD_PATH = path.join(DATA_DIR, 'components.cif');
 const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif');

+ 1 - 0
src/cli/cifschema/util/cif-dic.ts

@@ -71,6 +71,7 @@ export function getFieldType(type: string, description: string, values?: string[
         case 'ec-type':
         case 'ucode-alphanum-csv':
         case 'id_list':
+        case 'entity_id_list':
             return ListCol('str', ',', description);
         case 'id_list_spc':
             return ListCol('str', ' ', description);

+ 3 - 3
src/examples/alpha-orbitals/controls.tsx

@@ -4,16 +4,16 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
 import { AlphaOrbitalsExample } from '.';
 import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
 import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
 import { PluginContextContainer } from '../../mol-plugin-ui/plugin';
 
 export function mountControls(orbitals: AlphaOrbitalsExample, parent: Element) {
-    ReactDOM.render(<PluginContextContainer plugin={orbitals.plugin}>
+    createRoot(parent).render(<PluginContextContainer plugin={orbitals.plugin}>
         <Controls orbitals={orbitals} />
-    </PluginContextContainer>, parent);
+    </PluginContextContainer>);
 }
 
 function Controls({ orbitals }: { orbitals: AlphaOrbitalsExample }) {

+ 11 - 15
src/examples/alpha-orbitals/index.ts

@@ -82,24 +82,20 @@ export class AlphaOrbitalsExample {
 
         this.plugin.managers.interactivity.setProps({ granularity: 'element' });
 
-        this.plugin.behaviors.canvas3d.initialized.subscribe(init => {
-            if (!init) return;
-
-            if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
-                PluginCommands.Toast.Show(this.plugin, {
-                    title: 'Error',
-                    message: `Browser/device does not support required WebGL extension (OES_texture_float).`
-                });
-                return;
-            }
-
-            this.load({
-                moleculeSdf: DemoMoleculeSDF,
-                ...DemoOrbitals
+        if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
+            PluginCommands.Toast.Show(this.plugin, {
+                title: 'Error',
+                message: `Browser/device does not support required WebGL extension (OES_texture_float).`
             });
+            return;
+        }
 
-            mountControls(this, document.getElementById('controls')!);
+        this.load({
+            moleculeSdf: DemoMoleculeSDF,
+            ...DemoOrbitals
         });
+
+        mountControls(this, document.getElementById('controls')!);
     }
 
     readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);

BIN
src/extensions/backgrounds/images/cells.jpg


+ 91 - 0
src/extensions/backgrounds/index.ts

@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
+import { PluginConfig } from '../../mol-plugin/config';
+import { Color } from '../../mol-util/color/color';
+
+// from https://visualsonline.cancer.gov/details.cfm?imageid=2304, public domain
+import image_cells from './images/cells.jpg';
+
+// created with http://alexcpeterson.com/spacescape/
+import face_nebula_nx from './skyboxes/nebula/nebula_left2.jpg';
+import face_nebula_ny from './skyboxes/nebula/nebula_bottom4.jpg';
+import face_nebula_nz from './skyboxes/nebula/nebula_back6.jpg';
+import face_nebula_px from './skyboxes/nebula/nebula_right1.jpg';
+import face_nebula_py from './skyboxes/nebula/nebula_top3.jpg';
+import face_nebula_pz from './skyboxes/nebula/nebula_front5.jpg';
+
+export const Backgrounds = PluginBehavior.create<{ }>({
+    name: 'extension-backgrounds',
+    category: 'misc',
+    display: {
+        name: 'Backgrounds'
+    },
+    ctor: class extends PluginBehavior.Handler<{ }> {
+        register(): void {
+            this.ctx.config.set(PluginConfig.Background.Styles, [
+                [{
+                    variant: {
+                        name: 'radialGradient',
+                        params: {
+                            centerColor: Color(0xFFFFFF),
+                            edgeColor: Color(0x808080),
+                            ratio: 0.2,
+                            coverage: 'viewport',
+                        }
+                    }
+                }, 'Light Radial Gradient'],
+                [{
+                    variant: {
+                        name: 'image',
+                        params: {
+                            source: {
+                                name: 'url',
+                                params: image_cells
+                            },
+                            lightness: 0,
+                            saturation: 0,
+                            opacity: 1,
+                            coverage: 'viewport',
+                        }
+                    }
+                }, 'Normal Cells Image'],
+                [{
+                    variant: {
+                        name: 'skybox',
+                        params: {
+                            faces: {
+                                name: 'urls',
+                                params: {
+                                    nx: face_nebula_nx,
+                                    ny: face_nebula_ny,
+                                    nz: face_nebula_nz,
+                                    px: face_nebula_px,
+                                    py: face_nebula_py,
+                                    pz: face_nebula_pz,
+                                }
+                            },
+                            lightness: 0,
+                            saturation: 0,
+                            opacity: 1,
+                            blur: 0.3,
+                        }
+                    }
+                }, 'Purple Nebula Skybox'],
+            ]);
+        }
+
+        update() {
+            return false;
+        }
+
+        unregister() {
+            this.ctx.config.set(PluginConfig.Background.Styles, []);
+        }
+    },
+    params: () => ({ })
+});

BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_back6.jpg


BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_bottom4.jpg


BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_front5.jpg


BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_left2.jpg


BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_right1.jpg


BIN
src/extensions/backgrounds/skyboxes/nebula/nebula_top3.jpg


+ 10 - 0
src/extensions/backgrounds/typings.d.ts

@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+declare module '*.jpg' {
+    const value: string;
+    export = value;
+}

+ 11 - 0
src/extensions/cellpack/model.ts

@@ -581,9 +581,20 @@ export const LoadCellPackModel = StateAction.build({
 })(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
     if (params.preset.adjustStyle) {
         ctx.managers.interactivity.setProps({ granularity: 'chain' });
+        ctx.managers.structure.component.setOptions({
+            ... ctx.managers.structure.component.state.options,
+            visualQuality: 'custom',
+            ignoreLight: true,
+            showHydrogens: false,
+        });
         ctx.canvas3d?.setProps({
             multiSample: { mode: 'off' },
             cameraClipping: { far: false },
+            renderer: { colorMarker: false },
+            marking: {
+                enabled: true,
+                ghostEdgeStrength: 1,
+            },
             postprocessing: {
                 occlusion: {
                     name: 'on',

+ 11 - 13
src/extensions/dnatco/confal-pyramids/behavior.ts

@@ -8,7 +8,7 @@
 import { ConfalPyramidsColorThemeProvider } from './color';
 import { ConfalPyramids, ConfalPyramidsProvider } from './property';
 import { ConfalPyramidsRepresentationProvider } from './representation';
-import { Loci } from '../../../mol-model/loci';
+import { ConfalPyramidsTypes } from './types';
 import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
 import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../mol-plugin-state/builder/structure/representation-preset';
 import { StateObjectRef } from '../../../mol-state';
@@ -56,21 +56,10 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
         description: 'Schematic depiction of conformer class and confal value.',
     },
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
-
         private provider = ConfalPyramidsProvider;
 
-        private labelConfalPyramids = {
-            label: (loci: Loci): string | undefined => {
-                if (!this.params.showToolTip) return void 0;
-
-                /* TODO: Implement this */
-                return void 0;
-            }
-        };
-
         register(): void {
             this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
-            this.ctx.managers.lociLabels.addProvider(this.labelConfalPyramids);
 
             this.ctx.representation.structure.themes.colorThemeRegistry.add(ConfalPyramidsColorThemeProvider);
             this.ctx.representation.structure.registry.add(ConfalPyramidsRepresentationProvider);
@@ -88,7 +77,6 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
 
         unregister() {
             this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
-            this.ctx.managers.lociLabels.removeProvider(this.labelConfalPyramids);
 
             this.ctx.representation.structure.registry.remove(ConfalPyramidsRepresentationProvider);
             this.ctx.representation.structure.themes.colorThemeRegistry.remove(ConfalPyramidsColorThemeProvider);
@@ -101,3 +89,13 @@ export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean,
         showToolTip: PD.Boolean(true)
     })
 });
+
+export function confalPyramidLabel(halfPyramid: ConfalPyramidsTypes.HalfPyramid) {
+    const { step } = halfPyramid;
+    return `
+        <b>${step.auth_asym_id_1}</b> |
+        <b>${step.label_comp_id_1} ${step.auth_seq_id_1}${step.PDB_ins_code_1}${step.label_alt_id_1.length > 0 ? ` (alt ${step.label_alt_id_1})` : ''}
+           ${step.label_comp_id_2} ${step.auth_seq_id_2}${step.PDB_ins_code_2}${step.label_alt_id_2.length > 0 ? ` (alt ${step.label_alt_id_2})` : ''} </b><br />
+        <i>NtC:</i> ${step.NtC} | <i>Confal score:</i> ${step.confal_score} | <i>RMSD:</i> ${step.rmsd.toFixed(2)}
+    `;
+}

+ 2 - 2
src/extensions/dnatco/confal-pyramids/color.ts

@@ -247,8 +247,8 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
 
     function color(location: Location, isSecondary: boolean): Color {
         if (CPT.isLocation(location)) {
-            const { pyramid, isLower } = location.data;
-            const key = pyramid.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
+            const { step, isLower } = location.data;
+            const key = step.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
             return colorMap[key] ?? ErrorColor;
         }
 

+ 49 - 30
src/extensions/dnatco/confal-pyramids/property.ts

@@ -16,7 +16,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
 
-export type ConfalPyramids = PropertyWrapper<CPT.PyramidsData | undefined >;
+export type ConfalPyramids = PropertyWrapper<CPT.Steps | undefined>;
 
 export namespace ConfalPyramids {
     export const Schema = {
@@ -105,34 +105,42 @@ export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramids
 
 type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>;
 
-function createPyramidsFromCif(model: Model,
-    steps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
-    stepsSummary: StepsSummaryTable): CPT.PyramidsData {
-    const pyramids = new Array<CPT.Pyramid>();
-    const names = new Map<string, number>();
-    const locations = new Array<CPT.Location>();
-    let hasMultipleModels = false;
+function createPyramidsFromCif(
+    model: Model,
+    cifSteps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
+    stepsSummary: StepsSummaryTable
+): CPT.Steps {
+    const steps = new Array<CPT.Step>();
+    const mapping = new Array<CPT.MappedChains>();
 
     const {
         id, PDB_model_number, name,
         auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
         auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
-        _rowCount } = steps;
+        _rowCount
+    } = cifSteps;
 
     if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
 
     for (let i = 0; i < _rowCount; i++) {
-        const model_num = PDB_model_number.value(i);
-        if (model_num !== model.modelNum)
-            hasMultipleModels = true;
-
-        const { _NtC, _confal_score } = getNtCAndConfalScore(id.value(i), i, stepsSummary);
-
-        const pyramid = {
-            PDB_model_number: model_num,
+        const {
+            NtC,
+            confal_score,
+            rmsd
+        } = getSummaryData(id.value(i), i, stepsSummary);
+        const modelNum = PDB_model_number.value(i);
+        const chainId = auth_asym_id_1.value(i);
+        const seqId = auth_seq_id_1.value(i);
+        const modelIdx = modelNum - 1;
+
+        if (mapping.length <= modelIdx || !mapping[modelIdx])
+            mapping[modelIdx] = new Map<string, CPT.MappedResidues>();
+
+        const step = {
+            PDB_model_number: modelNum,
             name: name.value(i),
-            auth_asym_id_1: auth_asym_id_1.value(i),
-            auth_seq_id_1: auth_seq_id_1.value(i),
+            auth_asym_id_1: chainId,
+            auth_seq_id_1: seqId,
             label_comp_id_1: label_comp_id_1.value(i),
             label_alt_id_1: label_alt_id_1.value(i),
             PDB_ins_code_1: PDB_ins_code_1.value(i),
@@ -141,30 +149,41 @@ function createPyramidsFromCif(model: Model,
             label_comp_id_2: label_comp_id_2.value(i),
             label_alt_id_2: label_alt_id_2.value(i),
             PDB_ins_code_2: PDB_ins_code_2.value(i),
-            confal_score: _confal_score,
-            NtC: _NtC
+            confal_score,
+            NtC,
+            rmsd,
         };
 
-        pyramids.push(pyramid);
-        names.set(pyramid.name, pyramids.length - 1);
+        steps.push(step);
 
-        locations.push(CPT.Location(pyramid, false));
-        locations.push(CPT.Location(pyramid, true));
+        const mappedChains = mapping[modelIdx];
+        const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>();
+        const stepsForResidue = residuesOnChain.get(seqId) ?? [];
+        stepsForResidue.push(steps.length - 1);
+
+        residuesOnChain.set(seqId, stepsForResidue);
+        mappedChains.set(chainId, residuesOnChain);
+        mapping[modelIdx] = mappedChains;
     }
 
-    return { pyramids, names, locations, hasMultipleModels };
+    return { steps, mapping };
 }
 
-function getNtCAndConfalScore(id: number, i: number, stepsSummary: StepsSummaryTable) {
-    const { step_id, confal_score, assigned_NtC } = stepsSummary;
+function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) {
+    const {
+        step_id,
+        confal_score,
+        assigned_NtC,
+        cartesian_rmsd_closest_NtC_representative,
+    } = stepsSummary;
 
     // Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
     for (let j = i; j < stepsSummary._rowCount; j++) {
-        if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
+        if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
     }
     // Safety net for cases where the previous assumption is not met
     for (let j = 0; j < i; j++) {
-        if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
+        if (id === step_id.value(j)) return { NtC: assigned_NtC.value(j), confal_score: confal_score.value(j), rmsd: cartesian_rmsd_closest_NtC_representative.value(j) };
     }
     throw new Error('Inconsistent mmCIF data');
 }

+ 78 - 59
src/extensions/dnatco/confal-pyramids/representation.ts

@@ -6,7 +6,7 @@
  */
 
 import { ConfalPyramids, ConfalPyramidsProvider } from './property';
-import { ConfalPyramidsUtil } from './util';
+import { ConfalPyramidsIterator } from './util';
 import { ConfalPyramidsTypes as CPT } from './types';
 import { Interval } from '../../../mol-data/int';
 import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
@@ -16,14 +16,14 @@ import { PrimitiveBuilder } from '../../../mol-geo/primitive/primitive';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
 import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
 import { EmptyLoci, Loci } from '../../../mol-model/loci';
-import { Structure, StructureProperties, Unit } from '../../../mol-model/structure';
+import { Structure, Unit } from '../../../mol-model/structure';
 import { CustomProperty } from '../../../mol-model-props/common/custom-property';
 import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
 import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
 import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
 import { VisualUpdateState } from '../../../mol-repr/util';
 import { VisualContext } from '../../../mol-repr/visual';
-import { getAltResidueLociFromId, StructureGroup } from '../../../mol-repr/structure/visual/util/common';
+import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
 import { NullLocation } from '../../../mol-model/location';
@@ -32,6 +32,12 @@ const t = Mat4.identity();
 const w = Vec3.zero();
 const mp = Vec3.zero();
 
+const posO3 = Vec3();
+const posP = Vec3();
+const posOP1 = Vec3();
+const posOP2 = Vec3();
+const posO5 = Vec3();
+
 function calcMidpoint(mp: Vec3, v: Vec3, w: Vec3) {
     Vec3.sub(mp, v, w);
     Vec3.scale(mp, mp, 0.5);
@@ -53,64 +59,76 @@ function createConfalPyramidsIterator(structureGroup: StructureGroup): LocationI
     const { structure, group } = structureGroup;
     const instanceCount = group.units.length;
 
-    const prop = ConfalPyramidsProvider.get(structure.model).value;
-    if (prop === undefined || prop.data === undefined) {
-        return LocationIterator(0, 1, 1, () => NullLocation);
-    }
+    const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
+    if (!data) return LocationIterator(0, 1, 1, () => NullLocation);
 
-    const { locations } = prop.data;
+    const halfPyramidsCount = data.steps.length * 2;
 
     const getLocation = (groupIndex: number, instanceIndex: number) => {
-        if (locations.length <= groupIndex) return NullLocation;
-        return locations[groupIndex];
+        if (halfPyramidsCount <= groupIndex) return NullLocation;
+        const idx = Math.floor(groupIndex / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
+        return CPT.Location(data.steps[idx], groupIndex % 2 === 1);
     };
-    return LocationIterator(locations.length, instanceCount, 1, getLocation);
+    return LocationIterator(halfPyramidsCount, instanceCount, 1, getLocation);
 }
 
 function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ConfalPyramidsMeshParams>, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
 
-    const prop = ConfalPyramidsProvider.get(structure.model).value;
-    if (prop === undefined || prop.data === undefined) return Mesh.createEmpty(mesh);
-
-    const { pyramids } = prop.data;
-    if (pyramids.length === 0) return Mesh.createEmpty(mesh);
-
-    const mb = MeshBuilder.createState(512, 512, mesh);
-
-    const handler = (pyramid: CPT.Pyramid, first: ConfalPyramidsUtil.FirstResidueAtoms, second: ConfalPyramidsUtil.SecondResidueAtoms, firsLocIndex: number, secondLocIndex: number) => {
-        if (firsLocIndex === -1 || secondLocIndex === -1)
-            throw new Error('Invalid location index');
-
-        const scale = (pyramid.confal_score - 20.0) / 100.0;
-        const O3 = first.O3.pos;
-        const OP1 = second.OP1.pos; const OP2 = second.OP2.pos; const O5 = second.O5.pos; const P = second.P.pos;
-
-        shiftVertex(O3, P, scale);
-        shiftVertex(OP1, P, scale);
-        shiftVertex(OP2, P, scale);
-        shiftVertex(O5, P, scale);
-        calcMidpoint(mp, O3, O5);
-
-        mb.currentGroup = firsLocIndex;
-        let pb = PrimitiveBuilder(3);
-        /* Upper part (for first residue in step) */
-        pb.add(O3, OP1, OP2);
-        pb.add(O3, mp, OP1);
-        pb.add(O3, OP2, mp);
-        MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
-
-        /* Lower part (for second residue in step */
-        mb.currentGroup = secondLocIndex;
-        pb = PrimitiveBuilder(3);
-        pb.add(mp, O5, OP1);
-        pb.add(mp, OP2, O5);
-        pb.add(O5, OP2, OP1);
-        MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
-    };
-
-    const walker = new ConfalPyramidsUtil.UnitWalker(structure, unit, handler);
-    walker.walk();
+    const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
+    if (!data) return Mesh.createEmpty(mesh);
+
+    const { steps, mapping } = data;
+    if (steps.length === 0) return Mesh.createEmpty(mesh);
+    const vertexCount = (6 * steps.length) / mapping.length;
+
+    const mb = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh);
+
+    const it = new ConfalPyramidsIterator(structure, unit);
+    while (it.hasNext) {
+        const allPoints = it.move();
+
+        for (const points of allPoints) {
+            const { O3, P, OP1, OP2, O5, confalScore } = points;
+            const scale = (confalScore - 20.0) / 100.0;
+            // Steps can be drawn in a different order than they are stored.
+            // To make sure that we can get from the drawn pyramid back to the step in represents,
+            // we need to use an appropriate groupId. The stepIdx passed from the iterator
+            // is an index into the array of all steps in the structure.
+            // Since a step is drawn as two "half-pyramids" we need two ids to map to a single step.
+            // To do that, we just multiply the index by 2. idx*2 marks the "upper" half-pyramid,
+            // (idx*2)+1 the "lower" half-pyramid.
+            const groupIdx = points.stepIdx * 2;
+
+            unit.conformation.invariantPosition(O3, posO3);
+            unit.conformation.invariantPosition(P, posP);
+            unit.conformation.invariantPosition(OP1, posOP1);
+            unit.conformation.invariantPosition(OP2, posOP2);
+            unit.conformation.invariantPosition(O5, posO5);
+
+            shiftVertex(posO3, posP, scale);
+            shiftVertex(posOP1, posP, scale);
+            shiftVertex(posOP2, posP, scale);
+            shiftVertex(posO5, posP, scale);
+            calcMidpoint(mp, posO3, posO5);
+
+            mb.currentGroup = groupIdx;
+            let pb = PrimitiveBuilder(3);
+            /* Upper part (for first residue in step) */
+            pb.add(posO3, posOP1, posOP2);
+            pb.add(posO3, mp, posOP1);
+            pb.add(posO3, posOP2, mp);
+            MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
+
+            /* Lower part (for second residue in step) */
+            mb.currentGroup = groupIdx + 1;
+            pb = PrimitiveBuilder(3);
+            pb.add(mp, posO5, posOP1);
+            pb.add(mp, posOP2, posO5);
+            pb.add(posO5, posOP2, posOP1);
+            MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
+        }
+    }
 
     return MeshBuilder.getMesh(mb);
 }
@@ -124,16 +142,17 @@ function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGro
     const unit = structureGroup.group.units[instanceId];
     if (!Unit.isAtomic(unit)) return EmptyLoci;
 
-    const prop = ConfalPyramidsProvider.get(structure.model).value;
-    if (prop === undefined || prop.data === undefined) return EmptyLoci;
+    const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
+    if (!data) return EmptyLoci;
+
+    const halfPyramidsCount = data.steps.length * 2;
 
-    const { locations } = prop.data;
+    if (halfPyramidsCount <= groupId) return EmptyLoci;
 
-    if (locations.length <= groupId) return EmptyLoci;
-    const altId = StructureProperties.atom.label_alt_id(CPT.toElementLocation(locations[groupId]));
-    const rI = unit.residueIndex[locations[groupId].element.element];
+    const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
+    const step = data.steps[idx];
 
-    return getAltResidueLociFromId(structure, unit, rI, altId);
+    return CPT.Loci({ step, isLower: groupId % 2 === 1 }, [{}]);
 }
 
 function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {

+ 27 - 22
src/extensions/dnatco/confal-pyramids/types.ts

@@ -6,10 +6,13 @@
  */
 
 import { DataLocation } from '../../../mol-model/location';
-import { ElementIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
+import { DataLoci } from '../../../mol-model/loci';
+import { confalPyramidLabel } from './behavior';
 
 export namespace ConfalPyramidsTypes {
-    export type Pyramid = {
+    export const DataTag = 'dnatco-confal-half-pyramid';
+
+    export type Step = {
         PDB_model_number: number,
         name: string,
         auth_asym_id_1: string,
@@ -23,38 +26,40 @@ export namespace ConfalPyramidsTypes {
         label_alt_id_2: string,
         PDB_ins_code_2: string,
         confal_score: number,
-        NtC: string
+        NtC: string,
+        rmsd: number,
     }
 
-    export interface PyramidsData {
-        pyramids: Array<Pyramid>,
-        names: Map<string, number>,
-        locations: Array<Location>,
-        hasMultipleModels: boolean
-    }
+    export type MappedChains = Map<string, MappedResidues>;
+    export type MappedResidues = Map<number, number[]>;
 
-    export interface LocationData {
-        readonly pyramid: Pyramid
-        readonly isLower: boolean;
+    export interface Steps {
+        steps: Array<Step>,
+        mapping: MappedChains[],
     }
 
-    export interface Element {
-        structure: Structure;
-        unit: Unit.Atomic;
-        element: ElementIndex;
+    export interface HalfPyramid {
+        step: Step,
+        isLower: boolean,
     }
 
-    export interface Location extends DataLocation<LocationData, Element> {}
+    export interface Location extends DataLocation<HalfPyramid, {}> {}
 
-    export function Location(pyramid: Pyramid, isLower: boolean, structure?: Structure, unit?: Unit.Atomic, element?: ElementIndex) {
-        return DataLocation('pyramid', { pyramid, isLower }, { structure: structure as any, unit: unit as any, element: element as any });
+    export function Location(step: Step, isLower: boolean) {
+        return DataLocation(DataTag, { step, isLower }, {});
     }
 
     export function isLocation(x: any): x is Location {
-        return !!x && x.kind === 'data-location' && x.tag === 'pyramid';
+        return !!x && x.kind === 'data-location' && x.tag === DataTag;
+    }
+
+    export interface Loci extends DataLoci<HalfPyramid, {}> {}
+
+    export function Loci(data: HalfPyramid, elements: ReadonlyArray<{}>): Loci {
+        return DataLoci(DataTag, data, elements, undefined, () => confalPyramidLabel(data));
     }
 
-    export function toElementLocation(location: Location) {
-        return StructureElement.Location.create(location.element.structure, location.element.unit, location.element.element);
+    export function isLoci(x: any): x is Loci {
+        return !!x && x.kind === 'data-loci' && x.tag === DataTag;
     }
 }

+ 87 - 255
src/extensions/dnatco/confal-pyramids/util.ts

@@ -8,288 +8,120 @@
 import { ConfalPyramidsProvider } from './property';
 import { ConfalPyramidsTypes as CPT } from './types';
 import { Segmentation } from '../../../mol-data/int';
-import { Vec3 } from '../../../mol-math/linear-algebra';
 import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure';
 
-export namespace ConfalPyramidsUtil {
-    type Residue = Segmentation.Segment<ResidueIndex>;
+type Residue = Segmentation.Segment<ResidueIndex>;
 
-    export type AtomInfo = {
-        pos: Vec3,
-        index: ElementIndex,
-        fakeAltId: string,
-    };
+export type Pyramid = {
+    O3: ElementIndex,
+    P: ElementIndex,
+    OP1: ElementIndex,
+    OP2: ElementIndex,
+    O5: ElementIndex,
+    confalScore: number,
+    stepIdx: number,
+};
 
-    export type FirstResidueAtoms = {
-        O3: AtomInfo,
-    };
+const EmptyStepIndices = new Array<number>();
 
-    export type SecondResidueAtoms = {
-        OP1: AtomInfo,
-        OP2: AtomInfo,
-        O5: AtomInfo,
-        P: AtomInfo,
-    };
-
-    type ResidueInfo = {
-        PDB_model_num: number,
-        asym_id: string,
-        auth_asym_id: string,
-        seq_id: number,
-        auth_seq_id: number,
-        comp_id: string,
-        alt_id: string,
-        ins_code: string,
-    };
+function copyResidue(r?: Residue) {
+    return r ? { index: r.index, start: r.start, end: r.end } : void 0;
+}
 
-    export type Handler = (pyramid: CPT.Pyramid, first: FirstResidueAtoms, second: SecondResidueAtoms, firstLocIndex: number, secondLocIndex: number) => void;
+function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string): ElementIndex {
+    for (let eI = residue.start; eI < residue.end; eI++) {
+        loc.element = loc.unit.elements[eI];
+        const elName = StructureProperties.atom.label_atom_id(loc);
+        const elAltId = StructureProperties.atom.label_alt_id(loc);
 
-    function residueInfoFromLocation(loc: StructureElement.Location): ResidueInfo {
-        return {
-            PDB_model_num: StructureProperties.unit.model_num(loc),
-            asym_id: StructureProperties.chain.label_asym_id(loc),
-            auth_asym_id: StructureProperties.chain.auth_asym_id(loc),
-            seq_id: StructureProperties.residue.label_seq_id(loc),
-            auth_seq_id: StructureProperties.residue.auth_seq_id(loc),
-            comp_id: StructureProperties.atom.label_comp_id(loc),
-            alt_id: StructureProperties.atom.label_alt_id(loc),
-            ins_code: StructureProperties.residue.pdbx_PDB_ins_code(loc)
-        };
+        if (names.includes(elName) && (elAltId === altId || elAltId.length === 0))
+            return loc.element;
     }
 
-    export function hasMultipleModels(unit: Unit.Atomic): boolean {
-        const prop = ConfalPyramidsProvider.get(unit.model).value;
-        if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
-        return prop.data.hasMultipleModels;
-    }
+    return -1 as ElementIndex;
+}
 
-    function getPossibleAltIds(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] {
-        const possibleAltIds: string[] = [];
+function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue, altIdOne: string, altIdTwo: string, confalScore: number, stepIdx: number): Pyramid {
+    const O3 = getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne);
+    const P = getAtomIndex(loc, two, ['P'], altIdTwo);
+    const OP1 = getAtomIndex(loc, two, ['OP1'], altIdTwo);
+    const OP2 = getAtomIndex(loc, two, ['OP2'], altIdTwo);
+    const O5 = getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo);
 
-        const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
-        for (let rI = residue.start; rI <= residue.end - 1; rI++) {
-            loc.element = unit.elements[rI];
-            const altId = StructureProperties.atom.label_alt_id(loc);
-            if (altId !== '' && !possibleAltIds.includes(altId)) possibleAltIds.push(altId);
-        }
+    return { O3, P, OP1, OP2, O5, confalScore, stepIdx };
+}
 
-        return possibleAltIds;
+export class ConfalPyramidsIterator {
+    private chainIt: Segmentation.SegmentIterator<ChainIndex>;
+    private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
+    private residueOne?: Residue;
+    private residueTwo: Residue;
+    private data?: CPT.Steps;
+    private loc: StructureElement.Location;
+
+    private getStepIndices(r: Residue) {
+        this.loc.element = this.loc.unit.elements[r.start];
+
+        const modelIdx = StructureProperties.unit.model_num(this.loc) - 1;
+        const chainId = StructureProperties.chain.auth_asym_id(this.loc);
+        const seqId = StructureProperties.residue.auth_seq_id(this.loc);
+
+        const chains = this.data!.mapping[modelIdx];
+        if (!chains) return EmptyStepIndices;
+        const residues = chains.get(chainId);
+        if (!residues) return EmptyStepIndices;
+        return residues.get(seqId) ?? EmptyStepIndices;
     }
 
-    class Utility {
-        protected getPyramidByName(name: string): { pyramid: CPT.Pyramid | undefined, index: number } {
-            const index = this.data.names.get(name);
-            if (index === undefined) return { pyramid: undefined, index: -1 };
-
-            return { pyramid: this.data.pyramids[index], index };
-        }
-
-        protected stepToName(entry_id: string, modelNum: number, locFirst: StructureElement.Location, locSecond: StructureElement.Location, fakeAltId_1: string, fakeAltId_2: string) {
-            const first = residueInfoFromLocation(locFirst);
-            const second = residueInfoFromLocation(locSecond);
-            const model_id = this.hasMultipleModels ? `-m${modelNum}` : '';
-            const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
-            const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : '');
-            const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : '';
-            const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : '';
-
-            return `${entry_id}${model_id}_${first.auth_asym_id}_${first.comp_id}${alt_id_1}_${first.auth_seq_id}${ins_code_1}_${second.comp_id}${alt_id_2}_${second.auth_seq_id}${ins_code_2}`;
-        }
-
-        constructor(unit: Unit.Atomic) {
-            const prop = ConfalPyramidsProvider.get(unit.model).value;
-            if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data');
-
-            this.data = prop.data;
-            this.hasMultipleModels = hasMultipleModels(unit);
+    private moveStep() {
+        this.residueOne = copyResidue(this.residueTwo);
+        this.residueTwo = copyResidue(this.residueIt.move())!;
 
-            this.entryId = unit.model.entryId.toLowerCase();
-            this.modelNum = unit.model.modelNum;
-        }
-
-        protected readonly data: CPT.PyramidsData;
-        protected readonly hasMultipleModels: boolean;
-        protected readonly entryId: string;
-        protected readonly modelNum: number;
+        return this.toPyramids(this.residueOne!, this.residueTwo);
     }
 
-    export class UnitWalker extends Utility {
-        private getAtomIndices(names: string[], residue: Residue): ElementIndex[] {
-            const indices: ElementIndex[] = [];
-
-            const loc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
-            for (let rI = residue.start; rI <= residue.end - 1; rI++) {
-                loc.element = this.unit.elements[rI];
-                const thisName = StructureProperties.atom.label_atom_id(loc);
-                if (names.includes(thisName)) indices.push(loc.element);
-            }
-
-            if (indices.length === 0) {
-                let namesStr = '';
-                for (const n of names)
-                    namesStr += `${n} `;
-
-                throw new Error(`Element [${namesStr}] not found on residue ${residue.index}`);
-            }
-
-            return indices;
-        }
-
-        private getAtomPositions(indices: ElementIndex[]): Vec3[] {
-            const pos = this.unit.conformation.invariantPosition;
-            const positions: Vec3[] = [];
-
-            for (const eI of indices) {
-                const v = Vec3.zero();
-                pos(eI, v);
-                positions.push(v);
-            }
-
-            return positions;
-        }
-
-        private handleStep(firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[]) {
-            const modelNum = this.hasMultipleModels ? this.modelNum : -1;
-            let ok = false;
-
-            const firstLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
-            const secondLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
-            for (let i = 0; i < firstAtoms.length; i++) {
-                const first = firstAtoms[i];
-                for (let j = 0; j < secondAtoms.length; j++) {
-                    const second = secondAtoms[j];
-                    firstLoc.element = first.O3.index;
-                    secondLoc.element = second.OP1.index;
+    private toPyramids(one: Residue, two: Residue) {
+        const indices = this.getStepIndices(one);
 
-                    const name = this.stepToName(this.entryId, modelNum, firstLoc, secondLoc, first.O3.fakeAltId, second.OP1.fakeAltId);
-                    const { pyramid, index } = this.getPyramidByName(name);
-                    if (pyramid !== undefined) {
-                        const setLoc = (loc: CPT.Location, eI: ElementIndex) => {
-                            loc.element.structure = this.structure;
-                            loc.element.unit = this.unit;
-                            loc.element.element = eI;
-                        };
-
-                        const locIndex = index * 2;
-                        setLoc(this.data.locations[locIndex], firstLoc.element);
-                        setLoc(this.data.locations[locIndex + 1], secondLoc.element);
-                        this.handler(pyramid, first, second, locIndex, locIndex + 1);
-                        ok = true;
-                    }
-                }
-            }
-
-            if (!ok) throw new Error('Bogus step');
-        }
-
-        private processFirstResidue(residue: Residue, possibleAltIds: string[]) {
-            const indO3 = this.getAtomIndices(['O3\'', 'O3*'], residue);
-            const posO3 = this.getAtomPositions(indO3);
-
-            const altPos: FirstResidueAtoms[] = [
-                { O3: { pos: posO3[0], index: indO3[0], fakeAltId: '' } }
-            ];
-
-            for (let i = 1; i < indO3.length; i++) {
-                altPos.push({ O3: { pos: posO3[i], index: indO3[i], fakeAltId: '' } });
-            }
-
-            if (altPos.length === 1 && possibleAltIds.length > 1) {
-                /* We have some alternate positions on the residue but O3 does not have any - fake them */
-                altPos[0].O3.fakeAltId = possibleAltIds[0];
-
-                for (let i = 1; i < possibleAltIds.length; i++)
-                    altPos.push({ O3: { pos: posO3[0], index: indO3[0], fakeAltId: possibleAltIds[i] } });
-            }
-
-            return altPos;
+        const points = [];
+        for (const idx of indices) {
+            const step = this.data!.steps[idx];
+            points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.confal_score, idx));
         }
 
-        private processSecondResidue(residue: Residue, possibleAltIds: string[]) {
-            const indOP1 = this.getAtomIndices(['OP1'], residue);
-            const indOP2 = this.getAtomIndices(['OP2'], residue);
-            const indO5 = this.getAtomIndices(['O5\'', 'O5*'], residue);
-            const indP = this.getAtomIndices(['P'], residue);
-
-            const posOP1 = this.getAtomPositions(indOP1);
-            const posOP2 = this.getAtomPositions(indOP2);
-            const posO5 = this.getAtomPositions(indO5);
-            const posP = this.getAtomPositions(indP);
-
-            const infoOP1: AtomInfo[] = [];
-            /* We use OP1 as "pivotal" atom. There is no specific reason
-             * to pick OP1, it is as good a choice as any other atom
-             */
-            if (indOP1.length === 1 && possibleAltIds.length > 1) {
-                /* No altIds on OP1, fake them */
-                for (const altId of possibleAltIds)
-                    infoOP1.push({ pos: posOP1[0], index: indOP1[0], fakeAltId: altId });
-            } else {
-                for (let i = 0; i < indOP1.length; i++)
-                    infoOP1.push({ pos: posOP1[i], index: indOP1[i], fakeAltId: '' });
-            }
-
-            const mkInfo = (i: number, indices: ElementIndex[], positions: Vec3[], altId: string) => {
-                if (i >= indices.length) {
-                    const last = indices.length - 1;
-                    return { pos: positions[last], index: indices[last], fakeAltId: altId };
-                }
-
-                return { pos: positions[i], index: indices[i], fakeAltId: altId };
-            };
-
-            const altPos: SecondResidueAtoms[] = [];
-            for (let i = 0; i < infoOP1.length; i++) {
-                const altId = infoOP1[i].fakeAltId;
-
-                const OP2 = mkInfo(i, indOP2, posOP2, altId);
-                const O5 = mkInfo(i, indO5, posO5, altId);
-                const P = mkInfo(i, indP, posP, altId);
-
-                altPos.push({ OP1: infoOP1[i], OP2, O5, P });
-            }
-
-            return altPos;
-        }
-
-        private step(residue: Residue): { firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[] } {
-            const firstPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
-            const firstAtoms = this.processFirstResidue(residue, firstPossibleAltIds);
+        return points;
+    }
 
-            residue = this.residueIt.move();
+    constructor(structure: Structure, unit: Unit) {
+        this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
+        this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
 
-            const secondPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
-            const secondAtoms = this.processSecondResidue(residue, secondPossibleAltIds);
+        const prop = ConfalPyramidsProvider.get(unit.model).value;
+        this.data = prop?.data;
 
-            return { firstAtoms, secondAtoms };
+        if (this.chainIt.hasNext) {
+            this.residueIt.setSegment(this.chainIt.move());
+            if (this.residueIt.hasNext)
+                this.residueTwo = this.residueIt.move();
         }
 
-        walk() {
-            while (this.chainIt.hasNext) {
-                this.residueIt.setSegment(this.chainIt.move());
-
-                let residue = this.residueIt.move();
-                while (this.residueIt.hasNext) {
-                    try {
-                        const { firstAtoms, secondAtoms } = this.step(residue);
-
-                        this.handleStep(firstAtoms, secondAtoms);
-                    } catch (error) {
-                        /* Skip and move along */
-                        residue = this.residueIt.move();
-                    }
-                }
-            }
-        }
+        this.loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
+    }
 
-        constructor(private structure: Structure, private unit: Unit.Atomic, private handler: Handler) {
-            super(unit);
+    get hasNext() {
+        if (!this.data)
+            return false;
+        return this.residueIt.hasNext
+            ? true
+            : this.chainIt.hasNext;
+    }
 
-            this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements);
-            this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
+    move() {
+        if (this.residueIt.hasNext) {
+            return this.moveStep();
+        } else {
+            this.residueIt.setSegment(this.chainIt.move());
+            return this.moveStep();
         }
-
-        private chainIt: Segmentation.SegmentIterator<ChainIndex>;
-        private residueIt: Segmentation.SegmentIterator<ResidueIndex>;
     }
 }

+ 2 - 0
src/extensions/geo-export/ui.tsx

@@ -60,6 +60,8 @@ export class GeometryExporterUI extends CollapsableControls<{}, State> {
     }
 
     componentDidMount() {
+        if (!this.plugin.canvas3d) return;
+
         const merged = merge(
             this.controls.behaviors.params,
             this.plugin.canvas3d!.reprCount

+ 3 - 1
src/extensions/mp4-export/controls.ts

@@ -118,11 +118,13 @@ export class Mp4Controls extends PluginComponent {
     }
 
     private init() {
+        if (!this.plugin.canvas3d) return;
+
         this.subscribe(this.plugin.managers.animation.events.updated.pipe(debounceTime(16)), () => {
             this.sync();
         });
 
-        this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
+        this.subscribe(this.plugin.canvas3d.resized, () => this.syncInfo());
         this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
 
         this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));

+ 1 - 0
src/extensions/mp4-export/encoder.ts

@@ -69,6 +69,7 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
         const dt = durationMs / N;
 
         await ctx.update({ message: 'Rendering...', isIndeterminate: false, current: 0, max: N + 1 });
+        await params.pass.updateBackground();
 
         await plugin.managers.animation.play(params.animation.definition, params.animation.params);
         stoppedAnimation = false;

+ 1 - 4
src/extensions/rcsb/graphql/types.ts

@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
 export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
 export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
 export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
-// Generated on 2022-06-26T14:02:35-07:00
+// Generated on 2022-08-20T16:36:05-07:00
 
 /** All built-in and custom scalars, mapped to their actual values */
 export type Scalars = {
@@ -13,11 +13,8 @@ export type Scalars = {
   Boolean: boolean;
   Int: number;
   Float: number;
-  /** Built-in scalar representing an instant in time */
   Date: any;
-  /** Built-in scalar for dynamic values */
   ObjectScalar: any;
-  /** Use SPQR's SchemaPrinter to remove this from SDL */
   UNREPRESENTABLE: any;
 };
 

+ 11 - 7
src/mol-canvas3d/camera.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -260,7 +260,8 @@ namespace Camera {
             radius: 0,
             radiusMax: 10,
             fog: 50,
-            clipFar: true
+            clipFar: true,
+            minNear: 5,
         };
     }
 
@@ -276,6 +277,7 @@ namespace Camera {
         radiusMax: number
         fog: number
         clipFar: boolean
+        minNear: number
     }
 
     export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
@@ -292,6 +294,7 @@ namespace Camera {
         if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
         if (typeof source.fog !== 'undefined') out.fog = source.fog;
         if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
+        if (typeof source.minNear !== 'undefined') out.minNear = source.minNear;
 
         return out;
     }
@@ -303,6 +306,7 @@ namespace Camera {
             && a.radiusMax === b.radiusMax
             && a.fog === b.fog
             && a.clipFar === b.clipFar
+            && a.minNear === b.minNear
             && Vec3.exactEquals(a.position, b.position)
             && Vec3.exactEquals(a.up, b.up)
             && Vec3.exactEquals(a.target, b.target);
@@ -370,7 +374,7 @@ function updatePers(camera: Camera) {
 }
 
 function updateClip(camera: Camera) {
-    let { radius, radiusMax, mode, fog, clipFar } = camera.state;
+    let { radius, radiusMax, mode, fog, clipFar, minNear } = camera.state;
     if (radius < 0.01) radius = 0.01;
 
     const normalizedFar = clipFar ? radius : radiusMax;
@@ -384,12 +388,12 @@ function updateClip(camera: Camera) {
 
     if (mode === 'perspective') {
         // set at least to 5 to avoid slow sphere impostor rendering
-        near = Math.max(Math.min(radiusMax, 5), near);
-        far = Math.max(5, far);
+        near = Math.max(Math.min(radiusMax, minNear), near);
+        far = Math.max(minNear, far);
     } else {
         // not too close to 0 as it causes issues with outline rendering
-        near = Math.max(Math.min(radiusMax, 5), near);
-        far = Math.max(5, far);
+        near = Math.max(Math.min(radiusMax, minNear), near);
+        far = Math.max(minNear, far);
     }
 
     if (near === far) {

+ 3 - 3
src/mol-canvas3d/camera/stereo.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -13,8 +13,8 @@ import { Camera, ICamera } from '../camera';
 import { Viewport } from './util';
 
 export const StereoCameraParams = {
-    eyeSeparation: PD.Numeric(0.064, { min: 0.01, max: 0.5, step: 0.001 }),
-    focus: PD.Numeric(10, { min: 1, max: 100, step: 0.01 }),
+    eyeSeparation: PD.Numeric(0.062, { min: 0.02, max: 0.1, step: 0.001 }, { description: 'Distance between left and right camera.' }),
+    focus: PD.Numeric(10, { min: 1, max: 20, step: 0.1 }, { description: 'Apparent object distance.' }),
 };
 export const DefaultStereoCameraProps = PD.getDefaultValues(StereoCameraParams);
 export type StereoCameraProps = PD.Values<typeof StereoCameraParams>

+ 67 - 17
src/mol-canvas3d/canvas3d.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { BehaviorSubject, Subscription } from 'rxjs';
@@ -39,7 +40,10 @@ import { Helper } from './helper/helper';
 import { Passes } from './passes/passes';
 import { shallowEqual } from '../mol-util';
 import { MarkingParams } from './passes/marking';
-import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit } from '../mol-gl/webgl/render-item';
+import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit, GraphicsRenderVariantsDpoit } from '../mol-gl/webgl/render-item';
+import { degToRad, radToDeg } from '../mol-math/misc';
+import { AssetManager } from '../mol-util/assets';
+import { deepClone } from '../mol-util/object';
 
 export const Canvas3DParams = {
     camera: PD.Group({
@@ -49,6 +53,7 @@ export const Canvas3DParams = {
             on: PD.Group(StereoCameraParams),
             off: PD.Group({})
         }, { cycle: true, hideIf: p => p?.mode !== 'perspective' }),
+        fov: PD.Numeric(45, { min: 10, max: 130, step: 1 }, { label: 'Field of View' }),
         manualReset: PD.Boolean(false, { isHidden: true }),
     }, { pivot: 'mode' }),
     cameraFog: PD.MappedStatic('on', {
@@ -60,6 +65,7 @@ export const Canvas3DParams = {
     cameraClipping: PD.Group({
         radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
         far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
+        minNear: PD.Numeric(5, { min: 0.1, max: 10, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
     }, { pivot: 'radius' }),
     viewport: PD.MappedStatic('canvas', {
         canvas: PD.Group({}),
@@ -78,7 +84,9 @@ export const Canvas3DParams = {
     }),
 
     cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
+    sceneRadiusFactor: PD.Numeric(1, { min: 1, max: 10, step: 0.1 }),
     transparentBackground: PD.Boolean(false),
+    dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
 
     multiSample: PD.Group(MultiSampleParams),
     postprocessing: PD.Group(PostprocessingParams),
@@ -106,11 +114,13 @@ interface Canvas3DContext {
     readonly attribs: Readonly<Canvas3DContext.Attribs>
     readonly contextLost: BehaviorSubject<now.Timestamp>
     readonly contextRestored: BehaviorSubject<now.Timestamp>
+    readonly assetManager: AssetManager
     dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => void
 }
 
 namespace Canvas3DContext {
     export const DefaultAttribs = {
+        failIfMajorPerformanceCaveat: false,
         /** true by default to avoid issues with Safari (Jan 2021) */
         antialias: true,
         /** true to support multiple Canvas3D objects with a single context */
@@ -120,14 +130,19 @@ namespace Canvas3DContext {
         /** extra pixels to around target to check in case target is empty */
         pickPadding: 1,
         enableWboit: true,
+        enableDpoit: false,
         preferWebGl1: false
     };
     export type Attribs = typeof DefaultAttribs
 
-    export function fromCanvas(canvas: HTMLCanvasElement, attribs: Partial<Attribs> = {}): Canvas3DContext {
+    export function fromCanvas(canvas: HTMLCanvasElement, assetManager: AssetManager, attribs: Partial<Attribs> = {}): Canvas3DContext {
         const a = { ...DefaultAttribs, ...attribs };
-        const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
+
+        if (a.enableWboit && a.enableDpoit) throw new Error('Multiple transparency methods not allowed.');
+
+        const { failIfMajorPerformanceCaveat, antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
         const gl = getGLContext(canvas, {
+            failIfMajorPerformanceCaveat,
             antialias,
             preserveDrawingBuffer,
             alpha: true, // the renderer requires an alpha channel
@@ -139,7 +154,7 @@ namespace Canvas3DContext {
 
         const input = InputObserver.fromElement(canvas, { pixelScale, preventGestures: true });
         const webgl = createContext(gl, { pixelScale });
-        const passes = new Passes(webgl, attribs);
+        const passes = new Passes(webgl, assetManager, a);
 
         if (isDebugMode) {
             const loseContextExt = gl.getExtension('WEBGL_lose_context');
@@ -192,6 +207,7 @@ namespace Canvas3DContext {
             attribs: a,
             contextLost,
             contextRestored: webgl.contextRestored,
+            assetManager,
             dispose: (options?: Partial<{ doNotForceWebGLContextLoss: boolean }>) => {
                 input.dispose();
 
@@ -278,8 +294,8 @@ namespace Canvas3D {
     export interface DragEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, pageStart: Vec2, pageEnd: Vec2 }
     export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys, page?: Vec2, position?: Vec3 }
 
-    export function create({ webgl, input, passes, attribs }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
-        const p: Canvas3DProps = { ...DefaultCanvas3DParams, ...props };
+    export function create({ webgl, input, passes, attribs, assetManager }: Canvas3DContext, props: Partial<Canvas3DProps> = {}): Canvas3D {
+        const p: Canvas3DProps = { ...deepClone(DefaultCanvas3DParams), ...deepClone(props) };
 
         const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>();
         const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>();
@@ -296,14 +312,19 @@ namespace Canvas3D {
         let width = 128;
         let height = 128;
         updateViewport();
+        const scene = Scene.create(webgl, passes.draw.dpoitEnabled ? GraphicsRenderVariantsDpoit : (passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended));
 
-        const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
+        function getSceneRadius() {
+            return scene.boundingSphere.radius * p.sceneRadiusFactor;
+        }
 
         const camera = new Camera({
             position: Vec3.create(0, 0, 100),
             mode: p.camera.mode,
             fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
-            clipFar: p.cameraClipping.far
+            clipFar: p.cameraClipping.far,
+            minNear: p.cameraClipping.minNear,
+            fov: degToRad(p.camera.fov),
         }, { x, y, width, height }, { pixelScale: attribs.pixelScale });
         const stereoCamera = new StereoCamera(camera, p.camera.stereo.params);
 
@@ -315,6 +336,10 @@ namespace Canvas3D {
         const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction);
         const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
 
+        passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
+            if (changed) requestDraw();
+        });
+
         let cameraResetRequested = false;
         let nextCameraResetDuration: number | undefined = void 0;
         let nextCameraResetSnapshot: Camera.SnapshotProvider | undefined = void 0;
@@ -395,7 +420,7 @@ namespace Canvas3D {
                 y > gl.drawingBufferHeight || y + height < 0
             ) return false;
 
-            const markingUpdated = resolveMarking();
+            const markingUpdated = resolveMarking() && (renderer.props.colorMarker || p.marking.enabled);
 
             let didRender = false;
             controls.update(currentTime);
@@ -523,7 +548,7 @@ namespace Canvas3D {
                 const focus = camera.getFocus(center, radius);
                 const next = typeof nextCameraResetSnapshot === 'function' ? nextCameraResetSnapshot(scene, camera) : nextCameraResetSnapshot;
                 const snapshot = next ? { ...focus, ...next } : focus;
-                camera.setState({ ...snapshot, radiusMax: scene.boundingSphere.radius }, duration);
+                camera.setState({ ...snapshot, radiusMax: getSceneRadius() }, duration);
             }
 
             nextCameraResetDuration = void 0;
@@ -574,7 +599,7 @@ namespace Canvas3D {
             }
             if (oldBoundingSphereVisible.radius === 0) nextCameraResetDuration = 0;
 
-            camera.setState({ radiusMax: scene.boundingSphere.radius }, 0);
+            if (!p.camera.manualReset) camera.setState({ radiusMax: getSceneRadius() }, 0);
             reprCount.next(reprRenderObjects.size);
             if (isDebugMode) consoleStats();
 
@@ -650,7 +675,7 @@ namespace Canvas3D {
 
         function getProps(): Canvas3DProps {
             const radius = scene.boundingSphere.radius > 0
-                ? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
+                ? 100 - Math.round((camera.transition.target.radius / getSceneRadius()) * 100)
                 : 0;
 
             return {
@@ -658,14 +683,17 @@ namespace Canvas3D {
                     mode: camera.state.mode,
                     helper: { ...helper.camera.props },
                     stereo: { ...p.camera.stereo },
+                    fov: Math.round(radToDeg(camera.state.fov)),
                     manualReset: !!p.camera.manualReset
                 },
                 cameraFog: camera.state.fog > 0
                     ? { name: 'on' as const, params: { intensity: camera.state.fog } }
                     : { name: 'off' as const, params: {} },
-                cameraClipping: { far: camera.state.clipFar, radius },
+                cameraClipping: { far: camera.state.clipFar, radius, minNear: camera.state.minNear },
                 cameraResetDurationMs: p.cameraResetDurationMs,
+                sceneRadiusFactor: p.sceneRadiusFactor,
                 transparentBackground: p.transparentBackground,
+                dpoitIterations: p.dpoitIterations,
                 viewport: p.viewport,
 
                 postprocessing: { ...p.postprocessing },
@@ -767,10 +795,19 @@ namespace Canvas3D {
                     ? produce(getProps(), properties as any)
                     : properties;
 
+                if (props.sceneRadiusFactor !== undefined) {
+                    p.sceneRadiusFactor = props.sceneRadiusFactor;
+                    camera.setState({ radiusMax: getSceneRadius() }, 0);
+                }
+
                 const cameraState: Partial<Camera.Snapshot> = Object.create(null);
                 if (props.camera && props.camera.mode !== undefined && props.camera.mode !== camera.state.mode) {
                     cameraState.mode = props.camera.mode;
                 }
+                const oldFov = Math.round(radToDeg(camera.state.fov));
+                if (props.camera && props.camera.fov !== undefined && props.camera.fov !== oldFov) {
+                    cameraState.fov = degToRad(props.camera.fov);
+                }
                 if (props.cameraFog !== undefined && props.cameraFog.params) {
                     const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0;
                     if (newFog !== camera.state.fog) cameraState.fog = newFog;
@@ -779,8 +816,11 @@ namespace Canvas3D {
                     if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
                         cameraState.clipFar = props.cameraClipping.far;
                     }
+                    if (props.cameraClipping.minNear !== undefined && props.cameraClipping.minNear !== camera.state.minNear) {
+                        cameraState.minNear = props.cameraClipping.minNear;
+                    }
                     if (props.cameraClipping.radius !== undefined) {
-                        const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius);
+                        const radius = (getSceneRadius() / 100) * (100 - props.cameraClipping.radius);
                         if (radius > 0 && radius !== cameraState.radius) {
                             // if radius = 0, NaNs happen
                             cameraState.radius = Math.max(radius, 0.01);
@@ -791,9 +831,13 @@ namespace Canvas3D {
 
                 if (props.camera?.helper) helper.camera.setProps(props.camera.helper);
                 if (props.camera?.manualReset !== undefined) p.camera.manualReset = props.camera.manualReset;
-                if (props.camera?.stereo !== undefined) Object.assign(p.camera.stereo, props.camera.stereo);
+                if (props.camera?.stereo !== undefined) {
+                    Object.assign(p.camera.stereo, props.camera.stereo);
+                    stereoCamera.setProps(p.camera.stereo.params);
+                }
                 if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs;
                 if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground;
+                if (props.dpoitIterations !== undefined) p.dpoitIterations = props.dpoitIterations;
                 if (props.viewport !== undefined) {
                     const doNotUpdate = p.viewport === props.viewport ||
                         (p.viewport.name === props.viewport.name && shallowEqual(p.viewport.params, props.viewport.params));
@@ -805,6 +849,12 @@ namespace Canvas3D {
                     }
                 }
 
+                if (props.postprocessing?.background) {
+                    Object.assign(p.postprocessing.background, props.postprocessing.background);
+                    passes.draw.postprocessing.background.update(camera, p.postprocessing.background, changed => {
+                        if (changed && !doNotRequestDraw) requestDraw();
+                    });
+                }
                 if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing);
                 if (props.marking) Object.assign(p.marking, props.marking);
                 if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
@@ -823,7 +873,7 @@ namespace Canvas3D {
                 }
             },
             getImagePass: (props: Partial<ImageProps> = {}) => {
-                return new ImagePass(webgl, renderer, scene, camera, helper, passes.draw.wboitEnabled, props);
+                return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.wboitEnabled, passes.draw.dpoitEnabled, props);
             },
             getRenderObjects(): GraphicsRenderObject[] {
                 const renderObjects: GraphicsRenderObject[] = [];
@@ -888,4 +938,4 @@ namespace Canvas3D {
             Viewport.set(controls.viewport, x, y, width, height);
         }
     }
-}
+}

+ 467 - 0
src/mol-canvas3d/passes/background.ts

@@ -0,0 +1,467 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { QuadPositions, } from '../../mol-gl/compute/util';
+import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
+import { AttributeSpec, DefineSpec, TextureSpec, UniformSpec, Values, ValueSpec } from '../../mol-gl/renderable/schema';
+import { ShaderCode } from '../../mol-gl/shader-code';
+import { background_frag } from '../../mol-gl/shader/background.frag';
+import { background_vert } from '../../mol-gl/shader/background.vert';
+import { WebGLContext } from '../../mol-gl/webgl/context';
+import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
+import { createNullTexture, CubeFaces, Texture } from '../../mol-gl/webgl/texture';
+import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
+import { ValueCell } from '../../mol-util/value-cell';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { isTimingMode } from '../../mol-util/debug';
+import { Camera, ICamera } from '../camera';
+import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
+import { Vec2 } from '../../mol-math/linear-algebra/3d/vec2';
+import { Color } from '../../mol-util/color';
+import { Asset, AssetManager } from '../../mol-util/assets';
+import { Vec4 } from '../../mol-math/linear-algebra/3d/vec4';
+
+const SharedParams = {
+    opacity: PD.Numeric(1, { min: 0.0, max: 1.0, step: 0.01 }),
+    saturation: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
+    lightness: PD.Numeric(0, { min: -1, max: 1, step: 0.01 }),
+};
+
+const SkyboxParams = {
+    faces: PD.MappedStatic('urls', {
+        urls: PD.Group({
+            nx: PD.Text('', { label: 'Negative X / Left' }),
+            ny: PD.Text('', { label: 'Negative Y / Bottom' }),
+            nz: PD.Text('', { label: 'Negative Z / Back' }),
+            px: PD.Text('', { label: 'Positive X / Right' }),
+            py: PD.Text('', { label: 'Positive Y / Top' }),
+            pz: PD.Text('', { label: 'Positive Z / Front' }),
+        }, { isExpanded: true, label: 'URLs' }),
+        files: PD.Group({
+            nx: PD.File({ label: 'Negative X / Left', accept: 'image/*' }),
+            ny: PD.File({ label: 'Negative Y / Bottom', accept: 'image/*' }),
+            nz: PD.File({ label: 'Negative Z / Back', accept: 'image/*' }),
+            px: PD.File({ label: 'Positive X / Right', accept: 'image/*' }),
+            py: PD.File({ label: 'Positive Y / Top', accept: 'image/*' }),
+            pz: PD.File({ label: 'Positive Z / Front', accept: 'image/*' }),
+        }, { isExpanded: true, label: 'Files' }),
+    }),
+    blur: PD.Numeric(0, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'Note, this only works in WebGL2 or when "EXT_shader_texture_lod" is available.' }),
+    ...SharedParams,
+};
+type SkyboxProps = PD.Values<typeof SkyboxParams>
+
+const ImageParams = {
+    source: PD.MappedStatic('url', {
+        url: PD.Text(''),
+        file: PD.File({ accept: 'image/*' }),
+    }),
+    ...SharedParams,
+    coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
+};
+type ImageProps = PD.Values<typeof ImageParams>
+
+const HorizontalGradientParams = {
+    topColor: PD.Color(Color(0xDDDDDD)),
+    bottomColor: PD.Color(Color(0xEEEEEE)),
+    ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
+    coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
+};
+
+const RadialGradientParams = {
+    centerColor: PD.Color(Color(0xDDDDDD)),
+    edgeColor: PD.Color(Color(0xEEEEEE)),
+    ratio: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
+    coverage: PD.Select('viewport', PD.arrayToOptions(['viewport', 'canvas'])),
+};
+
+export const BackgroundParams = {
+    variant: PD.MappedStatic('off', {
+        off: PD.EmptyGroup(),
+        skybox: PD.Group(SkyboxParams, { isExpanded: true }),
+        image: PD.Group(ImageParams, { isExpanded: true }),
+        horizontalGradient: PD.Group(HorizontalGradientParams, { isExpanded: true }),
+        radialGradient: PD.Group(RadialGradientParams, { isExpanded: true }),
+    }, { label: 'Environment' }),
+};
+export type BackgroundProps = PD.Values<typeof BackgroundParams>
+
+export class BackgroundPass {
+    private renderable: BackgroundRenderable;
+
+    private skybox: {
+        texture: Texture
+        props: SkyboxProps
+        assets: Asset[]
+        loaded: boolean
+    } | undefined;
+
+    private image: {
+        texture: Texture
+        props: ImageProps
+        asset: Asset
+        loaded: boolean
+    } | undefined;
+
+    private readonly camera = new Camera();
+    private readonly target = Vec3();
+    private readonly position = Vec3();
+    private readonly dir = Vec3();
+
+    readonly texture: Texture;
+
+    constructor(private readonly webgl: WebGLContext, private readonly assetManager: AssetManager, width: number, height: number) {
+        this.renderable = getBackgroundRenderable(webgl, width, height);
+    }
+
+    setSize(width: number, height: number) {
+        const [w, h] = this.renderable.values.uTexSize.ref.value;
+
+        if (width !== w || height !== h) {
+            ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
+        }
+    }
+
+    private clearSkybox() {
+        if (this.skybox !== undefined) {
+            this.skybox.texture.destroy();
+            this.skybox.assets.forEach(a => this.assetManager.release(a));
+            this.skybox = undefined;
+        }
+    }
+
+    private updateSkybox(camera: ICamera, props: SkyboxProps, onload?: (changed: boolean) => void) {
+        const tf = this.skybox?.props.faces;
+        const f = props.faces.params;
+        if (!f.nx || !f.ny || !f.nz || !f.px || !f.py || !f.pz) {
+            this.clearSkybox();
+            onload?.(false);
+            return;
+        }
+        if (!this.skybox || !tf || !areSkyboxTexturePropsEqual(props.faces, this.skybox.props.faces)) {
+            this.clearSkybox();
+            const { texture, assets } = getSkyboxTexture(this.webgl, this.assetManager, props.faces, errored => {
+                if (this.skybox) this.skybox.loaded = !errored;
+                onload?.(true);
+            });
+            this.skybox = { texture, props: { ...props }, assets, loaded: false };
+            ValueCell.update(this.renderable.values.tSkybox, texture);
+            this.renderable.update();
+        } else {
+            onload?.(false);
+        }
+        if (!this.skybox) return;
+
+        let cam = camera;
+        if (camera.state.mode === 'orthographic') {
+            this.camera.setState({ ...camera.state, mode: 'perspective' });
+            this.camera.update();
+            cam = this.camera;
+        }
+
+        const m = this.renderable.values.uViewDirectionProjectionInverse.ref.value;
+        Vec3.sub(this.dir, cam.state.position, cam.state.target);
+        Vec3.setMagnitude(this.dir, this.dir, 0.1);
+        Vec3.copy(this.position, this.dir);
+        Mat4.lookAt(m, this.position, this.target, cam.state.up);
+        Mat4.mul(m, cam.projection, m);
+        Mat4.invert(m, m);
+        ValueCell.update(this.renderable.values.uViewDirectionProjectionInverse, m);
+
+        ValueCell.updateIfChanged(this.renderable.values.uBlur, props.blur);
+        ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
+        ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
+        ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
+        ValueCell.updateIfChanged(this.renderable.values.dVariant, 'skybox');
+        this.renderable.update();
+    }
+
+    private clearImage() {
+        if (this.image !== undefined) {
+            this.image.texture.destroy();
+            this.assetManager.release(this.image.asset);
+            this.image = undefined;
+        }
+    }
+
+    private updateImage(props: ImageProps, onload?: (loaded: boolean) => void) {
+        if (!props.source.params) {
+            this.clearImage();
+            onload?.(false);
+            return;
+        }
+        if (!this.image || !this.image.props.source.params || !areImageTexturePropsEqual(props.source, this.image.props.source)) {
+            this.clearImage();
+            const { texture, asset } = getImageTexture(this.webgl, this.assetManager, props.source, errored => {
+                if (this.image) this.image.loaded = !errored;
+                onload?.(true);
+            });
+            this.image = { texture, props: { ...props }, asset, loaded: false };
+            ValueCell.update(this.renderable.values.tImage, texture);
+            this.renderable.update();
+        } else {
+            onload?.(false);
+        }
+        if (!this.image) return;
+
+        ValueCell.updateIfChanged(this.renderable.values.uOpacity, props.opacity);
+        ValueCell.updateIfChanged(this.renderable.values.uSaturation, props.saturation);
+        ValueCell.updateIfChanged(this.renderable.values.uLightness, props.lightness);
+        ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, props.coverage === 'viewport' ? true : false);
+        ValueCell.updateIfChanged(this.renderable.values.dVariant, 'image');
+        this.renderable.update();
+    }
+
+    private updateImageScaling() {
+        const v = this.renderable.values;
+        const [w, h] = v.uTexSize.ref.value;
+        const iw = this.image?.texture.getWidth() || 0;
+        const ih = this.image?.texture.getHeight() || 0;
+        const r = w / h;
+        const ir = iw / ih;
+        // responsive scaling with offset
+        if (r < ir) {
+            ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, iw * h / ih, h));
+        } else {
+            ValueCell.update(v.uImageScale, Vec2.set(v.uImageScale.ref.value, w, ih * w / iw));
+        }
+        const [rw, rh] = v.uImageScale.ref.value;
+        const sr = rw / rh;
+        if (sr > r) {
+            ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, (1 - r / sr) / 2, 0));
+        } else {
+            ValueCell.update(v.uImageOffset, Vec2.set(v.uImageOffset.ref.value, 0, (1 - sr / r) / 2));
+        }
+    }
+
+    private updateGradient(colorA: Color, colorB: Color, ratio: number, variant: 'horizontalGradient' | 'radialGradient', viewportAdjusted: boolean) {
+        ValueCell.update(this.renderable.values.uGradientColorA, Color.toVec3Normalized(this.renderable.values.uGradientColorA.ref.value, colorA));
+        ValueCell.update(this.renderable.values.uGradientColorB, Color.toVec3Normalized(this.renderable.values.uGradientColorB.ref.value, colorB));
+        ValueCell.updateIfChanged(this.renderable.values.uGradientRatio, ratio);
+        ValueCell.updateIfChanged(this.renderable.values.uViewportAdjusted, viewportAdjusted);
+        ValueCell.updateIfChanged(this.renderable.values.dVariant, variant);
+        this.renderable.update();
+    }
+
+    update(camera: ICamera, props: BackgroundProps, onload?: (changed: boolean) => void) {
+        if (props.variant.name === 'off') {
+            this.clearSkybox();
+            this.clearImage();
+            onload?.(false);
+            return;
+        } else if (props.variant.name === 'skybox') {
+            this.clearImage();
+            this.updateSkybox(camera, props.variant.params, onload);
+        } else if (props.variant.name === 'image') {
+            this.clearSkybox();
+            this.updateImage(props.variant.params, onload);
+        } else if (props.variant.name === 'horizontalGradient') {
+            this.clearSkybox();
+            this.clearImage();
+            this.updateGradient(props.variant.params.topColor, props.variant.params.bottomColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
+            onload?.(false);
+        } else if (props.variant.name === 'radialGradient') {
+            this.clearSkybox();
+            this.clearImage();
+            this.updateGradient(props.variant.params.centerColor, props.variant.params.edgeColor, props.variant.params.ratio, props.variant.name, props.variant.params.coverage === 'viewport' ? true : false);
+            onload?.(false);
+        }
+
+        const { x, y, width, height } = camera.viewport;
+        ValueCell.update(this.renderable.values.uViewport, Vec4.set(this.renderable.values.uViewport.ref.value, x, y, width, height));
+    }
+
+    isEnabled(props: BackgroundProps) {
+        return !!(
+            (this.skybox && this.skybox.loaded) ||
+            (this.image && this.image.loaded) ||
+            props.variant.name === 'horizontalGradient' ||
+            props.variant.name === 'radialGradient'
+        );
+    }
+
+    private isReady() {
+        return !!(
+            (this.skybox && this.skybox.loaded) ||
+            (this.image && this.image.loaded) ||
+            this.renderable.values.dVariant.ref.value === 'horizontalGradient' ||
+            this.renderable.values.dVariant.ref.value === 'radialGradient'
+        );
+    }
+
+    render() {
+        if (!this.isReady()) return;
+
+        if (this.renderable.values.dVariant.ref.value === 'image') {
+            this.updateImageScaling();
+        }
+
+        if (isTimingMode) this.webgl.timer.mark('BackgroundPass.render');
+        this.renderable.render();
+        if (isTimingMode) this.webgl.timer.markEnd('BackgroundPass.render');
+    }
+
+    dispose() {
+        this.clearSkybox();
+        this.clearImage();
+    }
+}
+
+//
+
+const SkyboxName = 'background-skybox';
+
+type CubeAssets = { [k in keyof CubeFaces]: Asset };
+
+function getCubeAssets(assetManager: AssetManager, faces: SkyboxProps['faces']): CubeAssets {
+    if (faces.name === 'urls') {
+        return {
+            nx: Asset.getUrlAsset(assetManager, faces.params.nx),
+            ny: Asset.getUrlAsset(assetManager, faces.params.ny),
+            nz: Asset.getUrlAsset(assetManager, faces.params.nz),
+            px: Asset.getUrlAsset(assetManager, faces.params.px),
+            py: Asset.getUrlAsset(assetManager, faces.params.py),
+            pz: Asset.getUrlAsset(assetManager, faces.params.pz),
+        };
+    } else {
+        return {
+            nx: faces.params.nx!,
+            ny: faces.params.ny!,
+            nz: faces.params.nz!,
+            px: faces.params.px!,
+            py: faces.params.py!,
+            pz: faces.params.pz!,
+        };
+    }
+}
+
+function getCubeFaces(assetManager: AssetManager, cubeAssets: CubeAssets): CubeFaces {
+    const resolve = (asset: Asset) => {
+        return assetManager.resolve(asset, 'binary').run().then(a => new Blob([a.data]));
+    };
+
+    return {
+        nx: resolve(cubeAssets.nx),
+        ny: resolve(cubeAssets.ny),
+        nz: resolve(cubeAssets.nz),
+        px: resolve(cubeAssets.px),
+        py: resolve(cubeAssets.py),
+        pz: resolve(cubeAssets.pz),
+    };
+}
+
+function getSkyboxHash(faces: SkyboxProps['faces']) {
+    if (faces.name === 'urls') {
+        return `${SkyboxName}_${faces.params.nx}|${faces.params.ny}|${faces.params.nz}|${faces.params.px}|${faces.params.py}|${faces.params.pz}`;
+    } else {
+        return `${SkyboxName}_${faces.params.nx?.id}|${faces.params.ny?.id}|${faces.params.nz?.id}|${faces.params.px?.id}|${faces.params.py?.id}|${faces.params.pz?.id}`;
+    }
+}
+
+function areSkyboxTexturePropsEqual(facesA: SkyboxProps['faces'], facesB: SkyboxProps['faces']) {
+    return getSkyboxHash(facesA) === getSkyboxHash(facesB);
+}
+
+function getSkyboxTexture(ctx: WebGLContext, assetManager: AssetManager, faces: SkyboxProps['faces'], onload?: (errored?: boolean) => void): { texture: Texture, assets: Asset[] } {
+    const cubeAssets = getCubeAssets(assetManager, faces);
+    const cubeFaces = getCubeFaces(assetManager, cubeAssets);
+    const assets = [cubeAssets.nx, cubeAssets.ny, cubeAssets.nz, cubeAssets.px, cubeAssets.py, cubeAssets.pz];
+    const texture = ctx.resources.cubeTexture(cubeFaces, true, onload);
+    return { texture, assets };
+}
+
+//
+
+const ImageName = 'background-image';
+
+function getImageHash(source: ImageProps['source']) {
+    if (source.name === 'url') {
+        return `${ImageName}_${source.params}`;
+    } else {
+        return `${ImageName}_${source.params?.id}`;
+    }
+}
+
+function areImageTexturePropsEqual(sourceA: ImageProps['source'], sourceB: ImageProps['source']) {
+    return getImageHash(sourceA) === getImageHash(sourceB);
+}
+
+function getImageTexture(ctx: WebGLContext, assetManager: AssetManager, source: ImageProps['source'], onload?: (errored?: boolean) => void): { texture: Texture, asset: Asset } {
+    const texture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
+    const img = new Image();
+    img.onload = () => {
+        texture.load(img);
+        onload?.();
+    };
+    img.onerror = () => {
+        onload?.(true);
+    };
+    const asset = source.name === 'url'
+        ? Asset.getUrlAsset(assetManager, source.params)
+        : source.params!;
+    assetManager.resolve(asset, 'binary').run().then(a => {
+        const blob = new Blob([a.data]);
+        img.src = URL.createObjectURL(blob);
+    });
+    return { texture, asset };
+}
+
+//
+
+const BackgroundSchema = {
+    drawCount: ValueSpec('number'),
+    instanceCount: ValueSpec('number'),
+    aPosition: AttributeSpec('float32', 2, 0),
+    tSkybox: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
+    tImage: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
+    uImageScale: UniformSpec('v2'),
+    uImageOffset: UniformSpec('v2'),
+    uTexSize: UniformSpec('v2'),
+    uViewport: UniformSpec('v4'),
+    uViewportAdjusted: UniformSpec('b'),
+    uViewDirectionProjectionInverse: UniformSpec('m4'),
+    uGradientColorA: UniformSpec('v3'),
+    uGradientColorB: UniformSpec('v3'),
+    uGradientRatio: UniformSpec('f'),
+    uBlur: UniformSpec('f'),
+    uOpacity: UniformSpec('f'),
+    uSaturation: UniformSpec('f'),
+    uLightness: UniformSpec('f'),
+    dVariant: DefineSpec('string', ['skybox', 'image', 'verticalGradient', 'horizontalGradient', 'radialGradient']),
+};
+const SkyboxShaderCode = ShaderCode('background', background_vert, background_frag, {
+    shaderTextureLod: 'optional'
+});
+type BackgroundRenderable = ComputeRenderable<Values<typeof BackgroundSchema>>
+
+function getBackgroundRenderable(ctx: WebGLContext, width: number, height: number): BackgroundRenderable {
+    const values: Values<typeof BackgroundSchema> = {
+        drawCount: ValueCell.create(6),
+        instanceCount: ValueCell.create(1),
+        aPosition: ValueCell.create(QuadPositions),
+        tSkybox: ValueCell.create(createNullTexture()),
+        tImage: ValueCell.create(createNullTexture()),
+        uImageScale: ValueCell.create(Vec2()),
+        uImageOffset: ValueCell.create(Vec2()),
+        uTexSize: ValueCell.create(Vec2.create(width, height)),
+        uViewport: ValueCell.create(Vec4()),
+        uViewportAdjusted: ValueCell.create(true),
+        uViewDirectionProjectionInverse: ValueCell.create(Mat4()),
+        uGradientColorA: ValueCell.create(Vec3()),
+        uGradientColorB: ValueCell.create(Vec3()),
+        uGradientRatio: ValueCell.create(0.5),
+        uBlur: ValueCell.create(0),
+        uOpacity: ValueCell.create(1),
+        uSaturation: ValueCell.create(0),
+        uLightness: ValueCell.create(0),
+        dVariant: ValueCell.create('skybox'),
+    };
+
+    const schema = { ...BackgroundSchema };
+    const renderItem = createComputeRenderItem(ctx, 'triangles', SkyboxShaderCode, schema, values);
+
+    return createComputeRenderable(renderItem, values);
+}

+ 309 - 0
src/mol-canvas3d/passes/dpoit.ts

@@ -0,0 +1,309 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Gianluca Tomasello <giagitom@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ *
+ * Adapted from https://github.com/tsherif/webgl2examples, The MIT License, Copyright © 2017 Tarek Sherif, Shuai Shao
+ */
+
+import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
+import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
+import { TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
+import { ShaderCode } from '../../mol-gl/shader-code';
+import { WebGLContext } from '../../mol-gl/webgl/context';
+import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
+import { Texture } from '../../mol-gl/webgl/texture';
+import { ValueCell } from '../../mol-util';
+import { quad_vert } from '../../mol-gl/shader/quad.vert';
+import { evaluateDpoit_frag } from '../../mol-gl/shader/evaluate-dpoit.frag';
+import { blendBackDpoit_frag } from '../../mol-gl/shader/blend-back-dpoit.frag';
+import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
+import { Vec2 } from '../../mol-math/linear-algebra';
+import { isDebugMode, isTimingMode } from '../../mol-util/debug';
+import { isWebGL2 } from '../../mol-gl/webgl/compat';
+
+const BlendBackDpoitSchema = {
+    ...QuadSchema,
+    tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
+    uTexSize: UniformSpec('v2'),
+};
+const BlendBackDpoitShaderCode = ShaderCode('blend-back-dpoit', quad_vert, blendBackDpoit_frag);
+type BlendBackDpoitRenderable = ComputeRenderable<Values<typeof BlendBackDpoitSchema>>
+
+function getBlendBackDpoitRenderable(ctx: WebGLContext, dopitBlendBackTexture: Texture): BlendBackDpoitRenderable {
+    const values: Values<typeof BlendBackDpoitSchema> = {
+        ...QuadValues,
+        tDpoitBackColor: ValueCell.create(dopitBlendBackTexture),
+        uTexSize: ValueCell.create(Vec2.create(dopitBlendBackTexture.getWidth(), dopitBlendBackTexture.getHeight())),
+    };
+
+    const schema = { ...BlendBackDpoitSchema };
+    const renderItem = createComputeRenderItem(ctx, 'triangles', BlendBackDpoitShaderCode, schema, values);
+
+    return createComputeRenderable(renderItem, values);
+}
+
+const EvaluateDpoitSchema = {
+    ...QuadSchema,
+    tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
+    uTexSize: UniformSpec('v2'),
+};
+const EvaluateDpoitShaderCode = ShaderCode('evaluate-dpoit', quad_vert, evaluateDpoit_frag);
+type EvaluateDpoitRenderable = ComputeRenderable<Values<typeof EvaluateDpoitSchema>>
+
+function getEvaluateDpoitRenderable(ctx: WebGLContext, dpoitFrontColorTexture: Texture): EvaluateDpoitRenderable {
+    const values: Values<typeof EvaluateDpoitSchema> = {
+        ...QuadValues,
+        tDpoitFrontColor: ValueCell.create(dpoitFrontColorTexture),
+        uTexSize: ValueCell.create(Vec2.create(dpoitFrontColorTexture.getWidth(), dpoitFrontColorTexture.getHeight())),
+    };
+
+    const schema = { ...EvaluateDpoitSchema };
+    const renderItem = createComputeRenderItem(ctx, 'triangles', EvaluateDpoitShaderCode, schema, values);
+
+    return createComputeRenderable(renderItem, values);
+}
+
+export class DpoitPass {
+    private readonly DEPTH_CLEAR_VALUE = -99999.0; // NOTE same constant is set in shaders
+    private readonly MAX_DEPTH = 1.0;
+    private readonly MIN_DEPTH = 0.0;
+
+    private passCount = 0;
+    private writeId: number;
+    private readId: number;
+
+    private readonly blendBackRenderable: BlendBackDpoitRenderable;
+    private readonly renderable: EvaluateDpoitRenderable;
+
+    private readonly depthFramebuffers: Framebuffer[];
+    private readonly colorFramebuffers: Framebuffer[];
+
+    private readonly depthTextures: Texture[];
+    private readonly colorFrontTextures: Texture[];
+    private readonly colorBackTextures: Texture[];
+
+    private _supported = false;
+    get supported() {
+        return this._supported;
+    }
+
+    bind() {
+        const { state, gl, extensions: { blendMinMax } } = this.webgl;
+
+        // initialize
+        this.passCount = 0;
+
+        this.depthFramebuffers[0].bind();
+        state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.depthFramebuffers[1].bind();
+        state.clearColor(-this.MIN_DEPTH, this.MAX_DEPTH, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.colorFramebuffers[0].bind();
+        state.clearColor(0, 0, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.colorFramebuffers[1].bind();
+        state.clearColor(0, 0, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.depthFramebuffers[0].bind();
+        state.blendEquation(blendMinMax!.MAX);
+        state.depthMask(false);
+
+        return {
+            depth: this.depthTextures[1],
+            frontColor: this.colorFrontTextures[1],
+            backColor: this.colorBackTextures[1]
+        };
+    }
+
+    bindDualDepthPeeling() {
+        const { state, gl, extensions: { blendMinMax } } = this.webgl;
+
+        this.readId = this.passCount % 2;
+        this.writeId = 1 - this.readId; // ping-pong: 0 or 1
+
+        this.passCount += 1; // increment for next pass
+
+        this.depthFramebuffers[this.writeId].bind();
+        state.clearColor(this.DEPTH_CLEAR_VALUE, this.DEPTH_CLEAR_VALUE, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.colorFramebuffers[this.writeId].bind();
+        state.clearColor(0, 0, 0, 0);
+        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.depthFramebuffers[this.writeId].bind();
+        state.blendEquation(blendMinMax!.MAX);
+        state.depthMask(false);
+
+        return {
+            depth: this.depthTextures[this.readId],
+            frontColor: this.colorFrontTextures[this.readId],
+            backColor: this.colorBackTextures[this.readId]
+        };
+    }
+
+    renderBlendBack() {
+        if (isTimingMode) this.webgl.timer.mark('DpoitPass.renderBlendBack');
+        const { state, gl } = this.webgl;
+
+        state.blendEquation(gl.FUNC_ADD);
+        state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+
+        ValueCell.update(this.blendBackRenderable.values.tDpoitBackColor, this.colorBackTextures[this.writeId]);
+
+        this.blendBackRenderable.update();
+        this.blendBackRenderable.render();
+        if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.renderBlendBack');
+    }
+
+    render() {
+        if (isTimingMode) this.webgl.timer.mark('DpoitPass.render');
+        const { state, gl } = this.webgl;
+
+        state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+
+        ValueCell.update(this.renderable.values.tDpoitFrontColor, this.colorFrontTextures[this.writeId]);
+
+        this.renderable.update();
+        this.renderable.render();
+        if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.render');
+    }
+
+    setSize(width: number, height: number) {
+        const [w, h] = this.renderable.values.uTexSize.ref.value;
+        if (width !== w || height !== h) {
+            for (let i = 0; i < 2; i++) {
+                this.depthTextures[i].define(width, height);
+                this.colorFrontTextures[i].define(width, height);
+                this.colorBackTextures[i].define(width, height);
+            }
+            ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
+            ValueCell.update(this.blendBackRenderable.values.uTexSize, Vec2.set(this.blendBackRenderable.values.uTexSize.ref.value, width, height));
+        }
+    }
+
+    reset() {
+        if (this._supported) this._init();
+    }
+
+    private _init() {
+        const { extensions: { drawBuffers } } = this.webgl;
+        for (let i = 0; i < 2; i++) {
+            // depth
+            this.depthFramebuffers[i].bind();
+            drawBuffers!.drawBuffers([
+                drawBuffers!.COLOR_ATTACHMENT0,
+                drawBuffers!.COLOR_ATTACHMENT1,
+                drawBuffers!.COLOR_ATTACHMENT2
+            ]);
+
+            this.colorFrontTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color0');
+            this.colorBackTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color1');
+            this.depthTextures[i].attachFramebuffer(this.depthFramebuffers[i], 'color2');
+
+            // color
+            this.colorFramebuffers[i].bind();
+            drawBuffers!.drawBuffers([
+                drawBuffers!.COLOR_ATTACHMENT0,
+                drawBuffers!.COLOR_ATTACHMENT1
+            ]);
+
+            this.colorFrontTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color0');
+            this.colorBackTextures[i].attachFramebuffer(this.colorFramebuffers[i], 'color1');
+        }
+    }
+
+    static isSupported(webgl: WebGLContext) {
+        const { extensions: { drawBuffers, textureFloat, colorBufferFloat, blendMinMax } } = webgl;
+        if (!textureFloat || !colorBufferFloat || !drawBuffers || !blendMinMax) {
+            if (isDebugMode) {
+                const missing: string[] = [];
+                if (!textureFloat) missing.push('textureFloat');
+                if (!colorBufferFloat) missing.push('colorBufferFloat');
+                if (!drawBuffers) missing.push('drawBuffers');
+                if (!blendMinMax) missing.push('blendMinMax');
+                console.log(`Missing "${missing.join('", "')}" extensions required for "dpoit"`);
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    constructor(private webgl: WebGLContext, width: number, height: number) {
+        if (!DpoitPass.isSupported(webgl)) return;
+
+        const { resources, extensions: { colorBufferHalfFloat, textureHalfFloat } } = webgl;
+
+        // textures
+
+        if (isWebGL2(webgl.gl)) {
+            this.depthTextures = [
+                resources.texture('image-float32', 'rg', 'float', 'nearest'),
+                resources.texture('image-float32', 'rg', 'float', 'nearest')
+            ];
+
+            this.colorFrontTextures = colorBufferHalfFloat && textureHalfFloat ? [
+                resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
+                resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
+            ] : [
+                resources.texture('image-float32', 'rgba', 'float', 'nearest'),
+                resources.texture('image-float32', 'rgba', 'float', 'nearest')
+            ];
+
+            this.colorBackTextures = colorBufferHalfFloat && textureHalfFloat ? [
+                resources.texture('image-float16', 'rgba', 'fp16', 'nearest'),
+                resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
+            ] : [
+                resources.texture('image-float32', 'rgba', 'float', 'nearest'),
+                resources.texture('image-float32', 'rgba', 'float', 'nearest')
+            ];
+        } else {
+            // in webgl1 drawbuffers must be in the same format for some reason
+
+            this.depthTextures = [
+                resources.texture('image-float32', 'rgba', 'float', 'nearest'),
+                resources.texture('image-float32', 'rgba', 'float', 'nearest')
+            ];
+
+            this.colorFrontTextures = [
+                resources.texture('image-float32', 'rgba', 'float', 'nearest'),
+                resources.texture('image-float32', 'rgba', 'float', 'nearest')
+            ];
+
+            this.colorBackTextures = [
+                resources.texture('image-float32', 'rgba', 'float', 'nearest'),
+                resources.texture('image-float32', 'rgba', 'float', 'nearest')
+            ];
+        }
+
+        this.depthTextures[0].define(width, height);
+        this.depthTextures[1].define(width, height);
+
+        this.colorFrontTextures[0].define(width, height);
+        this.colorFrontTextures[1].define(width, height);
+
+        this.colorBackTextures[0].define(width, height);
+        this.colorBackTextures[1].define(width, height);
+
+        // framebuffers
+
+        this.depthFramebuffers = [resources.framebuffer(), resources.framebuffer()];
+        this.colorFramebuffers = [resources.framebuffer(), resources.framebuffer()];
+
+        // renderables
+
+        this.blendBackRenderable = getBlendBackDpoitRenderable(webgl, this.colorBackTextures[0]);
+        this.renderable = getEvaluateDpoitRenderable(webgl, this.colorFrontTextures[0]);
+
+        this._supported = true;
+        this._init();
+    }
+}

+ 125 - 48
src/mol-canvas3d/passes/draw.ts

@@ -3,6 +3,7 @@
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { WebGLContext } from '../../mol-gl/webgl/context';
@@ -17,15 +18,18 @@ import { Helper } from '../helper/helper';
 
 import { StereoCamera } from '../camera/stereo';
 import { WboitPass } from './wboit';
+import { DpoitPass } from './dpoit';
 import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
 import { MarkingPass, MarkingProps } from './marking';
 import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
 import { isTimingMode } from '../../mol-util/debug';
+import { AssetManager } from '../../mol-util/assets';
 
 type Props = {
-    postprocessing: PostprocessingProps
-    marking: MarkingProps
+    postprocessing: PostprocessingProps;
+    marking: MarkingProps;
     transparentBackground: boolean;
+    dpoitIterations: number;
 }
 
 type RenderContext = {
@@ -50,7 +54,8 @@ export class DrawPass {
     private copyFboTarget: CopyRenderable;
     private copyFboPostprocessing: CopyRenderable;
 
-    private wboit: WboitPass | undefined;
+    private readonly wboit: WboitPass | undefined;
+    private readonly dpoit: DpoitPass | undefined;
     private readonly marking: MarkingPass;
     readonly postprocessing: PostprocessingPass;
     private readonly antialiasing: AntialiasingPass;
@@ -59,11 +64,13 @@ export class DrawPass {
         return !!this.wboit?.supported;
     }
 
-    constructor(private webgl: WebGLContext, width: number, height: number, enableWboit: boolean) {
-        const { extensions, resources, isWebGL2 } = webgl;
+    get dpoitEnabled() {
+        return !!this.dpoit?.supported;
+    }
 
+    constructor(private webgl: WebGLContext, assetManager: AssetManager, width: number, height: number, enableWboit: boolean, enableDpoit: boolean) {
+        const { extensions, resources, isWebGL2 } = webgl;
         this.drawTarget = createNullRenderTarget(webgl.gl);
-
         this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
         this.packedDepth = !extensions.depthTexture;
 
@@ -78,8 +85,9 @@ export class DrawPass {
         }
 
         this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
+        this.dpoit = enableDpoit ? new DpoitPass(webgl, width, height) : undefined;
         this.marking = new MarkingPass(webgl, width, height);
-        this.postprocessing = new PostprocessingPass(webgl, this);
+        this.postprocessing = new PostprocessingPass(webgl, assetManager, this);
         this.antialiasing = new AntialiasingPass(webgl, this);
 
         this.copyFboTarget = createCopyRenderable(webgl, this.colorTarget.texture);
@@ -88,6 +96,7 @@ export class DrawPass {
 
     reset() {
         this.wboit?.reset();
+        this.dpoit?.reset();
     }
 
     setSize(width: number, height: number) {
@@ -111,23 +120,80 @@ export class DrawPass {
                 this.wboit.setSize(width, height);
             }
 
+            if (this.dpoit?.supported) {
+                this.dpoit.setSize(width, height);
+            }
+
             this.marking.setSize(width, height);
             this.postprocessing.setSize(width, height);
             this.antialiasing.setSize(width, height);
         }
     }
 
+    private _renderDpoit(renderer: Renderer, camera: ICamera, scene: Scene, iterations: number, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
+        if (!this.dpoit?.supported) throw new Error('expected dpoit to be supported');
+
+        this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
+        renderer.clear(true);
+
+        // render opaque primitives
+        if (scene.hasOpaque) {
+            renderer.renderDpoitOpaque(scene.primitives, camera, null);
+        }
+
+        if (PostprocessingPass.isEnabled(postprocessingProps)) {
+            if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
+                this.depthTargetTransparent.bind();
+                renderer.clearDepth(true);
+                if (scene.opacityAverage < 1) {
+                    renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
+                }
+            }
+
+            this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
+        }
+
+        this.depthTextureOpaque.detachFramebuffer(this.colorTarget.framebuffer, 'depth');
+
+        // render transparent primitives
+        if (scene.opacityAverage < 1) {
+            const target = PostprocessingPass.isEnabled(postprocessingProps)
+                ? this.postprocessing.target : this.colorTarget;
+
+            const dpoitTextures = this.dpoit.bind();
+            renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
+
+            for (let i = 0; i < iterations; i++) {
+                if (isTimingMode) this.webgl.timer.mark('DpoitPass.layer');
+                const dpoitTextures = this.dpoit.bindDualDepthPeeling();
+                renderer.renderDpoitTransparent(scene.primitives, camera, this.depthTextureOpaque, dpoitTextures);
+
+                target.bind();
+                this.dpoit.renderBlendBack();
+                if (isTimingMode) this.webgl.timer.markEnd('DpoitPass.layer');
+            }
+
+            // evaluate dpoit
+            target.bind();
+            this.dpoit.render();
+        }
+
+        // render transparent volumes
+        if (scene.volumes.renderables.length > 0) {
+            renderer.renderDpoitVolume(scene.volumes, camera, this.depthTextureOpaque);
+        }
+    }
+
     private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
         if (!this.wboit?.supported) throw new Error('expected wboit to be supported');
 
-        this.colorTarget.bind();
+        this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
         renderer.clear(true);
 
         // render opaque primitives
-        this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
-        this.colorTarget.bind();
-        renderer.clearDepth();
-        renderer.renderWboitOpaque(scene.primitives, camera, null);
+        if (scene.hasOpaque) {
+            renderer.renderWboitOpaque(scene.primitives, camera, null);
+        }
 
         if (PostprocessingPass.isEnabled(postprocessingProps)) {
             if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
@@ -165,14 +231,17 @@ export class DrawPass {
         if (toDrawingBuffer) {
             this.drawTarget.bind();
         } else {
-            this.colorTarget.bind();
             if (!this.packedDepth) {
                 this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
+            } else {
+                this.colorTarget.bind();
             }
         }
 
         renderer.clear(true);
-        renderer.renderBlendedOpaque(scene.primitives, camera, null);
+        if (scene.hasOpaque) {
+            renderer.renderBlendedOpaque(scene.primitives, camera, null);
+        }
 
         if (!toDrawingBuffer) {
             // do a depth pass if not rendering to drawing buffer and
@@ -235,7 +304,7 @@ export class DrawPass {
         }
     }
 
-    private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, props: Props) {
+    private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, props: Props) {
         const volumeRendering = scene.volumes.renderables.length > 0;
         const postprocessingEnabled = PostprocessingPass.isEnabled(props.postprocessing);
         const antialiasingEnabled = AntialiasingPass.isEnabled(props.postprocessing);
@@ -245,54 +314,54 @@ export class DrawPass {
         renderer.setViewport(x, y, width, height);
         renderer.update(camera);
 
-        if (props.transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
+        if (transparentBackground && !antialiasingEnabled && toDrawingBuffer) {
             this.drawTarget.bind();
             renderer.clear(false);
         }
 
         if (this.wboitEnabled) {
-            this._renderWboit(renderer, camera, scene, props.transparentBackground, props.postprocessing);
+            this._renderWboit(renderer, camera, scene, transparentBackground, props.postprocessing);
+        } else if (this.dpoitEnabled) {
+            this._renderDpoit(renderer, camera, scene, props.dpoitIterations, transparentBackground, props.postprocessing);
         } else {
-            this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, props.transparentBackground, props.postprocessing);
+            this._renderBlended(renderer, camera, scene, !volumeRendering && !postprocessingEnabled && !antialiasingEnabled && toDrawingBuffer, transparentBackground, props.postprocessing);
         }
 
-        if (postprocessingEnabled) {
-            this.postprocessing.target.bind();
-        } else if (!toDrawingBuffer || volumeRendering || this.wboitEnabled) {
-            this.colorTarget.bind();
-        } else {
-            this.drawTarget.bind();
-        }
-
-        if (markingEnabled) {
-            if (scene.markerAverage > 0) {
-                const markingDepthTest = props.marking.ghostEdgeStrength < 1;
-                if (markingDepthTest && scene.markerAverage !== 1) {
-                    this.marking.depthTarget.bind();
-                    renderer.clear(false, true);
-                    renderer.renderMarkingDepth(scene.primitives, camera, null);
-                }
+        const target = postprocessingEnabled
+            ? this.postprocessing.target
+            : !toDrawingBuffer || volumeRendering || this.wboitEnabled || this.dpoitEnabled
+                ? this.colorTarget
+                : this.drawTarget;
 
-                this.marking.maskTarget.bind();
+        if (markingEnabled && scene.markerAverage > 0) {
+            const markingDepthTest = props.marking.ghostEdgeStrength < 1;
+            if (markingDepthTest && scene.markerAverage !== 1) {
+                this.marking.depthTarget.bind();
                 renderer.clear(false, true);
-                renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
-
-                this.marking.update(props.marking);
-                this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
+                renderer.renderMarkingDepth(scene.primitives, camera, null);
             }
+
+            this.marking.maskTarget.bind();
+            renderer.clear(false, true);
+            renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
+
+            this.marking.update(props.marking);
+            this.marking.render(camera.viewport, target);
+        } else {
+            target.bind();
         }
 
         if (helper.debug.isEnabled) {
             helper.debug.syncVisibility();
-            renderer.renderBlended(helper.debug.scene, camera, null);
+            renderer.renderBlended(helper.debug.scene, camera);
         }
         if (helper.handle.isEnabled) {
-            renderer.renderBlended(helper.handle.scene, camera, null);
+            renderer.renderBlended(helper.handle.scene, camera);
         }
         if (helper.camera.isEnabled) {
             helper.camera.update(camera);
             renderer.update(helper.camera.camera);
-            renderer.renderBlended(helper.camera.scene, helper.camera.camera, null);
+            renderer.renderBlended(helper.camera.scene, helper.camera.camera);
         }
 
         if (antialiasingEnabled) {
@@ -303,7 +372,7 @@ export class DrawPass {
             this.webgl.state.disable(this.webgl.gl.DEPTH_TEST);
             if (postprocessingEnabled) {
                 this.copyFboPostprocessing.render();
-            } else if (volumeRendering || this.wboitEnabled) {
+            } else if (volumeRendering || this.wboitEnabled || this.dpoitEnabled) {
                 this.copyFboTarget.render();
             }
         }
@@ -314,15 +383,23 @@ export class DrawPass {
     render(ctx: RenderContext, props: Props, toDrawingBuffer: boolean) {
         if (isTimingMode) this.webgl.timer.mark('DrawPass.render');
         const { renderer, camera, scene, helper } = ctx;
-        renderer.setTransparentBackground(props.transparentBackground);
+
+        this.postprocessing.setTransparentBackground(props.transparentBackground);
+        const transparentBackground = props.transparentBackground || this.postprocessing.background.isEnabled(props.postprocessing.background);
+
+        renderer.setTransparentBackground(transparentBackground);
         renderer.setDrawingBufferSize(this.colorTarget.getWidth(), this.colorTarget.getHeight());
         renderer.setPixelRatio(this.webgl.pixelRatio);
 
         if (StereoCamera.is(camera)) {
-            this._render(renderer, camera.left, scene, helper, toDrawingBuffer, props);
-            this._render(renderer, camera.right, scene, helper, toDrawingBuffer, props);
+            if (isTimingMode) this.webgl.timer.mark('StereoCamera.left');
+            this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, props);
+            if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.left');
+            if (isTimingMode) this.webgl.timer.mark('StereoCamera.right');
+            this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, props);
+            if (isTimingMode) this.webgl.timer.markEnd('StereoCamera.right');
         } else {
-            this._render(renderer, camera, scene, helper, toDrawingBuffer, props);
+            this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, props);
         }
         if (isTimingMode) this.webgl.timer.markEnd('DrawPass.render');
     }
@@ -335,4 +412,4 @@ export class DrawPass {
         }
         return this.colorTarget;
     }
-}
+}

+ 2 - 2
src/mol-canvas3d/passes/fxaa.ts

@@ -44,8 +44,8 @@ export class FxaaPass {
         state.depthMask(false);
 
         const { x, y, width, height } = viewport;
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
 
         state.clearColor(0, 0, 0, 1);
         gl.clear(gl.COLOR_BUFFER_BIT);

+ 13 - 3
src/mol-canvas3d/passes/image.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -18,9 +18,11 @@ import { PixelData } from '../../mol-util/image';
 import { Helper } from '../helper/helper';
 import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
 import { MarkingParams } from './marking';
+import { AssetManager } from '../../mol-util/assets';
 
 export const ImageParams = {
     transparentBackground: PD.Boolean(false),
+    dpoitIterations: PD.Numeric(2, { min: 1, max: 10, step: 1 }),
     multiSample: PD.Group(MultiSampleParams),
     postprocessing: PD.Group(PostprocessingParams),
     marking: PD.Group(MarkingParams),
@@ -47,10 +49,10 @@ export class ImagePass {
     get width() { return this._width; }
     get height() { return this._height; }
 
-    constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, props: Partial<ImageProps>) {
+    constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, enableWboit: boolean, enableDpoit: boolean, props: Partial<ImageProps>) {
         this.props = { ...PD.getDefaultValues(ImageParams), ...props };
 
-        this.drawPass = new DrawPass(webgl, 128, 128, enableWboit);
+        this.drawPass = new DrawPass(webgl, assetManager, 128, 128, enableWboit, enableDpoit);
         this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
         this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
 
@@ -63,6 +65,14 @@ export class ImagePass {
         this.setSize(1024, 768);
     }
 
+    updateBackground() {
+        return new Promise<void>(resolve => {
+            this.drawPass.postprocessing.background.update(this.camera, this.props.postprocessing.background, () => {
+                resolve();
+            });
+        });
+    }
+
     setSize(width: number, height: number) {
         if (width === this._width && height === this._height) return;
 

+ 4 - 4
src/mol-canvas3d/passes/marking.ts

@@ -64,8 +64,8 @@ export class MarkingPass {
         state.depthMask(false);
 
         const { x, y, width, height } = viewport;
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
 
         state.clearColor(0, 0, 0, 0);
         gl.clear(gl.COLOR_BUFFER_BIT);
@@ -82,8 +82,8 @@ export class MarkingPass {
         state.depthMask(false);
 
         const { x, y, width, height } = viewport;
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
     }
 
     setSize(width: number, height: number) {

+ 11 - 10
src/mol-canvas3d/passes/multi-sample.ts

@@ -61,6 +61,7 @@ type Props = {
     postprocessing: PostprocessingProps
     marking: MarkingProps
     transparentBackground: boolean;
+    dpoitIterations: number;
 }
 
 type RenderContext = {
@@ -176,8 +177,8 @@ export class MultiSamplePass {
             state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
             state.disable(gl.DEPTH_TEST);
             state.depthMask(false);
-            gl.viewport(x, y, width, height);
-            gl.scissor(x, y, width, height);
+            state.viewport(x, y, width, height);
+            state.scissor(x, y, width, height);
             if (i === 0) {
                 state.clearColor(0, 0, 0, 0);
                 gl.clear(gl.COLOR_BUFFER_BIT);
@@ -192,8 +193,8 @@ export class MultiSamplePass {
         compose.update();
 
         this.bindOutputTarget(toDrawingBuffer);
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
 
         state.disable(gl.BLEND);
         compose.render();
@@ -231,8 +232,8 @@ export class MultiSamplePass {
             state.disable(gl.BLEND);
             state.disable(gl.DEPTH_TEST);
             state.depthMask(false);
-            gl.viewport(x, y, width, height);
-            gl.scissor(x, y, width, height);
+            state.viewport(x, y, width, height);
+            state.scissor(x, y, width, height);
             compose.render();
             sampleIndex += 1;
         } else {
@@ -267,8 +268,8 @@ export class MultiSamplePass {
                 state.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE);
                 state.disable(gl.DEPTH_TEST);
                 state.depthMask(false);
-                gl.viewport(x, y, width, height);
-                gl.scissor(x, y, width, height);
+                state.viewport(x, y, width, height);
+                state.scissor(x, y, width, height);
                 if (sampleIndex === 0) {
                     state.clearColor(0, 0, 0, 0);
                     gl.clear(gl.COLOR_BUFFER_BIT);
@@ -283,8 +284,8 @@ export class MultiSamplePass {
         drawPass.postprocessing.setOcclusionOffset(0, 0);
 
         this.bindOutputTarget(toDrawingBuffer);
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
 
         const accumulationWeight = sampleIndex * sampleWeight;
         if (accumulationWeight > 0) {

+ 7 - 3
src/mol-canvas3d/passes/passes.ts

@@ -8,22 +8,26 @@ import { DrawPass } from './draw';
 import { PickPass } from './pick';
 import { MultiSamplePass } from './multi-sample';
 import { WebGLContext } from '../../mol-gl/webgl/context';
+import { AssetManager } from '../../mol-util/assets';
 
 export class Passes {
     readonly draw: DrawPass;
     readonly pick: PickPass;
     readonly multiSample: MultiSamplePass;
 
-    constructor(private webgl: WebGLContext, attribs: Partial<{ pickScale: number, enableWboit: boolean }> = {}) {
+    constructor(private webgl: WebGLContext, assetManager: AssetManager, attribs: Partial<{ pickScale: number, enableWboit: boolean, enableDpoit: boolean }> = {}) {
         const { gl } = webgl;
-        this.draw = new DrawPass(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false);
+        this.draw = new DrawPass(webgl, assetManager, gl.drawingBufferWidth, gl.drawingBufferHeight, attribs.enableWboit || false, attribs.enableDpoit || false);
         this.pick = new PickPass(webgl, this.draw, attribs.pickScale || 0.25);
         this.multiSample = new MultiSamplePass(webgl, this.draw);
     }
 
     updateSize() {
         const { gl } = this.webgl;
-        this.draw.setSize(gl.drawingBufferWidth, gl.drawingBufferHeight);
+        // Avoid setting dimensions to 0x0 because it causes "empty textures are not allowed" error.
+        const width = Math.max(gl.drawingBufferWidth, 2);
+        const height = Math.max(gl.drawingBufferHeight, 2);
+        this.draw.setSize(width, height);
         this.pick.syncSize();
         this.multiSample.syncSize();
     }

+ 54 - 24
src/mol-canvas3d/passes/postprocessing.ts

@@ -28,6 +28,8 @@ import { Color } from '../../mol-util/color';
 import { FxaaParams, FxaaPass } from './fxaa';
 import { SmaaParams, SmaaPass } from './smaa';
 import { isTimingMode } from '../../mol-util/debug';
+import { BackgroundParams, BackgroundPass } from './background';
+import { AssetManager } from '../../mol-util/assets';
 
 const OutlinesSchema = {
     ...QuadSchema,
@@ -91,7 +93,7 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender
         ...QuadValues,
         tDepth: ValueCell.create(depthTexture),
 
-        uSamples: ValueCell.create([0.0, 0.0, 1.0]),
+        uSamples: ValueCell.create(getSamples(32)),
         dNSamples: ValueCell.create(32),
 
         uProjection: ValueCell.create(Mat4.identity()),
@@ -138,7 +140,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
         tSsaoDepth: ValueCell.create(ssaoDepthTexture),
         uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
 
-        uKernel: ValueCell.create([0.0]),
+        uKernel: ValueCell.create(getBlurKernel(15)),
         dOcclusionKernelSize: ValueCell.create(15),
 
         uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
@@ -171,15 +173,26 @@ function getBlurKernel(kernelSize: number): number[] {
     return kernel;
 }
 
-function getSamples(vectorSamples: Vec3[], nSamples: number): number[] {
+const RandomHemisphereVector: Vec3[] = [];
+for (let i = 0; i < 256; i++) {
+    const v = Vec3();
+    v[0] = Math.random() * 2.0 - 1.0;
+    v[1] = Math.random() * 2.0 - 1.0;
+    v[2] = Math.random();
+    Vec3.normalize(v, v);
+    Vec3.scale(v, v, Math.random());
+    RandomHemisphereVector.push(v);
+}
+
+function getSamples(nSamples: number): number[] {
     const samples = [];
     for (let i = 0; i < nSamples; i++) {
         let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples);
         scale = 0.1 + scale * (1.0 - 0.1);
 
-        samples.push(vectorSamples[i][0] * scale);
-        samples.push(vectorSamples[i][1] * scale);
-        samples.push(vectorSamples[i][2] * scale);
+        samples.push(RandomHemisphereVector[i][0] * scale);
+        samples.push(RandomHemisphereVector[i][1] * scale);
+        samples.push(RandomHemisphereVector[i][2] * scale);
     }
 
     return samples;
@@ -274,12 +287,13 @@ export const PostprocessingParams = {
         smaa: PD.Group(SmaaParams),
         off: PD.Group({})
     }, { options: [['fxaa', 'FXAA'], ['smaa', 'SMAA'], ['off', 'Off']], description: 'Smooth pixel edges' }),
+    background: PD.Group(BackgroundParams, { isFlat: true }),
 };
 export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
 
 export class PostprocessingPass {
     static isEnabled(props: PostprocessingProps) {
-        return props.occlusion.name === 'on' || props.outline.name === 'on';
+        return props.occlusion.name === 'on' || props.outline.name === 'on' || props.background.variant.name !== 'off';
     }
 
     static isOutlineEnabled(props: PostprocessingProps) {
@@ -291,7 +305,6 @@ export class PostprocessingPass {
     private readonly outlinesTarget: RenderTarget;
     private readonly outlinesRenderable: OutlinesRenderable;
 
-    private readonly randomHemisphereVector: Vec3[];
     private readonly ssaoFramebuffer: Framebuffer;
     private readonly ssaoBlurFirstPassFramebuffer: Framebuffer;
     private readonly ssaoBlurSecondPassFramebuffer: Framebuffer;
@@ -318,7 +331,10 @@ export class PostprocessingPass {
         return Math.min(1, 1 / this.webgl.pixelRatio) * this.downsampleFactor;
     }
 
-    constructor(private webgl: WebGLContext, private drawPass: DrawPass) {
+    private readonly bgColor = Vec3();
+    readonly background: BackgroundPass;
+
+    constructor(private readonly webgl: WebGLContext, assetManager: AssetManager, private readonly drawPass: DrawPass) {
         const { colorTarget, depthTextureTransparent, depthTextureOpaque } = drawPass;
         const width = colorTarget.getWidth();
         const height = colorTarget.getHeight();
@@ -334,16 +350,6 @@ export class PostprocessingPass {
         this.outlinesTarget = webgl.createRenderTarget(width, height, false);
         this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent);
 
-        this.randomHemisphereVector = [];
-        for (let i = 0; i < 256; i++) {
-            const v = Vec3();
-            v[0] = Math.random() * 2.0 - 1.0;
-            v[1] = Math.random() * 2.0 - 1.0;
-            v[2] = Math.random();
-            Vec3.normalize(v, v);
-            Vec3.scale(v, v, Math.random());
-            this.randomHemisphereVector.push(v);
-        }
         this.ssaoFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurFirstPassFramebuffer = webgl.resources.framebuffer();
         this.ssaoBlurSecondPassFramebuffer = webgl.resources.framebuffer();
@@ -368,6 +374,8 @@ export class PostprocessingPass {
         this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
         this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
         this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture);
+
+        this.background = new BackgroundPass(webgl, assetManager, width, height);
     }
 
     setSize(width: number, height: number) {
@@ -391,6 +399,8 @@ export class PostprocessingPass {
             ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
             ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
             ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
+
+            this.background.setSize(width, height);
         }
     }
 
@@ -440,7 +450,7 @@ export class PostprocessingPass {
                 needsUpdateSsao = true;
 
                 this.nSamples = props.occlusion.params.samples;
-                ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.randomHemisphereVector, this.nSamples));
+                ValueCell.update(this.ssaoRenderable.values.uSamples, getSamples(this.nSamples));
                 ValueCell.updateIfChanged(this.ssaoRenderable.values.dNSamples, this.nSamples);
             }
             ValueCell.updateIfChanged(this.ssaoRenderable.values.uRadius, Math.pow(2, props.occlusion.params.radius));
@@ -538,8 +548,8 @@ export class PostprocessingPass {
         state.depthMask(false);
 
         const { x, y, width, height } = camera.viewport;
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
     }
 
     private occlusionOffset: [x: number, y: number] = [0, 0];
@@ -549,6 +559,11 @@ export class PostprocessingPass {
         ValueCell.update(this.renderable.values.uOcclusionOffset, Vec2.set(this.renderable.values.uOcclusionOffset.ref.value, x, y));
     }
 
+    private transparentBackground = false;
+    setTransparentBackground(value: boolean) {
+        this.transparentBackground = value;
+    }
+
     render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
         if (isTimingMode) this.webgl.timer.mark('PostprocessingPass.render');
         this.updateState(camera, transparentBackground, backgroundColor, props);
@@ -583,8 +598,23 @@ export class PostprocessingPass {
         }
 
         const { gl, state } = this.webgl;
-        state.clearColor(0, 0, 0, 1);
-        gl.clear(gl.COLOR_BUFFER_BIT);
+
+        this.background.update(camera, props.background);
+        if (this.background.isEnabled(props.background)) {
+            if (this.transparentBackground) {
+                state.clearColor(0, 0, 0, 0);
+            } else {
+                Color.toVec3Normalized(this.bgColor, backgroundColor);
+                state.clearColor(this.bgColor[0], this.bgColor[1], this.bgColor[2], 1);
+            }
+            gl.clear(gl.COLOR_BUFFER_BIT);
+            state.enable(gl.BLEND);
+            state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+            this.background.render();
+        } else {
+            state.clearColor(0, 0, 0, 1);
+            gl.clear(gl.COLOR_BUFFER_BIT);
+        }
 
         this.renderable.render();
         if (isTimingMode) this.webgl.timer.markEnd('PostprocessingPass.render');

+ 7 - 6
src/mol-canvas3d/passes/smaa.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -11,7 +11,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
 import { WebGLContext } from '../../mol-gl/webgl/context';
 import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
 import { RenderTarget } from '../../mol-gl/webgl/render-target';
-import { createTexture, loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
+import { loadImageTexture, Texture } from '../../mol-gl/webgl/texture';
 import { Vec2, Vec4 } from '../../mol-math/linear-algebra';
 import { ValueCell } from '../../mol-util';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -71,9 +71,10 @@ export class SmaaPass {
         state.depthMask(false);
 
         const { x, y, width, height } = viewport;
-        gl.viewport(x, y, width, height);
-        gl.scissor(x, y, width, height);
+        state.viewport(x, y, width, height);
+        state.scissor(x, y, width, height);
 
+        state.colorMask(true, true, true, true);
         state.clearColor(0, 0, 0, 1);
         gl.clear(gl.COLOR_BUFFER_BIT);
 
@@ -191,8 +192,8 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights
     const width = edgesTexture.getWidth();
     const height = edgesTexture.getHeight();
 
-    const areaTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgb', 'ubyte', 'linear');
-    const searchTexture = createTexture(ctx.gl, ctx.extensions, 'image-uint8', 'rgba', 'ubyte', 'nearest');
+    const areaTexture = ctx.resources.texture('image-uint8', 'rgb', 'ubyte', 'linear');
+    const searchTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
 
     const values: Values<typeof WeightsSchema> = {
         ...QuadValues,

+ 11 - 1
src/mol-canvas3d/passes/wboit.ts

@@ -18,6 +18,8 @@ import { evaluateWboit_frag } from '../../mol-gl/shader/evaluate-wboit.frag';
 import { Framebuffer } from '../../mol-gl/webgl/framebuffer';
 import { Vec2 } from '../../mol-math/linear-algebra';
 import { isDebugMode, isTimingMode } from '../../mol-util/debug';
+import { isWebGL2 } from '../../mol-gl/webgl/compat';
+import { Renderbuffer } from '../../mol-gl/webgl/renderbuffer';
 
 const EvaluateWboitSchema = {
     ...QuadSchema,
@@ -50,6 +52,7 @@ export class WboitPass {
     private readonly framebuffer: Framebuffer;
     private readonly textureA: Texture;
     private readonly textureB: Texture;
+    private readonly depthRenderbuffer: Renderbuffer;
 
     private _supported = false;
     get supported() {
@@ -87,6 +90,7 @@ export class WboitPass {
         if (width !== w || height !== h) {
             this.textureA.define(width, height);
             this.textureB.define(width, height);
+            this.depthRenderbuffer.setSize(width, height);
             ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
         }
     }
@@ -106,6 +110,8 @@ export class WboitPass {
 
         this.textureA.attachFramebuffer(this.framebuffer, 'color0');
         this.textureB.attachFramebuffer(this.framebuffer, 'color1');
+
+        this.depthRenderbuffer.attachFramebuffer(this.framebuffer);
     }
 
     static isSupported(webgl: WebGLContext) {
@@ -128,7 +134,7 @@ export class WboitPass {
     constructor(private webgl: WebGLContext, width: number, height: number) {
         if (!WboitPass.isSupported(webgl)) return;
 
-        const { resources } = webgl;
+        const { resources, gl } = webgl;
 
         this.textureA = resources.texture('image-float32', 'rgba', 'float', 'nearest');
         this.textureA.define(width, height);
@@ -136,6 +142,10 @@ export class WboitPass {
         this.textureB = resources.texture('image-float32', 'rgba', 'float', 'nearest');
         this.textureB.define(width, height);
 
+        this.depthRenderbuffer = isWebGL2(gl)
+            ? resources.renderbuffer('depth32f', 'depth', width, height)
+            : resources.renderbuffer('depth16', 'depth', width, height);
+
         this.renderable = getEvaluateWboitRenderable(webgl, this.textureA, this.textureB);
         this.framebuffer = resources.framebuffer();
 

+ 1 - 0
src/mol-geo/geometry/base.ts

@@ -111,6 +111,7 @@ export namespace BaseGeometry {
             uRoughness: ValueCell.create(props.material.roughness),
             uBumpiness: ValueCell.create(props.material.bumpiness),
             dLightCount: ValueCell.create(1),
+            dColorMarker: ValueCell.create(true),
 
             dClipObjectCount: ValueCell.create(clip.objects.count),
             dClipVariant: ValueCell.create(clip.variant),

+ 4 - 0
src/mol-geo/geometry/mesh/mesh.ts

@@ -45,6 +45,8 @@ export interface Mesh {
     readonly normalBuffer: ValueCell<Float32Array>,
     /** Group buffer as array of group ids for each vertex wrapped in a value cell */
     readonly groupBuffer: ValueCell<Float32Array>,
+    /** Indicates that group may vary within a triangle, wrapped in a value cell */
+    readonly varyingGroup: ValueCell<boolean>,
 
     /** Bounding sphere of the mesh */
     readonly boundingSphere: Sphere3D
@@ -95,6 +97,7 @@ export namespace Mesh {
             indexBuffer: ValueCell.create(indices),
             normalBuffer: ValueCell.create(normals),
             groupBuffer: ValueCell.create(groups),
+            varyingGroup: ValueCell.create(false),
             get boundingSphere() {
                 const newHash = hashCode(mesh);
                 if (newHash !== currentHash) {
@@ -686,6 +689,7 @@ export namespace Mesh {
             aNormal: mesh.normalBuffer,
             aGroup: mesh.groupBuffer,
             elements: mesh.indexBuffer,
+            dVaryingGroup: mesh.varyingGroup,
             boundingSphere: ValueCell.create(boundingSphere),
             invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
             uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),

+ 2 - 2
src/mol-geo/geometry/text/font-atlas.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -88,7 +88,7 @@ export class FontAtlas {
         this.scratchCanvas.width = this.maxWidth;
         this.scratchCanvas.height = this.lineHeight;
 
-        this.scratchContext = this.scratchCanvas.getContext('2d')!;
+        this.scratchContext = this.scratchCanvas.getContext('2d', { willReadFrequently: true })!;
         this.scratchContext.font = `${p.fontStyle} ${p.fontVariant} ${p.fontWeight} ${fontSize}px ${p.fontFamily}`;
         this.scratchContext.fillStyle = 'black';
         this.scratchContext.textBaseline = 'middle';

+ 14 - 18
src/mol-geo/geometry/texture-mesh/color-smoothing.ts

@@ -27,20 +27,18 @@ export const ColorAccumulateSchema = {
     instanceCount: ValueSpec('number'),
     stride: ValueSpec('number'),
 
-    uTotalCount: UniformSpec('i'),
-    uInstanceCount: UniformSpec('i'),
-    uGroupCount: UniformSpec('i'),
+    uGroupCount: UniformSpec('i', 'material'),
 
     aTransform: AttributeSpec('float32', 16, 1),
     aInstance: AttributeSpec('float32', 1, 1),
     aSample: AttributeSpec('float32', 1, 0),
 
-    uGeoTexDim: UniformSpec('v2', 'buffered'),
-    tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest'),
-    tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest'),
+    uGeoTexDim: UniformSpec('v2', 'material'),
+    tPosition: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
+    tGroup: TextureSpec('texture', 'rgba', 'float', 'nearest', 'material'),
 
-    uColorTexDim: UniformSpec('v2'),
-    tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
+    uColorTexDim: UniformSpec('v2', 'material'),
+    tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest', 'material'),
     dColorType: DefineSpec('string', ['group', 'groupInstance', 'vertex', 'vertexInstance']),
 
     uCurrentSlice: UniformSpec('f'),
@@ -88,8 +86,6 @@ function getAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, box:
         ValueCell.updateIfChanged(v.instanceCount, input.instanceCount);
         ValueCell.updateIfChanged(v.stride, stride);
 
-        ValueCell.updateIfChanged(v.uTotalCount, input.vertexCount);
-        ValueCell.updateIfChanged(v.uInstanceCount, input.instanceCount);
         ValueCell.updateIfChanged(v.uGroupCount, input.groupCount);
 
         ValueCell.update(v.aTransform, input.transformBuffer);
@@ -126,8 +122,6 @@ function createAccumulateRenderable(ctx: WebGLContext, input: AccumulateInput, b
         instanceCount: ValueCell.create(input.instanceCount),
         stride: ValueCell.create(stride),
 
-        uTotalCount: ValueCell.create(input.vertexCount),
-        uInstanceCount: ValueCell.create(input.instanceCount),
         uGroupCount: ValueCell.create(input.groupCount),
 
         aTransform: ValueCell.create(input.transformBuffer),
@@ -325,8 +319,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
 
     if (isTimingMode) webgl.timer.mark('ColorAccumulate.render');
     setAccumulateDefaults(webgl);
-    gl.viewport(0, 0, width, height);
-    gl.scissor(0, 0, width, height);
+    state.viewport(0, 0, width, height);
+    state.scissor(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
     ValueCell.update(uCurrentY, 0);
     let currCol = 0;
@@ -342,8 +336,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
         // console.log({ i, currX, currY });
         ValueCell.update(uCurrentX, currX);
         ValueCell.update(uCurrentSlice, i);
-        gl.viewport(currX, currY, dx, dy);
-        gl.scissor(currX, currY, dx, dy);
+        state.viewport(currX, currY, dx, dy);
+        state.scissor(currX, currY, dx, dy);
         accumulateRenderable.render();
         ++currCol;
         currX += dx;
@@ -377,8 +371,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
 
     setNormalizeDefaults(webgl);
     texture.attachFramebuffer(framebuffer, 0);
-    gl.viewport(0, 0, width, height);
-    gl.scissor(0, 0, width, height);
+    state.viewport(0, 0, width, height);
+    state.scissor(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
     normalizeRenderable.render();
     if (isTimingMode) webgl.timer.markEnd('ColorNormalize.render');
@@ -393,6 +387,8 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
     const type = isInstanceType ? 'volumeInstance' : 'volume';
     if (isTimingMode) webgl.timer.markEnd('calcTextureMeshColorSmoothing');
 
+    // printTextureImage(readTexture(webgl, texture), { scale: 0.75 });
+
     return { texture, gridDim, gridTexDim: Vec2.create(width, height), gridTransform, type };
 }
 

+ 3 - 0
src/mol-geo/geometry/texture-mesh/texture-mesh.ts

@@ -38,6 +38,7 @@ export interface TextureMesh {
     readonly vertexTexture: ValueCell<Texture>,
     readonly groupTexture: ValueCell<Texture>,
     readonly normalTexture: ValueCell<Texture>,
+    readonly varyingGroup: ValueCell<boolean>,
     readonly doubleBuffer: TextureMesh.DoubleBuffer
 
     readonly boundingSphere: Sphere3D
@@ -92,6 +93,7 @@ export namespace TextureMesh {
                 vertexTexture: ValueCell.create(vertexTexture),
                 groupTexture: ValueCell.create(groupTexture),
                 normalTexture: ValueCell.create(normalTexture),
+                varyingGroup: ValueCell.create(false),
                 doubleBuffer: new DoubleBuffer(),
                 boundingSphere: Sphere3D.clone(boundingSphere),
                 meta: {}
@@ -157,6 +159,7 @@ export namespace TextureMesh {
             tPosition: textureMesh.vertexTexture,
             tGroup: textureMesh.groupTexture,
             tNormal: textureMesh.normalTexture,
+            dVaryingGroup: textureMesh.varyingGroup,
 
             boundingSphere: ValueCell.create(boundingSphere),
             invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),

+ 5 - 5
src/mol-gl/_spec/renderer.spec.ts

@@ -53,17 +53,17 @@ describe('renderer', () => {
         scene.commit();
         expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
         expect(ctx.stats.resourceCounts.texture).toBe(9);
-        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 5 : 0);
-        expect(ctx.stats.resourceCounts.program).toBe(5);
-        expect(ctx.stats.resourceCounts.shader).toBe(10);
+        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 6 : 0);
+        expect(ctx.stats.resourceCounts.program).toBe(6);
+        expect(ctx.stats.resourceCounts.shader).toBe(12);
 
         scene.remove(points);
         scene.commit();
         expect(ctx.stats.resourceCounts.attribute).toBe(0);
         expect(ctx.stats.resourceCounts.texture).toBe(1);
         expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
-        expect(ctx.stats.resourceCounts.program).toBe(5);
-        expect(ctx.stats.resourceCounts.shader).toBe(10);
+        expect(ctx.stats.resourceCounts.program).toBe(6);
+        expect(ctx.stats.resourceCounts.shader).toBe(12);
 
         ctx.resources.destroy();
         expect(ctx.stats.resourceCounts.program).toBe(0);

+ 2 - 2
src/mol-gl/compute/grid3d.ts

@@ -225,8 +225,8 @@ export function createGrid3dComputeRenderable<S extends RenderableSchema, P, CS>
 
 function resetGl(webgl: WebGLContext, w: number) {
     const { gl, state } = webgl;
-    gl.viewport(0, 0, w, w);
-    gl.scissor(0, 0, w, w);
+    state.viewport(0, 0, w, w);
+    state.scissor(0, 0, w, w);
     state.disable(gl.SCISSOR_TEST);
     state.disable(gl.BLEND);
     state.disable(gl.DEPTH_TEST);

+ 8 - 8
src/mol-gl/compute/histogram-pyramid/reduction.ts

@@ -122,7 +122,7 @@ export interface HistogramPyramid {
 
 export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2, gridTexDim: Vec3): HistogramPyramid {
     if (isTimingMode) ctx.timer.mark('createHistogramPyramid');
-    const { gl } = ctx;
+    const { gl, state } = ctx;
     const w = inputTexture.getWidth();
     const h = inputTexture.getHeight();
 
@@ -146,7 +146,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
     const framebuffer = getFramebuffer('pyramid', ctx);
     pyramidTex.attachFramebuffer(framebuffer, 0);
 
-    gl.viewport(0, 0, maxSizeX, maxSizeY);
+    state.viewport(0, 0, maxSizeX, maxSizeY);
     if (isWebGL2(gl)) {
         gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
     } else {
@@ -157,7 +157,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
     for (let i = 0; i < levels; ++i) levelTexturesFramebuffers.push(getLevelTextureFramebuffer(ctx, i));
 
     const renderable = getHistopyramidReductionRenderable(ctx, inputTexture, levelTexturesFramebuffers[0].texture);
-    ctx.state.currentRenderItemId = -1;
+    state.currentRenderItemId = -1;
     setRenderingDefaults(ctx);
 
     let offset = 0;
@@ -176,15 +176,15 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
             ValueCell.update(renderable.values.tPreviousLevel, levelTexturesFramebuffers[levels - i].texture);
             renderable.update();
         }
-        ctx.state.currentRenderItemId = -1;
-        gl.viewport(0, 0, size, size);
-        gl.scissor(0, 0, size, size);
+        state.currentRenderItemId = -1;
+        state.viewport(0, 0, size, size);
+        state.scissor(0, 0, size, size);
         if (isWebGL2(gl)) {
             gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]);
         } else {
             gl.clear(gl.COLOR_BUFFER_BIT);
         }
-        gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
+        state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
         renderable.render();
 
         pyramidTex.bind(0);
@@ -197,7 +197,7 @@ export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture,
     gl.finish();
     if (isTimingMode) ctx.timer.markEnd('createHistogramPyramid');
 
-    // printTexture(ctx, pyramidTex, 2)
+    // printTextureImage(readTexture(ctx, pyramidTex), { scale: 0.75 });
 
     //
 

+ 2 - 2
src/mol-gl/compute/histogram-pyramid/sum.ts

@@ -68,7 +68,7 @@ const sumInts = new Int32Array(4);
 
 export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
     if (isTimingMode) ctx.timer.mark('getHistopyramidSum');
-    const { gl, resources } = ctx;
+    const { gl, state, resources } = ctx;
 
     const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture);
     ctx.state.currentRenderItemId = -1;
@@ -89,7 +89,7 @@ export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture
 
     setRenderingDefaults(ctx);
 
-    gl.viewport(0, 0, 1, 1);
+    state.viewport(0, 0, 1, 1);
     renderable.render();
     gl.finish();
 

+ 5 - 4
src/mol-gl/compute/marching-cubes/active-voxels.ts

@@ -85,7 +85,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
 
 export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
     if (isTimingMode) ctx.timer.mark('calcActiveVoxels');
-    const { gl, resources } = ctx;
+    const { gl, state, resources } = ctx;
     const width = volumeData.getWidth();
     const height = volumeData.getHeight();
 
@@ -106,15 +106,16 @@ export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim
 
     activeVoxelsTex.attachFramebuffer(framebuffer, 0);
     setRenderingDefaults(ctx);
-    gl.viewport(0, 0, width, height);
-    gl.scissor(0, 0, width, height);
+    state.viewport(0, 0, width, height);
+    state.scissor(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
-    gl.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
+    state.scissor(0, 0, gridTexDim[0], gridTexDim[1]);
     renderable.render();
 
     // console.log('gridScale', gridScale, 'gridTexDim', gridTexDim, 'gridDim', gridDim);
     // console.log('volumeData', volumeData);
     // console.log('at', readTexture(ctx, activeVoxelsTex));
+    // printTextureImage(readTexture(ctx, activeVoxelsTex), { scale: 0.75 });
 
     gl.finish();
     if (isTimingMode) ctx.timer.markEnd('calcActiveVoxels');

+ 17 - 10
src/mol-gl/compute/marching-cubes/isosurface.ts

@@ -42,12 +42,13 @@ const IsosurfaceSchema = {
 
     dPackedGroup: DefineSpec('boolean'),
     dAxisOrder: DefineSpec('string', ['012', '021', '102', '120', '201', '210']),
+    dConstantGroup: DefineSpec('boolean'),
 };
 type IsosurfaceValues = Values<typeof IsosurfaceSchema>
 
 const IsosurfaceName = 'isosurface';
 
-function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3): ComputeRenderable<IsosurfaceValues> {
+function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
     if (ctx.namedComputeRenderables[IsosurfaceName]) {
         const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
 
@@ -66,17 +67,18 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
         ValueCell.update(v.uGridTransform, transform);
         ValueCell.update(v.uScale, scale);
 
-        ValueCell.update(v.dPackedGroup, packedGroup);
+        ValueCell.updateIfChanged(v.dPackedGroup, packedGroup);
         ValueCell.updateIfChanged(v.dAxisOrder, axisOrder.join(''));
+        ValueCell.updateIfChanged(v.dConstantGroup, constantGroup);
 
         ctx.namedComputeRenderables[IsosurfaceName].update();
     } else {
-        ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
+        ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
     }
     return ctx.namedComputeRenderables[IsosurfaceName];
 }
 
-function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3) {
+function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
     // console.log('uSize', Math.pow(2, levels))
     const values: IsosurfaceValues = {
         ...QuadValues,
@@ -99,6 +101,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
 
         dPackedGroup: ValueCell.create(packedGroup),
         dAxisOrder: ValueCell.create(axisOrder.join('')),
+        dConstantGroup: ValueCell.create(constantGroup),
     };
 
     const schema = { ...IsosurfaceSchema };
@@ -119,12 +122,12 @@ function setRenderingDefaults(ctx: WebGLContext) {
     state.clearColor(0, 0, 0, 0);
 }
 
-export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
+export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
     const { drawBuffers } = ctx.extensions;
     if (!drawBuffers) throw new Error('need WebGL draw buffers');
 
     if (isTimingMode) ctx.timer.mark('createIsosurfaceBuffers');
-    const { gl, resources, extensions } = ctx;
+    const { gl, state, resources, extensions } = ctx;
     const { pyramidTex, height, levels, scale, count } = histogramPyramid;
     const width = pyramidTex.getWidth();
 
@@ -178,7 +181,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     groupTexture.attachFramebuffer(framebuffer, 1);
     normalTexture.attachFramebuffer(framebuffer, 2);
 
-    const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder);
+    const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
     ctx.state.currentRenderItemId = -1;
 
     framebuffer.bind();
@@ -189,13 +192,17 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
     ]);
 
     setRenderingDefaults(ctx);
-    gl.viewport(0, 0, width, height);
+    state.viewport(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
     renderable.render();
 
     gl.finish();
     if (isTimingMode) ctx.timer.markEnd('createIsosurfaceBuffers');
 
+    // printTextureImage(readTexture(ctx, vertexTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
+    // printTextureImage(readTexture(ctx, groupTexture, new Uint8Array(width * height * 4)), { scale: 0.75 });
+    // printTextureImage(readTexture(ctx, normalTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
+
     return { vertexTexture, groupTexture, normalTexture, vertexCount: count };
 }
 
@@ -210,11 +217,11 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
  *
  * Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
  */
-export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
+export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
     if (isTimingMode) ctx.timer.mark('extractIsosurface');
     const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
     const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
-    const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, vertexTexture, groupTexture, normalTexture);
+    const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
     if (isTimingMode) ctx.timer.markEnd('extractIsosurface');
 
     return gv;

+ 5 - 5
src/mol-gl/compute/util.ts

@@ -75,9 +75,9 @@ export function getSharedCopyRenderable(ctx: WebGLContext, texture: Texture) {
 const ReadTextureName = 'read-texture';
 const ReadAlphaTextureName = 'read-alpha-texture';
 
-export function readTexture(ctx: WebGLContext, texture: Texture) {
+export function readTexture<T extends Uint8Array | Float32Array | Int32Array = Uint8Array>(ctx: WebGLContext, texture: Texture, array?: T) {
     const { gl, resources } = ctx;
-    if (texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
+    if (!array && texture.type !== gl.UNSIGNED_BYTE) throw new Error('unsupported texture type');
 
     if (!ctx.namedFramebuffers[ReadTextureName]) {
         ctx.namedFramebuffers[ReadTextureName] = resources.framebuffer();
@@ -86,7 +86,7 @@ export function readTexture(ctx: WebGLContext, texture: Texture) {
 
     const width = texture.getWidth();
     const height = texture.getHeight();
-    const array = new Uint8Array(width * height * 4);
+    if (!array) array = new Uint8Array(width * height * 4) as T;
     framebuffer.bind();
     texture.attachFramebuffer(framebuffer, 0);
     ctx.readPixels(0, 0, width, height, array);
@@ -125,8 +125,8 @@ export function readAlphaTexture(ctx: WebGLContext, texture: Texture) {
     state.clearColor(0, 0, 0, 0);
     state.blendFunc(gl.ONE, gl.ONE);
     state.blendEquation(gl.FUNC_ADD);
-    gl.viewport(0, 0, width, height);
-    gl.scissor(0, 0, width, height);
+    state.viewport(0, 0, width, height);
+    state.scissor(0, 0, width, height);
     gl.clear(gl.COLOR_BUFFER_BIT);
     copy.render();
 

+ 5 - 6
src/mol-gl/renderable.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -10,7 +10,6 @@ import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant } from './
 import { ValueCell } from '../mol-util';
 import { idFactory } from '../mol-util/id-factory';
 import { clamp } from '../mol-math/interpolate';
-import { Textures } from './webgl/texture';
 
 const getNextRenderableId = idFactory();
 
@@ -30,7 +29,7 @@ export interface Renderable<T extends RenderableValues> {
     readonly values: T
     readonly state: RenderableState
 
-    render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => void
+    render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => void
     getProgram: (variant: GraphicsRenderVariant) => Program
     update: () => void
     dispose: () => void
@@ -43,11 +42,11 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
         values,
         state,
 
-        render: (variant: GraphicsRenderVariant, sharedTexturesList?: Textures) => {
+        render: (variant: GraphicsRenderVariant, sharedTexturesCount: number) => {
             if (values.uAlpha && values.alpha) {
                 ValueCell.updateIfChanged(values.uAlpha, clamp(values.alpha.ref.value * state.alphaFactor, 0, 1));
             }
-            renderItem.render(variant, sharedTexturesList);
+            renderItem.render(variant, sharedTexturesCount);
         },
         getProgram: (variant: GraphicsRenderVariant) => renderItem.getProgram(variant),
         update: () => renderItem.update(),
@@ -73,7 +72,7 @@ export function createComputeRenderable<T extends Values<RenderableSchema>>(rend
         id: getNextRenderableId(),
         values,
 
-        render: () => renderItem.render('compute'),
+        render: () => renderItem.render('compute', 0),
         update: () => renderItem.update(),
         dispose: () => renderItem.destroy()
     };

+ 3 - 3
src/mol-gl/renderable/cylinders.ts

@@ -23,12 +23,12 @@ export const CylindersSchema = {
     elements: ElementsSpec('uint32'),
 
     padding: ValueSpec('number'),
-    uDoubleSided: UniformSpec('b'),
+    uDoubleSided: UniformSpec('b', 'material'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
-    uBumpFrequency: UniformSpec('f'),
-    uBumpAmplitude: UniformSpec('f'),
+    uBumpFrequency: UniformSpec('f', 'material'),
+    uBumpAmplitude: UniformSpec('f', 'material'),
 };
 export type CylindersSchema = typeof CylindersSchema
 export type CylindersValues = Values<CylindersSchema>

+ 2 - 2
src/mol-gl/renderable/direct-volume.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -26,7 +26,7 @@ export const DirectVolumeSchema = {
     uTransform: UniformSpec('m4'),
     uGridDim: UniformSpec('v3'),
     tTransferTex: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
-    uTransferScale: UniformSpec('f'),
+    uTransferScale: UniformSpec('f', 'material'),
 
     dGridTexType: DefineSpec('string', ['2d', '3d']),
     uGridTexDim: UniformSpec('v3'),

+ 2 - 2
src/mol-gl/renderable/lines.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -20,7 +20,7 @@ export const LinesSchema = {
     aEnd: AttributeSpec('float32', 3, 0),
     elements: ElementsSpec('uint32'),
     dLineSizeAttenuation: DefineSpec('boolean'),
-    uDoubleSided: UniformSpec('b'),
+    uDoubleSided: UniformSpec('b', 'material'),
     dFlipSided: DefineSpec('boolean'),
 };
 export type LinesSchema = typeof LinesSchema

+ 4 - 3
src/mol-gl/renderable/mesh.ts

@@ -17,14 +17,15 @@ export const MeshSchema = {
     aPosition: AttributeSpec('float32', 3, 0),
     aNormal: AttributeSpec('float32', 3, 0),
     elements: ElementsSpec('uint32'),
+    dVaryingGroup: DefineSpec('boolean'),
     dFlatShaded: DefineSpec('boolean'),
-    uDoubleSided: UniformSpec('b'),
+    uDoubleSided: UniformSpec('b', 'material'),
     dFlipSided: DefineSpec('boolean'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
-    uBumpFrequency: UniformSpec('f'),
-    uBumpAmplitude: UniformSpec('f'),
+    uBumpFrequency: UniformSpec('f', 'material'),
+    uBumpAmplitude: UniformSpec('f', 'material'),
     meta: ValueSpec('unknown')
 } as const;
 export type MeshSchema = typeof MeshSchema

+ 24 - 13
src/mol-gl/renderable/schema.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { ValueCell } from '../../mol-util';
@@ -36,6 +37,7 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
     const attributeValues: AttributeValues = {};
     const defineValues: DefineValues = {};
     const textureValues: TextureValues = {};
+    const materialTextureValues: TextureValues = {};
     const uniformValues: UniformValues = {};
     const materialUniformValues: UniformValues = {};
     const bufferedUniformValues: UniformValues = {};
@@ -44,7 +46,10 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
         if (spec.type === 'attribute') attributeValues[k] = values[k];
         if (spec.type === 'define') defineValues[k] = values[k];
         // check if k exists in values to exclude global textures
-        if (spec.type === 'texture' && values[k] !== undefined) textureValues[k] = values[k];
+        if (spec.type === 'texture' && values[k] !== undefined) {
+            if (spec.variant === 'material') materialTextureValues[k] = values[k];
+            else textureValues[k] = values[k];
+        }
         // check if k exists in values to exclude global uniforms
         if (spec.type === 'uniform' && values[k] !== undefined) {
             if (spec.variant === 'material') materialUniformValues[k] = values[k];
@@ -52,7 +57,7 @@ export function splitValues(schema: RenderableSchema, values: RenderableValues)
             else uniformValues[k] = values[k];
         }
     });
-    return { attributeValues, defineValues, textureValues, uniformValues, materialUniformValues, bufferedUniformValues };
+    return { attributeValues, defineValues, textureValues, materialTextureValues, uniformValues, materialUniformValues, bufferedUniformValues };
 }
 
 export type Versions<T extends RenderableValues> = { [k in keyof T]: number }
@@ -76,9 +81,9 @@ export function UniformSpec<K extends UniformKind>(kind: K, variant?: 'material'
     return { type: 'uniform', kind, variant };
 }
 
-export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter }
-export function TextureSpec<K extends TextureKind>(kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter): TextureSpec<K> {
-    return { type: 'texture', kind, format, dataType, filter };
+export type TextureSpec<K extends TextureKind> = { type: 'texture', kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter, variant?: 'material' }
+export function TextureSpec<K extends TextureKind>(kind: K, format: TextureFormat, dataType: TextureType, filter: TextureFilter, variant?: 'material'): TextureSpec<K> {
+    return { type: 'texture', kind, format, dataType, filter, variant };
 }
 
 export type ElementsSpec<K extends ElementsKind> = { type: 'elements', kind: K }
@@ -163,6 +168,11 @@ export type GlobalUniformValues = Values<GlobalUniformSchema>
 
 export const GlobalTextureSchema = {
     tDepth: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
+
+    // dpoit
+    tDpoitDepth: TextureSpec('texture', 'rg', 'float', 'nearest'),
+    tDpoitFrontColor: TextureSpec('texture', 'rgba', 'float', 'nearest'),
+    tDpoitBackColor: TextureSpec('texture', 'rgba', 'float', 'nearest')
 } as const;
 export type GlobalTextureSchema = typeof GlobalTextureSchema
 export type GlobalTextureValues = Values<GlobalTextureSchema>
@@ -194,7 +204,7 @@ export const SizeSchema = {
     uSizeTexDim: UniformSpec('v2'),
     tSize: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
     dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'groupInstance']),
-    uSizeFactor: UniformSpec('f'),
+    uSizeFactor: UniformSpec('f', 'material'),
 } as const;
 export type SizeSchema = typeof SizeSchema
 export type SizeValues = Values<SizeSchema>
@@ -232,7 +242,7 @@ export const TransparencySchema = {
     uTransparencyGridDim: UniformSpec('v3'),
     uTransparencyGridTransform: UniformSpec('v4'),
     tTransparencyGrid: TextureSpec('texture', 'alpha', 'ubyte', 'linear'),
-    dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance']),
+    dTransparencyType: DefineSpec('string', ['instance', 'groupInstance', 'volumeInstance'])
 } as const;
 export type TransparencySchema = typeof TransparencySchema
 export type TransparencyValues = Values<TransparencySchema>
@@ -270,14 +280,15 @@ export const BaseSchema = {
     ...ClippingSchema,
 
     dLightCount: DefineSpec('number'),
+    dColorMarker: DefineSpec('boolean'),
 
     dClipObjectCount: DefineSpec('number'),
     dClipVariant: DefineSpec('string', ['instance', 'pixel']),
-    uClipObjectType: UniformSpec('i[]'),
-    uClipObjectInvert: UniformSpec('b[]'),
-    uClipObjectPosition: UniformSpec('v3[]'),
-    uClipObjectRotation: UniformSpec('v4[]'),
-    uClipObjectScale: UniformSpec('v3[]'),
+    uClipObjectType: UniformSpec('i[]', 'material'),
+    uClipObjectInvert: UniformSpec('b[]', 'material'),
+    uClipObjectPosition: UniformSpec('v3[]', 'material'),
+    uClipObjectRotation: UniformSpec('v4[]', 'material'),
+    uClipObjectScale: UniformSpec('v3[]', 'material'),
 
     aInstance: AttributeSpec('float32', 1, 1),
     /**
@@ -322,4 +333,4 @@ export const BaseSchema = {
     invariantBoundingSphere: ValueSpec('sphere'),
 } as const;
 export type BaseSchema = typeof BaseSchema
-export type BaseValues = Values<BaseSchema>
+export type BaseValues = Values<BaseSchema>

+ 3 - 3
src/mol-gl/renderable/spheres.ts

@@ -20,12 +20,12 @@ export const SpheresSchema = {
     elements: ElementsSpec('uint32'),
 
     padding: ValueSpec('number'),
-    uDoubleSided: UniformSpec('b'),
+    uDoubleSided: UniformSpec('b', 'material'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
-    uBumpFrequency: UniformSpec('f'),
-    uBumpAmplitude: UniformSpec('f'),
+    uBumpFrequency: UniformSpec('f', 'material'),
+    uBumpAmplitude: UniformSpec('f', 'material'),
 };
 export type SpheresSchema = typeof SpheresSchema
 export type SpheresValues = Values<SpheresSchema>

+ 8 - 8
src/mol-gl/renderable/text.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -24,13 +24,13 @@ export const TextSchema = {
     tFont: TextureSpec('image-uint8', 'alpha', 'ubyte', 'linear'),
     padding: ValueSpec('number'),
 
-    uBorderWidth: UniformSpec('f'),
-    uBorderColor: UniformSpec('v3'),
-    uOffsetX: UniformSpec('f'),
-    uOffsetY: UniformSpec('f'),
-    uOffsetZ: UniformSpec('f'),
-    uBackgroundColor: UniformSpec('v3'),
-    uBackgroundOpacity: UniformSpec('f'),
+    uBorderWidth: UniformSpec('f', 'material'),
+    uBorderColor: UniformSpec('v3', 'material'),
+    uOffsetX: UniformSpec('f', 'material'),
+    uOffsetY: UniformSpec('f', 'material'),
+    uOffsetZ: UniformSpec('f', 'material'),
+    uBackgroundColor: UniformSpec('v3', 'material'),
+    uBackgroundOpacity: UniformSpec('f', 'material'),
 };
 export type TextSchema = typeof TextSchema
 export type TextValues = Values<TextSchema>

+ 4 - 4
src/mol-gl/renderable/texture-mesh.ts

@@ -17,15 +17,15 @@ export const TextureMeshSchema = {
     tPosition: TextureSpec('texture', 'rgb', 'float', 'nearest'),
     tGroup: TextureSpec('texture', 'alpha', 'float', 'nearest'),
     tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'),
-
+    dVaryingGroup: DefineSpec('boolean'),
     dFlatShaded: DefineSpec('boolean'),
-    uDoubleSided: UniformSpec('b'),
+    uDoubleSided: UniformSpec('b', 'material'),
     dFlipSided: DefineSpec('boolean'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
-    uBumpFrequency: UniformSpec('f'),
-    uBumpAmplitude: UniformSpec('f'),
+    uBumpFrequency: UniformSpec('f', 'material'),
+    uBumpAmplitude: UniformSpec('f', 'material'),
     meta: ValueSpec('unknown')
 };
 export type TextureMeshSchema = typeof TextureMeshSchema

+ 15 - 3
src/mol-gl/renderable/util.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -8,6 +8,7 @@ import { Sphere3D } from '../../mol-math/geometry';
 import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
 import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
 import { TextureFilter } from '../webgl/texture';
+import { arrayMinMax } from '../../mol-util/array';
 
 export function calculateTextureInfo(n: number, itemSize: number) {
     n = Math.max(n, 2); // observed issues with 1 pixel textures
@@ -42,7 +43,8 @@ export function createTextureImage<T extends Uint8Array | Float32Array>(n: numbe
 const DefaultPrintImageOptions = {
     scale: 1,
     pixelated: false,
-    id: 'molstar.debug.image'
+    id: 'molstar.debug.image',
+    normalize: false,
 };
 export type PrintImageOptions = typeof DefaultPrintImageOptions
 
@@ -58,7 +60,17 @@ export function printTextureImage(textureImage: TextureImage<any>, options: Part
             }
         }
     } else if (itemSize === 4) {
-        data.set(array);
+        if (options.normalize) {
+            const [min, max] = arrayMinMax(array);
+            for (let i = 0, il = width * height * 4; i < il; i += 4) {
+                data[i] = ((array[i] - min) / (max - min)) * 255;
+                data[i + 1] = ((array[i + 1] - min) / (max - min)) * 255;
+                data[i + 2] = ((array[i + 2] - min) / (max - min)) * 255;
+                data[i + 3] = 255;
+            }
+        } else {
+            data.set(array);
+        }
     } else {
         console.warn(`itemSize '${itemSize}' not supported`);
     }

+ 98 - 13
src/mol-gl/renderer.ts

@@ -2,6 +2,7 @@
  * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author Gianluca Tomasello <giagitom@gmail.com>
  */
 
 import { Viewport } from '../mol-canvas3d/camera/util';
@@ -64,12 +65,15 @@ interface Renderer {
     renderDepthTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
-    renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
+    renderBlended: (group: Scene, camera: ICamera) => void
     renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
+    renderDpoitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
+    renderDpoitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => void
+    renderDpoitVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
 
     setProps: (props: Partial<RendererProps>) => void
     setViewport: (x: number, y: number, width: number, height: number) => void
@@ -89,6 +93,7 @@ export const RendererParams = {
     interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
     interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
 
+    colorMarker: PD.Boolean(true, { description: 'Enable color marker' }),
     highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
     selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
     highlightStrength: PD.Numeric(0.3, { min: 0.0, max: 1.0, step: 0.1 }),
@@ -140,7 +145,7 @@ namespace Renderer {
     const enum Flag {
         None = 0,
         BlendedFront = 1,
-        BlendedBack = 2
+        BlendedBack = 2,
     }
 
     const enum Mask {
@@ -246,6 +251,10 @@ namespace Renderer {
                 ValueCell.update(r.values.dLightCount, light.count);
                 definesNeedUpdate = true;
             }
+            if (r.values.dColorMarker.ref.value !== p.colorMarker) {
+                ValueCell.update(r.values.dColorMarker, p.colorMarker);
+                definesNeedUpdate = true;
+            }
             if (definesNeedUpdate) r.update();
 
             const program = r.getProgram(variant);
@@ -258,11 +267,12 @@ namespace Renderer {
             if (globalUniformsNeedUpdate) {
                 // console.log('globalUniformsNeedUpdate')
                 program.setUniforms(globalUniformList);
+                program.bindTextures(sharedTexturesList, 0);
                 globalUniformsNeedUpdate = false;
             }
 
             if (r.values.dGeometryType.ref.value === 'directVolume') {
-                if (variant !== 'colorWboit' && variant !== 'colorBlended') {
+                if (variant !== 'colorDpoit' && variant !== 'colorWboit' && variant !== 'colorBlended') {
                     return; // only color supported
                 }
 
@@ -315,7 +325,7 @@ namespace Renderer {
                 }
             }
 
-            r.render(variant, sharedTexturesList);
+            r.render(variant, sharedTexturesList.length);
         };
 
         const update = (camera: ICamera) => {
@@ -353,8 +363,8 @@ namespace Renderer {
             state.colorMask(true, true, true, true);
 
             const { x, y, width, height } = viewport;
-            gl.viewport(x, y, width, height);
-            gl.scissor(x, y, width, height);
+            state.viewport(x, y, width, height);
+            state.scissor(x, y, width, height);
 
             globalUniformsNeedUpdate = true;
             state.currentRenderItemId = -1;
@@ -469,9 +479,13 @@ namespace Renderer {
             if (isTimingMode) ctx.timer.markEnd('Renderer.renderMarkingMask');
         };
 
-        const renderBlended = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
-            renderBlendedOpaque(group, camera, depthTexture);
-            renderBlendedTransparent(group, camera, depthTexture);
+        const renderBlended = (scene: Scene, camera: ICamera) => {
+            if (scene.hasOpaque) {
+                renderBlendedOpaque(scene, camera, null);
+            }
+            if (scene.opacityAverage < 1) {
+                renderBlendedTransparent(scene, camera, null);
+            }
         };
 
         const renderBlendedOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
@@ -585,13 +599,78 @@ namespace Renderer {
                 // TODO: simplify, handle in renderable.state???
                 // uAlpha is updated in "render" so we need to recompute it here
                 const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
-                if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
+                if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
                     renderObject(r, 'colorWboit', Flag.None);
                 }
             }
             if (isTimingMode) ctx.timer.markEnd('Renderer.renderWboitTransparent');
         };
 
+        const renderDpoitOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
+            if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitOpaque');
+            state.disable(gl.BLEND);
+            state.enable(gl.DEPTH_TEST);
+            state.depthMask(true);
+
+            updateInternal(group, camera, depthTexture, Mask.Opaque, false);
+
+            const { renderables } = group;
+            for (let i = 0, il = renderables.length; i < il; ++i) {
+                const r = renderables[i];
+
+                // TODO: simplify, handle in renderable.state???
+                // uAlpha is updated in "render" so we need to recompute it here
+                const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
+                if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
+                    renderObject(r, 'colorDpoit', Flag.None);
+                }
+            }
+            if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitOpaque');
+        };
+
+        const renderDpoitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, dpoitTextures: { depth: Texture, frontColor: Texture, backColor: Texture }) => {
+            if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitTransparent');
+
+            state.enable(gl.BLEND);
+
+            arrayMapUpsert(sharedTexturesList, 'tDpoitDepth', dpoitTextures.depth);
+            arrayMapUpsert(sharedTexturesList, 'tDpoitFrontColor', dpoitTextures.frontColor);
+            arrayMapUpsert(sharedTexturesList, 'tDpoitBackColor', dpoitTextures.backColor);
+
+            updateInternal(group, camera, depthTexture, Mask.Transparent, false);
+
+            const { renderables } = group;
+
+            for (let i = 0, il = renderables.length; i < il; ++i) {
+                const r = renderables[i];
+
+                // TODO: simplify, handle in renderable.state???
+                // uAlpha is updated in "render" so we need to recompute it here
+                const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
+                if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
+                    renderObject(r, 'colorDpoit', Flag.None);
+                }
+            }
+            if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitTransparent');
+        };
+
+        const renderDpoitVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
+            if (isTimingMode) ctx.timer.mark('Renderer.renderDpoitVolume');
+            state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+            state.enable(gl.BLEND);
+
+            updateInternal(group, camera, depthTexture, Mask.Transparent, false);
+
+            const { renderables } = group;
+            for (let i = 0, il = renderables.length; i < il; ++i) {
+                const r = renderables[i];
+                if (r.values.dGeometryType.ref.value === 'directVolume') {
+                    renderObject(r, 'colorDpoit', Flag.None);
+                }
+            }
+            if (isTimingMode) ctx.timer.markEnd('Renderer.renderDpoitVolume');
+        };
+
         return {
             clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
                 state.enable(gl.SCISSOR_TEST);
@@ -635,6 +714,9 @@ namespace Renderer {
             renderBlendedVolume,
             renderWboitOpaque,
             renderWboitTransparent,
+            renderDpoitOpaque,
+            renderDpoitTransparent,
+            renderDpoitVolume,
 
             setProps: (props: Partial<RendererProps>) => {
                 if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
@@ -661,6 +743,9 @@ namespace Renderer {
                     ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor));
                 }
 
+                if (props.colorMarker !== undefined && props.colorMarker !== p.colorMarker) {
+                    p.colorMarker = props.colorMarker;
+                }
                 if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
                     p.highlightColor = props.highlightColor;
                     ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor));
@@ -705,8 +790,8 @@ namespace Renderer {
                 }
             },
             setViewport: (x: number, y: number, width: number, height: number) => {
-                gl.viewport(x, y, width, height);
-                gl.scissor(x, y, width, height);
+                state.viewport(x, y, width, height);
+                state.scissor(x, y, width, height);
                 if (x !== viewport.x || y !== viewport.y || width !== viewport.width || height !== viewport.height) {
                     Viewport.set(viewport, x, y, width, height);
                     ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height));
@@ -749,4 +834,4 @@ namespace Renderer {
     }
 }
 
-export { Renderer };
+export { Renderer };

+ 32 - 4
src/mol-gl/scene.ts

@@ -45,8 +45,8 @@ function calculateBoundingSphere(renderables: GraphicsRenderable[], boundingSphe
 }
 
 function renderableSort(a: GraphicsRenderable, b: GraphicsRenderable) {
-    const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
-    const drawProgramIdB = (b.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
+    const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit') || a.getProgram('colorDpoit')).id;
+    const drawProgramIdB = (b.getProgram('colorBlended') || b.getProgram('colorWboit') || b.getProgram('colorDpoit')).id;
     const materialIdA = a.materialId;
     const materialIdB = b.materialId;
 
@@ -80,8 +80,12 @@ interface Scene extends Object3D {
     has: (o: GraphicsRenderObject) => boolean
     clear: () => void
     forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void
+    /** Marker average of primitive renderables */
     readonly markerAverage: number
+    /** Opacity average of primitive renderables */
     readonly opacityAverage: number
+    /** Is `true` if any primitive renderable (possibly) has any opaque part */
+    readonly hasOpaque: boolean
 }
 
 namespace Scene {
@@ -103,6 +107,7 @@ namespace Scene {
 
         let markerAverage = 0;
         let opacityAverage = 0;
+        let hasOpaque = false;
 
         const object3d = Object3D.create();
         const { view, position, direction, up } = object3d;
@@ -160,7 +165,9 @@ namespace Scene {
             }
 
             renderables.sort(renderableSort);
+            markerAverage = calculateMarkerAverage();
             opacityAverage = calculateOpacityAverage();
+            hasOpaque = calculateHasOpaque();
             return true;
         }
 
@@ -182,7 +189,10 @@ namespace Scene {
             const newVisibleHash = computeVisibleHash();
             if (newVisibleHash !== visibleHash) {
                 boundingSphereVisibleDirty = true;
+                markerAverage = calculateMarkerAverage();
                 opacityAverage = calculateOpacityAverage();
+                hasOpaque = calculateHasOpaque();
+                visibleHash = newVisibleHash;
                 return true;
             } else {
                 return false;
@@ -212,12 +222,27 @@ namespace Scene {
                 // uAlpha is updated in "render" so we need to recompute it here
                 const alpha = clamp(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1);
                 const xray = p.values.dXrayShaded?.ref.value ? 0.5 : 1;
-                opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha * xray;
+                const fuzzy = p.values.dPointStyle?.ref.value === 'fuzzy' ? 0.5 : 1;
+                const text = p.values.dGeometryType.ref.value === 'text' ? 0.5 : 1;
+                opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha * xray * fuzzy * text;
                 count += 1;
             }
             return count > 0 ? opacityAverage / count : 0;
         }
 
+        function calculateHasOpaque() {
+            if (primitives.length === 0) return false;
+            for (let i = 0, il = primitives.length; i < il; ++i) {
+                const p = primitives[i];
+                if (!p.state.visible) continue;
+
+                if (p.state.opaque) return true;
+                if (p.state.alphaFactor === 1 && p.values.alpha.ref.value === 1 && p.values.transparencyAverage.ref.value !== 1) return true;
+                if (p.values.dTransparentBackfaces?.ref.value === 'opaque') return true;
+            }
+            return false;
+        }
+
         return {
             view, position, direction, up,
 
@@ -245,6 +270,7 @@ namespace Scene {
                 }
                 markerAverage = calculateMarkerAverage();
                 opacityAverage = calculateOpacityAverage();
+                hasOpaque = calculateHasOpaque();
             },
             add: (o: GraphicsRenderObject) => commitQueue.add(o),
             remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
@@ -281,7 +307,6 @@ namespace Scene {
                 if (boundingSphereVisibleDirty) {
                     calculateBoundingSphere(renderables, boundingSphereVisible, true);
                     boundingSphereVisibleDirty = false;
-                    visibleHash = computeVisibleHash();
                 }
                 return boundingSphereVisible;
             },
@@ -291,6 +316,9 @@ namespace Scene {
             get opacityAverage() {
                 return opacityAverage;
             },
+            get hasOpaque() {
+                return hasOpaque;
+            },
         };
     }
 }

+ 5 - 1
src/mol-gl/shader-code.ts

@@ -67,6 +67,7 @@ import { texture3d_from_1d_trilinear } from './shader/chunks/texture3d-from-1d-t
 import { texture3d_from_2d_linear } from './shader/chunks/texture3d-from-2d-linear.glsl';
 import { texture3d_from_2d_nearest } from './shader/chunks/texture3d-from-2d-nearest.glsl';
 import { wboit_write } from './shader/chunks/wboit-write.glsl';
+import { dpoit_write } from './shader/chunks/dpoit-write.glsl';
 
 const ShaderChunks: { [k: string]: string } = {
     apply_fog,
@@ -99,7 +100,8 @@ const ShaderChunks: { [k: string]: string } = {
     texture3d_from_1d_trilinear,
     texture3d_from_2d_linear,
     texture3d_from_2d_nearest,
-    wboit_write
+    wboit_write,
+    dpoit_write
 };
 
 const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gm;
@@ -292,7 +294,9 @@ const glsl300VertPrefixCommon = `
 const glsl300FragPrefixCommon = `
 #define varying in
 #define texture2D texture
+#define textureCube texture
 #define texture2DLodEXT textureLod
+#define textureCubeLodEXT textureLod
 
 #define gl_FragColor out_FragData0
 #define gl_FragDepthEXT gl_FragDepth

+ 90 - 0
src/mol-gl/shader/background.frag.ts

@@ -0,0 +1,90 @@
+export const background_frag = `
+precision mediump float;
+precision mediump samplerCube;
+precision mediump sampler2D;
+
+#if defined(dVariant_skybox)
+    uniform samplerCube tSkybox;
+    uniform mat4 uViewDirectionProjectionInverse;
+    uniform float uBlur;
+    uniform float uOpacity;
+    uniform float uSaturation;
+    uniform float uLightness;
+#elif defined(dVariant_image)
+    uniform sampler2D tImage;
+    uniform vec2 uImageScale;
+    uniform vec2 uImageOffset;
+    uniform float uOpacity;
+    uniform float uSaturation;
+    uniform float uLightness;
+#elif defined(dVariant_horizontalGradient) || defined(dVariant_radialGradient)
+    uniform vec3 uGradientColorA;
+    uniform vec3 uGradientColorB;
+    uniform float uGradientRatio;
+#endif
+
+uniform vec2 uTexSize;
+uniform vec4 uViewport;
+uniform bool uViewportAdjusted;
+varying vec4 vPosition;
+
+// TODO: add as general pp option to remove banding?
+// Iestyn's RGB dither from http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
+vec3 ScreenSpaceDither(vec2 vScreenPos) {
+    vec3 vDither = vec3(dot(vec2(171.0, 231.0), vScreenPos.xy));
+    vDither.rgb = fract(vDither.rgb / vec3(103.0, 71.0, 97.0));
+    return vDither.rgb / 255.0;
+}
+
+vec3 saturateColor(vec3 c, float amount) {
+    // https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
+    const vec3 W = vec3(0.2125, 0.7154, 0.0721);
+    vec3 intensity = vec3(dot(c, W));
+    return mix(intensity, c, 1.0 + amount);
+}
+
+vec3 lightenColor(vec3 c, float amount) {
+    return c + amount;
+}
+
+void main() {
+    #if defined(dVariant_skybox)
+        vec4 t = uViewDirectionProjectionInverse * vPosition;
+        #ifdef enabledShaderTextureLod
+            gl_FragColor = textureCubeLodEXT(tSkybox, normalize(t.xyz / t.w), uBlur * 8.0);
+        #else
+            gl_FragColor = textureCube(tSkybox, normalize(t.xyz / t.w));
+        #endif
+        gl_FragColor.a = uOpacity;
+        gl_FragColor.rgb = lightenColor(saturateColor(gl_FragColor.rgb, uSaturation), uLightness);
+    #elif defined(dVariant_image)
+        vec2 coords;
+        if (uViewportAdjusted) {
+            coords = ((gl_FragCoord.xy - uViewport.xy) * (uTexSize / uViewport.zw) / uImageScale) + uImageOffset;
+        } else {
+            coords = (gl_FragCoord.xy / uImageScale) + uImageOffset;
+        }
+        gl_FragColor = texture2D(tImage, vec2(coords.x, 1.0 - coords.y));
+        gl_FragColor.a = uOpacity;
+        gl_FragColor.rgb = lightenColor(saturateColor(gl_FragColor.rgb, uSaturation), uLightness);
+    #elif defined(dVariant_horizontalGradient)
+        float d;
+        if (uViewportAdjusted) {
+            d = ((gl_FragCoord.y - uViewport.y) * (uTexSize.y / uViewport.w) / uTexSize.y) + 1.0 - (uGradientRatio * 2.0);
+        } else {
+            d = (gl_FragCoord.y / uTexSize.y) + 1.0 - (uGradientRatio * 2.0);
+        }
+        gl_FragColor = vec4(mix(uGradientColorB, uGradientColorA, clamp(d, 0.0, 1.0)), 1.0);
+        gl_FragColor.rgb += ScreenSpaceDither(gl_FragCoord.xy);
+    #elif defined(dVariant_radialGradient)
+        float d;
+        if (uViewportAdjusted) {
+            d = distance(vec2(0.5), (gl_FragCoord.xy - uViewport.xy) * (uTexSize / uViewport.zw) / uTexSize) + uGradientRatio - 0.5;
+        } else {
+            d = distance(vec2(0.5), gl_FragCoord.xy / uTexSize) + uGradientRatio - 0.5;
+        }
+        gl_FragColor = vec4(mix(uGradientColorB, uGradientColorA, 1.0 - clamp(d, 0.0, 1.0)), 1.0);
+        gl_FragColor.rgb += ScreenSpaceDither(gl_FragCoord.xy);
+    #endif
+}
+`;

+ 12 - 0
src/mol-gl/shader/background.vert.ts

@@ -0,0 +1,12 @@
+export const background_vert = `
+precision mediump float;
+
+attribute vec2 aPosition;
+
+varying vec4 vPosition;
+
+void main() {
+    vPosition = vec4(aPosition, 1.0, 1.0);
+    gl_Position = vec4(aPosition, 1.0, 1.0);
+}
+`;

+ 20 - 0
src/mol-gl/shader/blend-back-dpoit.frag.ts

@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Gianluca Tomasello <giagitom@gmail.com>
+ */
+
+export const blendBackDpoit_frag = `
+    precision highp float;
+
+    uniform sampler2D tDpoitBackColor;
+    uniform vec2 uTexSize;
+
+    void main() {
+        vec2 coords = gl_FragCoord.xy / uTexSize;
+        gl_FragColor = texture2D(tDpoitBackColor, coords);
+        if (gl_FragColor.a == 0.0) {
+            discard;
+        }
+    }
+`;

+ 14 - 3
src/mol-gl/shader/chunks/apply-fog.glsl.ts

@@ -12,8 +12,19 @@ if (!uTransparentBackground) {
         gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
     }
 } else {
-    // pre-multiplied alpha expected for transparent background
-    gl_FragColor.rgb *= fogAlpha;
-    gl_FragColor.a = fogAlpha;
+    #if defined(dRenderVariant_colorDpoit)
+        if (gl_FragColor.a < 1.0) {
+            // transparent objects are blended with background color
+            gl_FragColor.a = fogAlpha;
+        } else {
+            // opaque objects need to be pre-multiplied alpha
+            gl_FragColor.rgb *= fogAlpha;
+            gl_FragColor.a = fogAlpha;
+        }
+    #else
+        // pre-multiplied alpha expected for transparent background
+        gl_FragColor.rgb *= fogAlpha;
+        gl_FragColor.a = fogAlpha;
+    #endif
 }
 `;

+ 2 - 8
src/mol-gl/shader/chunks/apply-light-color.glsl.ts

@@ -13,14 +13,7 @@ export const apply_light_color = `
 #else
     #ifdef bumpEnabled
         if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
-            vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
-            #ifdef enabledFragDepth
-                if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
-                    normal = bumpNormal;
-                }
-            #else
-                normal = bumpNormal;
-            #endif
+            normal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
         }
     #endif
 
@@ -64,6 +57,7 @@ export const apply_light_color = `
     RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
 
     vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
+    outgoingLight = clamp(outgoingLight, 0.01, 0.99); // prevents black artifacts on specular highlight with transparent background
 
     gl_FragColor = vec4(outgoingLight, color.a);
 #endif

+ 10 - 8
src/mol-gl/shader/chunks/apply-marker-color.glsl.ts

@@ -1,11 +1,13 @@
 export const apply_marker_color = `
-if (marker > 0.0) {
-    if ((uMarkerPriority == 1 && marker != 2.0) || (uMarkerPriority != 1 && marker == 1.0)) {
-        gl_FragColor.rgb = mix(gl_FragColor.rgb, uHighlightColor, uHighlightStrength);
-        gl_FragColor.a = max(gl_FragColor.a, uHighlightStrength * 0.002); // for direct-volume rendering
-    } else {
-        gl_FragColor.rgb = mix(gl_FragColor.rgb, uSelectColor, uSelectStrength);
-        gl_FragColor.a = max(gl_FragColor.a, uSelectStrength * 0.002); // for direct-volume rendering
+#if defined(dColorMarker)
+    if (marker > 0.0) {
+        if ((uMarkerPriority == 1 && marker != 2.0) || (uMarkerPriority != 1 && marker == 1.0)) {
+            gl_FragColor.rgb = mix(gl_FragColor.rgb, uHighlightColor, uHighlightStrength);
+            gl_FragColor.a = max(gl_FragColor.a, uHighlightStrength * 0.002); // for direct-volume rendering
+        } else {
+            gl_FragColor.rgb = mix(gl_FragColor.rgb, uSelectColor, uSelectStrength);
+            gl_FragColor.a = max(gl_FragColor.a, uSelectStrength * 0.002); // for direct-volume rendering
+        }
     }
-}
+#endif
 `;

+ 1 - 1
src/mol-gl/shader/chunks/assign-marker-varying.glsl.ts

@@ -1,5 +1,5 @@
 export const assign_marker_varying = `
-#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
+#if defined(dNeedsMarker)
     #if defined(dMarkerType_instance)
         vMarker = readFromTexture(tMarker, aInstance, uMarkerTexDim).a;
     #elif defined(dMarkerType_groupInstance)

+ 2 - 2
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -1,5 +1,5 @@
 export const assign_material_color = `
-#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
+#if defined(dNeedsMarker)
     float marker = uMarker;
     if (uMarker == -1.0) {
         marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
@@ -86,7 +86,7 @@ export const assign_material_color = `
 // apply per-group transparency
 #if defined(dTransparency) && (defined(dRenderVariant_pick) || defined(dRenderVariant_color))
     float ta = 1.0 - vTransparency;
-    if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better
+    if (vTransparency < 0.09) ta = 1.0; // hard cutoff looks better
 
     #if defined(dRenderVariant_pick)
         if (ta < uPickingAlphaThreshold)

+ 1 - 1
src/mol-gl/shader/chunks/color-frag-params.glsl.ts

@@ -27,7 +27,7 @@ uniform float uBumpiness;
         varying vec4 vSubstance;
     #endif
 #elif defined(dRenderVariant_pick)
-    #if __VERSION__ == 100
+    #if __VERSION__ == 100 || !defined(dVaryingGroup)
         #ifdef requiredDrawBuffers
             varying vec4 vObject;
             varying vec4 vInstance;

+ 1 - 1
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -55,7 +55,7 @@ uniform float uBumpiness;
         #endif
     #endif
 #elif defined(dRenderVariant_pick)
-    #if __VERSION__ == 100
+    #if __VERSION__ == 100 || !defined(dVaryingGroup)
         #ifdef requiredDrawBuffers
             varying vec4 vObject;
             varying vec4 vInstance;

+ 16 - 8
src/mol-gl/shader/chunks/common-frag-params.glsl.ts

@@ -14,7 +14,7 @@ uniform int uMarkingType;
     uniform vec3 uClipObjectScale[dClipObjectCount];
 
     #if defined(dClipping)
-        #if __VERSION__ == 100
+        #if __VERSION__ == 100 || defined(dClippingType_instance) || !defined(dVaryingGroup)
             varying float vClipping;
         #else
             flat in float vClipping;
@@ -22,21 +22,29 @@ uniform int uMarkingType;
     #endif
 #endif
 
-uniform vec3 uHighlightColor;
-uniform vec3 uSelectColor;
-uniform float uHighlightStrength;
-uniform float uSelectStrength;
-uniform int uMarkerPriority;
+#if defined(dColorMarker)
+    uniform vec3 uHighlightColor;
+    uniform vec3 uSelectColor;
+    uniform float uHighlightStrength;
+    uniform float uSelectStrength;
+    uniform int uMarkerPriority;
+#endif
 
-#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
+#if defined(dNeedsMarker)
     uniform float uMarker;
-    #if __VERSION__ == 100
+    #if __VERSION__ == 100 || defined(dMarkerType_instance) || !defined(dVaryingGroup)
         varying float vMarker;
     #else
         flat in float vMarker;
     #endif
 #endif
 
+#if defined(dRenderVariant_colorDpoit)
+    #define MAX_DPOIT_DEPTH 99999.0 // NOTE constant also set in TypeScript
+    uniform sampler2D tDpoitDepth;
+    uniform sampler2D tDpoitFrontColor;
+#endif
+
 varying vec3 vModelPosition;
 varying vec3 vViewPosition;
 

+ 6 - 4
src/mol-gl/shader/chunks/common-vert-params.glsl.ts

@@ -21,7 +21,7 @@ uniform int uPickType;
     #if defined(dClipping)
         uniform vec2 uClippingTexDim;
         uniform sampler2D tClipping;
-        #if __VERSION__ == 100
+        #if __VERSION__ == 100 || defined(dClippingType_instance) || !defined(dVaryingGroup)
             varying float vClipping;
         #else
             flat out float vClipping;
@@ -29,11 +29,11 @@ uniform int uPickType;
     #endif
 #endif
 
-#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
+#if defined(dNeedsMarker)
     uniform float uMarker;
     uniform vec2 uMarkerTexDim;
     uniform sampler2D tMarker;
-    #if __VERSION__ == 100
+    #if __VERSION__ == 100 || defined(dMarkerType_instance) || !defined(dVaryingGroup)
         varying float vMarker;
     #else
         flat out float vMarker;
@@ -44,7 +44,9 @@ varying vec3 vModelPosition;
 varying vec3 vViewPosition;
 
 #if defined(noNonInstancedActiveAttribs)
-    #define VertexID gl_VertexID
+    // int() is needed for some Safari versions
+    // see https://bugs.webkit.org/show_bug.cgi?id=244152
+    #define VertexID int(gl_VertexID)
 #else
     attribute float aVertex;
     #define VertexID int(aVertex)

+ 5 - 1
src/mol-gl/shader/chunks/common.glsl.ts

@@ -1,7 +1,7 @@
 export const common = `
 // TODO find a better place for these convenience defines
 
-#if defined(dRenderVariant_colorBlended) || defined(dRenderVariant_colorWboit)
+#if defined(dRenderVariant_colorBlended) || defined(dRenderVariant_colorWboit) || defined(dRenderVariant_colorDpoit)
     #define dRenderVariant_color
 #endif
 
@@ -17,6 +17,10 @@ export const common = `
     #define dColorType_varying
 #endif
 
+#if (defined(dRenderVariant_color) && defined(dColorMarker)) || defined(dRenderVariant_marking)
+    #define dNeedsMarker
+#endif
+
 #define MaskAll 0
 #define MaskOpaque 1
 #define MaskTransparent 2

+ 70 - 0
src/mol-gl/shader/chunks/dpoit-write.glsl.ts

@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Gianluca Tomasello <giagitom@gmail.com>
+ */
+
+export const dpoit_write = `
+#if defined(dRenderVariant_colorDpoit)
+    if (uRenderMask == MaskOpaque) {
+        if (preFogAlpha < 1.0) {
+            discard;
+        }
+    } else if (uRenderMask == MaskTransparent) {
+        // the 'fragmentDepth > 0.99' check is to handle precision issues with packed depth
+        vec2 coords = gl_FragCoord.xy / uDrawingBufferSize;
+        if (preFogAlpha != 1.0 && (fragmentDepth < getDepth(coords) || fragmentDepth > 0.99)) {
+            #ifdef dTransparentBackfaces_off
+                if (interior) discard;
+            #endif
+
+            // adapted from https://github.com/tsherif/webgl2examples
+            // The MIT License, Copyright 2017 Tarek Sherif, Shuai Shao
+
+            vec2 lastDepth = texture2D(tDpoitDepth, coords).rg;
+            vec4 lastFrontColor = texture2D(tDpoitFrontColor, coords);
+
+            vec4 fragColor = gl_FragColor;
+
+            // depth value always increases
+            // so we can use MAX blend equation
+            gl_FragData[2].rg = vec2(-MAX_DPOIT_DEPTH);
+
+            // front color always increases
+            // so we can use MAX blend equation
+            gl_FragColor = lastFrontColor;
+
+            // back color is separately blend afterwards each pass
+            gl_FragData[1] = vec4(0.0);
+
+            float nearestDepth = -lastDepth.x;
+            float furthestDepth = lastDepth.y;
+            float alphaMultiplier = 1.0 - lastFrontColor.a;
+
+            if (fragmentDepth < nearestDepth || fragmentDepth > furthestDepth) {
+                // Skip this depth since it's been peeled.
+                return;
+            }
+
+            if (fragmentDepth > nearestDepth && fragmentDepth < furthestDepth) {
+                // This needs to be peeled.
+                // The ones remaining after MAX blended for
+                // all need-to-peel will be peeled next pass.
+                gl_FragData[2].rg = vec2(-fragmentDepth, fragmentDepth);
+                return;
+            }
+
+            // write to back and front color buffer
+            if (fragmentDepth == nearestDepth) {
+                gl_FragColor.rgb += fragColor.rgb * fragColor.a * alphaMultiplier;
+                gl_FragColor.a = 1.0 - alphaMultiplier * (1.0 - fragColor.a);
+            } else {
+                gl_FragData[1] += fragColor;
+            }
+
+        } else {
+            discard;
+        }
+    }
+#endif
+`;

+ 1 - 2
src/mol-gl/shader/compute/color-smoothing/accumulate.vert.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -10,7 +10,6 @@ precision highp float;
 #include common
 #include read_from_texture
 
-uniform int uTotalCount;
 uniform int uGroupCount;
 
 attribute float aSample;

+ 7 - 6
src/mol-gl/shader/cylinders.frag.ts

@@ -109,14 +109,14 @@ void main() {
 
     vec3 vViewPosition = vModelPosition + intersection.x * rayDir;
     vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
-    gl_FragDepthEXT = calcDepth(vViewPosition);
+    float fragmentDepth = calcDepth(vViewPosition);
 
-    vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
+    if (fragmentDepth < 0.0) discard;
+    if (fragmentDepth > 1.0) discard;
 
-    if (gl_FragDepthEXT < 0.0) discard;
-    if (gl_FragDepthEXT > 1.0) discard;
+    gl_FragDepthEXT = fragmentDepth;
 
-    float fragmentDepth = gl_FragDepthEXT;
+    vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
     #include assign_material_color
 
     #if defined(dRenderVariant_pick)
@@ -142,6 +142,7 @@ void main() {
         #include apply_marker_color
         #include apply_fog
         #include wboit_write
+        #include dpoit_write
     #endif
 }
-`;
+`;

+ 19 - 15
src/mol-gl/shader/direct-volume.frag.ts

@@ -50,15 +50,17 @@ uniform int uVertexCount;
 uniform int uInstanceCount;
 uniform int uGroupCount;
 
-uniform vec3 uHighlightColor;
-uniform vec3 uSelectColor;
-uniform float uHighlightStrength;
-uniform float uSelectStrength;
-uniform int uMarkerPriority;
-
-uniform float uMarker;
-uniform vec2 uMarkerTexDim;
-uniform sampler2D tMarker;
+#if defined(dColorMarker)
+    uniform vec3 uHighlightColor;
+    uniform vec3 uSelectColor;
+    uniform float uHighlightStrength;
+    uniform float uSelectStrength;
+    uniform int uMarkerPriority;
+
+    uniform float uMarker;
+    uniform vec2 uMarkerTexDim;
+    uniform sampler2D tMarker;
+#endif
 
 uniform float uMetalness;
 uniform float uRoughness;
@@ -304,11 +306,13 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
 
         gl_FragColor.a = material.a * uAlpha * uTransferScale;
 
-        float marker = uMarker;
-        if (uMarker == -1.0) {
-            marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
-            marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
-        }
+        #if defined(dColorMarker)
+            float marker = uMarker;
+            if (uMarker == -1.0) {
+                marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
+                marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
+            }
+        #endif
         #include apply_marker_color
 
         preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;
@@ -352,4 +356,4 @@ void main() {
     float preFogAlpha = clamp(preFogAlphaBlended, 0.0, 1.0);
     #include wboit_write
 }
-`;
+`;

+ 17 - 0
src/mol-gl/shader/evaluate-dpoit.frag.ts

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Gianluca Tomasello <giagitom@gmail.com>
+ */
+
+export const evaluateDpoit_frag = `
+precision highp float;
+
+uniform sampler2D tDpoitFrontColor;
+uniform vec2 uTexSize;
+
+void main() {
+    vec2 coords = gl_FragCoord.xy / uTexSize;
+    gl_FragColor = texture2D(tDpoitFrontColor, coords);
+}
+`;

+ 2 - 1
src/mol-gl/shader/image.frag.ts

@@ -159,6 +159,7 @@ void main() {
         #include apply_marker_color
         #include apply_fog
         #include wboit_write
+        #include dpoit_write
     #endif
 }
-`;
+`;

+ 2 - 1
src/mol-gl/shader/lines.frag.ts

@@ -39,6 +39,7 @@ void main(){
         #include apply_marker_color
         #include apply_fog
         #include wboit_write
+        #include dpoit_write
     #endif
 }
-`;
+`;

+ 2 - 2
src/mol-gl/shader/marching-cubes/isosurface.frag.ts

@@ -268,9 +268,9 @@ void main(void) {
     gl_FragData[0].xyz = (uGridTransform * vec4(b0 + t * (b0 - b1), 1.0)).xyz;
 
     // group id
-    #if __VERSION__ == 100
+    #if __VERSION__ == 100 || defined(dConstantGroup)
         // webgl1 does not support 'flat' interpolation (i.e. no interpolation)
-        // so we ensure a constant group id per triangle here
+        // ensure a constant group id per triangle as needed
         #ifdef dPackedGroup
             gl_FragData[1] = vec4(voxel(coord3d).rgb, 1.0);
         #else

+ 2 - 1
src/mol-gl/shader/mesh.frag.ts

@@ -62,6 +62,7 @@ void main() {
         #include apply_marker_color
         #include apply_fog
         #include wboit_write
+        #include dpoit_write
     #endif
 }
-`;
+`;

Some files were not shown because too many files changed in this diff