Ver código fonte

Merge branch 'master' of https://github.com/molstar/molstar into forkdev

# Conflicts:
#	src/apps/viewer/extensions/cellpack/model.ts
DESKTOP-O6LIMN9\ludov 5 anos atrás
pai
commit
35bd0ec5d8
73 arquivos alterados com 3122 adições e 1040 exclusões
  1. 18 0
      .github/workflows/lint.yml
  2. 9 6
      README.md
  3. 88 0
      data/cif-field-names/bird-field-names.csv
  4. 60 0
      data/cif-field-names/ccd-field-names.csv
  5. 60 0
      data/cif-field-names/cif-core-field-names.csv
  6. 805 0
      data/cif-field-names/mmcif-field-names.csv
  7. 76 0
      data/cif-field-names/mmtf-filter.csv
  8. 1 1
      data/rcsb-graphql/codegen.yml
  9. 1 1
      package-lock.json
  10. 3 1
      package.json
  11. 0 30
      src/apps/basic-wrapper/controls.tsx
  12. 0 99
      src/apps/basic-wrapper/helpers.ts
  13. 0 219
      src/apps/basic-wrapper/index.ts
  14. 0 108
      src/apps/basic-wrapper/superposition.ts
  15. 119 0
      src/apps/cif2bcif/converter.ts
  16. 67 0
      src/apps/cif2bcif/index.ts
  17. 274 0
      src/apps/cifschema/index.ts
  18. 446 0
      src/apps/cifschema/util/cif-dic.ts
  19. 151 0
      src/apps/cifschema/util/generate.ts
  20. 20 0
      src/apps/cifschema/util/helper.ts
  21. 77 0
      src/apps/cifschema/util/schema.ts
  22. 0 178
      src/apps/demos/lighting/index.ts
  23. 57 107
      src/apps/viewer/extensions/cellpack/model.ts
  24. 11 3
      src/apps/viewer/extensions/cellpack/state.ts
  25. 5 3
      src/apps/viewer/extensions/cellpack/util.ts
  26. 0 0
      src/examples/basic-wrapper/coloring.ts
  27. 16 0
      src/examples/basic-wrapper/controls.tsx
  28. 3 2
      src/examples/basic-wrapper/index.html
  29. 155 0
      src/examples/basic-wrapper/index.ts
  30. 118 0
      src/examples/basic-wrapper/superposition.ts
  31. 0 0
      src/examples/domain-annotation-server/mapping.ts
  32. 0 0
      src/examples/domain-annotation-server/schemas.ts
  33. 1 1
      src/examples/domain-annotation-server/server.ts
  34. 0 0
      src/examples/domain-annotation-server/test.ts
  35. 0 0
      src/examples/lighting/index.html
  36. 117 0
      src/examples/lighting/index.ts
  37. 37 11
      src/mol-canvas3d/canvas3d.ts
  38. 3 4
      src/mol-model-props/computed/interactions/hydrogen-bonds.ts
  39. 1 1
      src/mol-model-props/computed/interactions/metal.ts
  40. 3 2
      src/mol-model-props/computed/representations/interactions.ts
  41. 1 1
      src/mol-model-props/rcsb/assembly-symmetry.ts
  42. 3 1
      src/mol-model-props/rcsb/graphql/types.ts
  43. 2 1
      src/mol-model-props/rcsb/representations/validation-report-clashes.ts
  44. 2 2
      src/mol-plugin-state/builder/structure.ts
  45. 1 1
      src/mol-plugin-state/builder/structure/representation-preset.ts
  46. 3 0
      src/mol-plugin-state/manager/loci-label.ts
  47. 1 1
      src/mol-plugin-state/manager/structure/hierarchy.ts
  48. 6 1
      src/mol-plugin-ui/controls.tsx
  49. 2 1
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts
  50. 9 8
      src/mol-plugin/behavior/dynamic/custom-props/rcsb/ui/assembly-symmetry.tsx
  51. 2 1
      src/mol-plugin/behavior/dynamic/representation.ts
  52. 8 1
      src/mol-plugin/behavior/static/misc.ts
  53. 2 1
      src/mol-plugin/commands.ts
  54. 7 2
      src/mol-plugin/context.ts
  55. 2 1
      src/mol-repr/structure/complex-representation.ts
  56. 1 1
      src/mol-repr/structure/complex-visual.ts
  57. 47 0
      src/mol-repr/structure/params.ts
  58. 1 39
      src/mol-repr/structure/representation.ts
  59. 2 1
      src/mol-repr/structure/representation/ball-and-stick.ts
  60. 1 0
      src/mol-repr/structure/representation/cartoon.ts
  61. 2 1
      src/mol-repr/structure/representation/ellipsoid.ts
  62. 2 1
      src/mol-repr/structure/units-representation.ts
  63. 1 1
      src/mol-repr/structure/units-visual.ts
  64. 173 0
      src/mol-script/runtime/query/base.ts
  65. 1 168
      src/mol-script/runtime/query/compiler.ts
  66. 4 3
      src/mol-script/runtime/query/table.ts
  67. 10 0
      src/mol-state/state.ts
  68. 2 2
      src/servers/model/server.ts
  69. 3 3
      src/servers/plugin-state/index.ts
  70. 2 2
      src/servers/volume/server.ts
  71. 3 1
      tsconfig.json
  72. 2 0
      webpack.config.common.js
  73. 12 17
      webpack.config.js

+ 18 - 0
.github/workflows/lint.yml

@@ -0,0 +1,18 @@
+on:
+  push:
+  pull_request:
+
+jobs:
+  eslint:
+    name: eslint
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - name: install node v12
+      uses: actions/setup-node@v1
+      with:
+        node-version: 12
+    - name: yarn install
+      run: yarn install
+    - name: eslint
+      uses: icrawl/action-eslint@v1

+ 9 - 6
README.md

@@ -89,12 +89,11 @@ and navigate to `build/viewer`
 
 ### Code generation
 **CIF schemas**
-Install CIFTools `npm install ciftools -g`
 
-    cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
-    cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
-    cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
-    cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
+    node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
+    node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
+    node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
+    node ./lib/apps/cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
 
 **GraphQL schemas**
 
@@ -103,7 +102,7 @@ Install CIFTools `npm install ciftools -g`
 ### Other scripts
 **Create chem comp bond table**
 
-    export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
+    node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
 
 **Test model server**
 
@@ -119,6 +118,10 @@ Install CIFTools `npm install ciftools -g`
 
 To see all available commands, use ``node build/model-server/preprocess -h``.
 
+Or
+
+    node ./lib/apps/cif2bcif
+
 ## Development
 
 ### Installation

+ 88 - 0
data/cif-field-names/bird-field-names.csv

@@ -0,0 +1,88 @@
+pdbx_reference_molecule.prd_id
+pdbx_reference_molecule.name
+pdbx_reference_molecule.represent_as
+pdbx_reference_molecule.type
+pdbx_reference_molecule.type_evidence_code
+pdbx_reference_molecule.class
+pdbx_reference_molecule.class_evidence_code
+pdbx_reference_molecule.formula
+pdbx_reference_molecule.chem_comp_id
+pdbx_reference_molecule.formula_weight
+pdbx_reference_molecule.release_status
+pdbx_reference_molecule.replaces
+pdbx_reference_molecule.replaced_by
+pdbx_reference_molecule.compound_details
+pdbx_reference_molecule.description
+pdbx_reference_molecule.representative_PDB_id_code
+
+pdbx_reference_entity_list.prd_id
+pdbx_reference_entity_list.ref_entity_id
+pdbx_reference_entity_list.component_id
+pdbx_reference_entity_list.type
+pdbx_reference_entity_list.details
+
+pdbx_reference_entity_nonpoly.prd_id
+pdbx_reference_entity_nonpoly.ref_entity_id
+pdbx_reference_entity_nonpoly.name
+pdbx_reference_entity_nonpoly.chem_comp_id
+
+pdbx_reference_entity_link.prd_id
+pdbx_reference_entity_link.link_id
+pdbx_reference_entity_link.link_class
+pdbx_reference_entity_link.ref_entity_id_1
+pdbx_reference_entity_link.entity_seq_num_1
+pdbx_reference_entity_link.comp_id_1
+pdbx_reference_entity_link.atom_id_1
+pdbx_reference_entity_link.ref_entity_id_2
+pdbx_reference_entity_link.entity_seq_num_2
+pdbx_reference_entity_link.comp_id_2
+pdbx_reference_entity_link.atom_id_2
+pdbx_reference_entity_link.value_order
+pdbx_reference_entity_link.component_1
+pdbx_reference_entity_link.component_2
+pdbx_reference_entity_link.details
+
+pdbx_reference_entity_poly_link.prd_id
+pdbx_reference_entity_poly_link.ref_entity_id
+pdbx_reference_entity_poly_link.link_id
+pdbx_reference_entity_poly_link.atom_id_1
+pdbx_reference_entity_poly_link.comp_id_1
+pdbx_reference_entity_poly_link.entity_seq_num_1
+pdbx_reference_entity_poly_link.atom_id_2
+pdbx_reference_entity_poly_link.comp_id_2
+pdbx_reference_entity_poly_link.entity_seq_num_2
+pdbx_reference_entity_poly_link.value_order
+pdbx_reference_entity_poly_link.component_id
+
+pdbx_reference_entity_poly.prd_id
+pdbx_reference_entity_poly.ref_entity_id
+pdbx_reference_entity_poly.db_code
+pdbx_reference_entity_poly.db_name
+pdbx_reference_entity_poly.type
+
+pdbx_reference_entity_sequence.prd_id
+pdbx_reference_entity_sequence.ref_entity_id
+pdbx_reference_entity_sequence.type
+pdbx_reference_entity_sequence.NRP_flag
+pdbx_reference_entity_sequence.one_letter_codes
+
+pdbx_reference_entity_poly_seq.prd_id
+pdbx_reference_entity_poly_seq.ref_entity_id
+pdbx_reference_entity_poly_seq.num
+pdbx_reference_entity_poly_seq.mon_id
+pdbx_reference_entity_poly_seq.parent_mon_id
+pdbx_reference_entity_poly_seq.hetero
+pdbx_reference_entity_poly_seq.observed
+
+pdbx_reference_entity_src_nat.prd_id
+pdbx_reference_entity_src_nat.ref_entity_id
+pdbx_reference_entity_src_nat.ordinal
+pdbx_reference_entity_src_nat.taxid
+pdbx_reference_entity_src_nat.organism_scientific
+pdbx_reference_entity_src_nat.db_code
+pdbx_reference_entity_src_nat.db_name
+
+pdbx_prd_audit.prd_id
+pdbx_prd_audit.date
+pdbx_prd_audit.processing_site
+pdbx_prd_audit.action_type

+ 60 - 0
data/cif-field-names/ccd-field-names.csv

@@ -0,0 +1,60 @@
+chem_comp.id
+chem_comp.name
+chem_comp.type
+chem_comp.pdbx_type
+chem_comp.formula
+chem_comp.mon_nstd_parent_comp_id
+chem_comp.pdbx_synonyms
+chem_comp.pdbx_formal_charge
+chem_comp.pdbx_initial_date
+chem_comp.pdbx_modified_date
+chem_comp.pdbx_ambiguous_flag
+chem_comp.pdbx_release_status
+chem_comp.pdbx_replaced_by
+chem_comp.pdbx_replaces
+chem_comp.formula_weight
+chem_comp.one_letter_code
+chem_comp.three_letter_code
+chem_comp.pdbx_model_coordinates_details
+chem_comp.pdbx_model_coordinates_missing_flag
+chem_comp.pdbx_ideal_coordinates_details
+chem_comp.pdbx_ideal_coordinates_missing_flag
+chem_comp.pdbx_model_coordinates_db_code
+chem_comp.pdbx_processing_site
+
+chem_comp_atom.comp_id
+chem_comp_atom.atom_id
+chem_comp_atom.alt_atom_id
+chem_comp_atom.type_symbol
+chem_comp_atom.charge
+chem_comp_atom.pdbx_align
+chem_comp_atom.pdbx_aromatic_flag
+chem_comp_atom.pdbx_leaving_atom_flag
+chem_comp_atom.pdbx_stereo_config
+chem_comp_atom.model_Cartn_x
+chem_comp_atom.model_Cartn_y
+chem_comp_atom.model_Cartn_z
+chem_comp_atom.pdbx_model_Cartn_x_ideal
+chem_comp_atom.pdbx_model_Cartn_y_ideal
+chem_comp_atom.pdbx_model_Cartn_z_ideal
+chem_comp_atom.pdbx_ordinal
+
+chem_comp_bond.comp_id
+chem_comp_bond.atom_id_1
+chem_comp_bond.atom_id_2
+chem_comp_bond.value_order
+chem_comp_bond.pdbx_aromatic_flag
+chem_comp_bond.pdbx_stereo_config
+chem_comp_bond.pdbx_ordinal
+
+pdbx_chem_comp_descriptor.comp_id
+pdbx_chem_comp_descriptor.type
+pdbx_chem_comp_descriptor.program
+pdbx_chem_comp_descriptor.program_version
+pdbx_chem_comp_descriptor.descriptor
+
+pdbx_chem_comp_identifier.comp_id
+pdbx_chem_comp_identifier.type
+pdbx_chem_comp_identifier.program
+pdbx_chem_comp_identifier.program_version
+pdbx_chem_comp_identifier.identifier

+ 60 - 0
data/cif-field-names/cif-core-field-names.csv

@@ -0,0 +1,60 @@
+audit.block_doi
+
+database_code.depnum_ccdc_archive
+
+chemical.name_systematic
+chemical.name_common
+chemical.melting_point
+
+chemical_formula.moiety
+chemical_formula.sum
+chemical_formula.weight
+
+atom_type.symbol
+atom_type.description
+
+atom_type_scat.dispersion_real
+atom_type_scat.dispersion_imag
+atom_type_scat.source
+
+space_group.crystal_system
+space_group.name_H-M_full
+space_group_symop.operation_xyz
+
+cell.length_a
+cell.length_b
+cell.length_c
+cell.angle_alpha
+cell.angle_beta
+cell.angle_gamma
+cell.volume
+cell.formula_units_Z
+
+atom_site.label
+atom_site.type_symbol
+atom_site.fract_x
+atom_site.fract_y
+atom_site.fract_z
+atom_site.U_iso_or_equiv
+atom_site.adp_type
+atom_site.occupancy
+atom_site.calc_flag
+atom_site.refinement_flags
+atom_site.disorder_assembly
+atom_site.disorder_group
+
+atom_site.site_symmetry_multiplicity
+
+atom_site_aniso.label
+atom_site_aniso.U_11
+atom_site_aniso.U_22
+atom_site_aniso.U_33
+atom_site_aniso.U_23
+atom_site_aniso.U_13
+atom_site_aniso.U_12
+
+geom_bond.atom_site_label_1
+geom_bond.atom_site_label_2
+geom_bond.distance
+geom_bond.site_symmetry_2
+geom_bond.publ_flag

+ 805 - 0
data/cif-field-names/mmcif-field-names.csv

@@ -0,0 +1,805 @@
+atom_sites.entry_id
+atom_sites.fract_transf_matrix
+atom_sites.fract_transf_vector
+
+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.pdbx_formal_charge
+atom_site.Cartn_x
+atom_site.Cartn_y
+atom_site.Cartn_z
+atom_site.occupancy
+atom_site.B_iso_or_equiv
+atom_site.auth_atom_id
+atom_site.auth_comp_id
+atom_site.auth_asym_id
+atom_site.auth_seq_id
+atom_site.pdbx_PDB_model_num
+atom_site.ihm_model_id
+
+atom_site_anisotrop.id
+atom_site_anisotrop.U
+atom_site_anisotrop.U_esd
+atom_site_anisotrop.pdbx_PDB_ins_code
+atom_site_anisotrop.pdbx_auth_asym_id
+atom_site_anisotrop.pdbx_auth_atom_id
+atom_site_anisotrop.pdbx_auth_comp_id
+atom_site_anisotrop.pdbx_auth_seq_id
+atom_site_anisotrop.pdbx_label_alt_id
+atom_site_anisotrop.pdbx_label_asym_id
+atom_site_anisotrop.pdbx_label_atom_id
+atom_site_anisotrop.pdbx_label_comp_id
+atom_site_anisotrop.pdbx_label_seq_id
+atom_site_anisotrop.type_symbol
+
+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
+
+chem_comp_bond.comp_id
+chem_comp_bond.pdbx_stereo_config
+chem_comp_bond.pdbx_ordinal
+chem_comp_bond.pdbx_aromatic_flag
+chem_comp_bond.atom_id_1
+chem_comp_bond.atom_id_2
+chem_comp_bond.value_order
+
+pdbx_chem_comp_identifier.comp_id
+pdbx_chem_comp_identifier.type
+pdbx_chem_comp_identifier.program
+pdbx_chem_comp_identifier.program_version
+pdbx_chem_comp_identifier.identifier
+
+pdbx_chem_comp_related.comp_id
+pdbx_chem_comp_related.related_comp_id
+pdbx_chem_comp_related.relationship_type
+pdbx_chem_comp_related.details
+
+pdbx_chem_comp_synonyms.comp_id
+pdbx_chem_comp_synonyms.name
+pdbx_chem_comp_synonyms.provenance
+
+cell.entry_id
+cell.length_a
+cell.length_b
+cell.length_c
+cell.angle_alpha
+cell.angle_beta
+cell.angle_gamma
+cell.Z_PDB
+cell.pdbx_unique_axis
+
+pdbx_database_related.db_name
+pdbx_database_related.details
+pdbx_database_related.db_id
+pdbx_database_related.content_type
+
+pdbx_database_status.status_code
+pdbx_database_status.status_code_sf
+pdbx_database_status.status_code_mr
+pdbx_database_status.entry_id
+pdbx_database_status.recvd_initial_deposition_date
+pdbx_database_status.SG_entry
+pdbx_database_status.deposit_site
+pdbx_database_status.process_site
+pdbx_database_status.status_code_cs
+pdbx_database_status.methods_development_category
+pdbx_database_status.pdb_format_compatible
+
+entity.id
+entity.type
+entity.src_method
+entity.pdbx_description
+entity.formula_weight
+entity.pdbx_number_of_molecules
+entity.details
+entity.pdbx_mutation
+entity.pdbx_fragment
+entity.pdbx_ec
+
+entity_poly.entity_id
+entity_poly.type
+entity_poly.nstd_linkage
+entity_poly.nstd_monomer
+entity_poly.pdbx_seq_one_letter_code
+entity_poly.pdbx_seq_one_letter_code_can
+entity_poly.pdbx_strand_id
+entity_poly.pdbx_target_identifier
+
+entity_poly_seq.entity_id
+entity_poly_seq.num
+entity_poly_seq.mon_id
+entity_poly_seq.hetero
+
+entity_src_gen.entity_id
+entity_src_gen.pdbx_src_id
+entity_src_gen.pdbx_beg_seq_num
+entity_src_gen.pdbx_end_seq_num
+entity_src_gen.pdbx_gene_src_gene
+entity_src_gen.pdbx_gene_src_scientific_name
+entity_src_gen.plasmid_name
+
+entity_src_nat.entity_id
+entity_src_nat.pdbx_src_id
+entity_src_nat.pdbx_beg_seq_num
+entity_src_nat.pdbx_end_seq_num
+entity_src_nat.pdbx_organism_scientific
+entity_src_nat.pdbx_plasmid_name
+
+pdbx_entity_instance_feature.ordinal
+pdbx_entity_instance_feature.feature_type
+pdbx_entity_instance_feature.details
+pdbx_entity_instance_feature.asym_id
+pdbx_entity_instance_feature.comp_id
+pdbx_entity_instance_feature.seq_num
+pdbx_entity_instance_feature.auth_asym_id
+pdbx_entity_instance_feature.auth_comp_id
+pdbx_entity_instance_feature.auth_seq_num
+
+pdbx_entity_src_syn.entity_id
+pdbx_entity_src_syn.pdbx_src_id
+pdbx_entity_src_syn.pdbx_beg_seq_num
+pdbx_entity_src_syn.pdbx_end_seq_num
+pdbx_entity_src_syn.organism_scientific
+
+pdbx_entity_branch.entity_id
+pdbx_entity_branch.type
+
+pdbx_entity_branch_list.entity_id
+pdbx_entity_branch_list.comp_id
+pdbx_entity_branch_list.num
+pdbx_entity_branch_list.hetero
+
+pdbx_entity_branch_link.link_id
+pdbx_entity_branch_link.entity_id
+pdbx_entity_branch_link.entity_branch_list_num_1
+pdbx_entity_branch_link.comp_id_1
+pdbx_entity_branch_link.atom_id_1
+pdbx_entity_branch_link.leaving_atom_id_1
+pdbx_entity_branch_link.atom_stereo_config_1
+pdbx_entity_branch_link.entity_branch_list_num_2
+pdbx_entity_branch_link.comp_id_2
+pdbx_entity_branch_link.atom_id_2
+pdbx_entity_branch_link.leaving_atom_id_2
+pdbx_entity_branch_link.atom_stereo_config_2
+pdbx_entity_branch_link.value_order
+pdbx_entity_branch_link.details
+
+pdbx_branch_scheme.asym_id
+pdbx_branch_scheme.entity_id
+pdbx_branch_scheme.mon_id
+pdbx_branch_scheme.num
+pdbx_branch_scheme.auth_asym_id
+pdbx_branch_scheme.auth_mon_id
+pdbx_branch_scheme.auth_seq_num
+pdbx_branch_scheme.hetero
+pdbx_branch_scheme.pdb_mon_id
+pdbx_branch_scheme.pdb_asym_id
+pdbx_branch_scheme.pdb_seq_num
+
+pdbx_entity_branch_descriptor.ordinal
+pdbx_entity_branch_descriptor.entity_id
+pdbx_entity_branch_descriptor.descriptor
+pdbx_entity_branch_descriptor.type
+pdbx_entity_branch_descriptor.program
+pdbx_entity_branch_descriptor.program_version
+
+pdbx_entity_nonpoly.entity_id
+pdbx_entity_nonpoly.name
+pdbx_entity_nonpoly.comp_id
+
+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
+
+entry.id
+
+audit_conform.dict_name
+audit_conform.dict_version
+audit_conform.dict_location
+
+database_2.database_id
+database_2.database_code
+
+audit_author.name
+audit_author.pdbx_ordinal
+audit_author.identifier_ORCID
+
+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
+
+citation_author.citation_id
+citation_author.name
+citation_author.ordinal
+
+exptl.entry_id
+exptl.method
+
+struct.entry_id
+struct.title
+struct.pdbx_descriptor
+
+struct_asym.id
+struct_asym.pdbx_blank_PDB_chainid_flag
+struct_asym.pdbx_modified
+struct_asym.entity_id
+struct_asym.details
+
+struct_conf.conf_type_id
+struct_conf.id
+struct_conf.pdbx_PDB_helix_id
+struct_conf.beg_label_comp_id
+struct_conf.beg_label_asym_id
+struct_conf.beg_label_seq_id
+struct_conf.pdbx_beg_PDB_ins_code
+struct_conf.end_label_comp_id
+struct_conf.end_label_asym_id
+struct_conf.end_label_seq_id
+struct_conf.pdbx_end_PDB_ins_code
+struct_conf.beg_auth_comp_id
+struct_conf.beg_auth_asym_id
+struct_conf.beg_auth_seq_id
+struct_conf.end_auth_comp_id
+struct_conf.end_auth_asym_id
+struct_conf.end_auth_seq_id
+struct_conf.pdbx_PDB_helix_class
+struct_conf.details
+struct_conf.pdbx_PDB_helix_length
+
+struct_conn.id
+struct_conn.conn_type_id
+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
+
+struct_conn_type.id
+struct_conn_type.criteria
+struct_conn_type.reference
+
+struct_keywords.entry_id
+struct_keywords.pdbx_keywords
+struct_keywords.text
+
+struct_ncs_oper.id
+struct_ncs_oper.code
+struct_ncs_oper.matrix
+struct_ncs_oper.vector
+struct_ncs_oper.details
+
+struct_sheet_range.sheet_id
+struct_sheet_range.id
+struct_sheet_range.beg_label_comp_id
+struct_sheet_range.beg_label_asym_id
+struct_sheet_range.beg_label_seq_id
+struct_sheet_range.pdbx_beg_PDB_ins_code
+struct_sheet_range.end_label_comp_id
+struct_sheet_range.end_label_asym_id
+struct_sheet_range.end_label_seq_id
+struct_sheet_range.pdbx_end_PDB_ins_code
+struct_sheet_range.beg_auth_comp_id
+struct_sheet_range.beg_auth_asym_id
+struct_sheet_range.beg_auth_seq_id
+struct_sheet_range.end_auth_comp_id
+struct_sheet_range.end_auth_asym_id
+struct_sheet_range.end_auth_seq_id
+
+struct_site.id
+struct_site.pdbx_evidence_code
+struct_site.pdbx_auth_asym_id
+struct_site.pdbx_auth_comp_id
+struct_site.pdbx_auth_seq_id
+struct_site.pdbx_auth_ins_code
+struct_site.pdbx_num_residues
+struct_site.details
+
+struct_site_gen.id
+struct_site_gen.site_id
+struct_site_gen.pdbx_num_res
+struct_site_gen.label_comp_id
+struct_site_gen.label_asym_id
+struct_site_gen.label_seq_id
+struct_site_gen.pdbx_auth_ins_code
+struct_site_gen.auth_comp_id
+struct_site_gen.auth_asym_id
+struct_site_gen.auth_seq_id
+struct_site_gen.label_atom_id
+struct_site_gen.label_alt_id
+struct_site_gen.symmetry
+struct_site_gen.details
+
+symmetry.entry_id
+symmetry.cell_setting
+symmetry.Int_Tables_number
+symmetry.space_group_name_Hall
+symmetry.space_group_name_H-M
+
+pdbx_molecule.instance_id
+pdbx_molecule.prd_id
+pdbx_molecule.asym_id
+
+pdbx_molecule_features.prd_id
+pdbx_molecule_features.name
+pdbx_molecule_features.type
+pdbx_molecule_features.class
+pdbx_molecule_features.details
+
+pdbx_reference_entity_link.prd_id
+pdbx_reference_entity_link.link_id
+pdbx_reference_entity_link.link_class
+pdbx_reference_entity_link.ref_entity_id_1
+pdbx_reference_entity_link.entity_seq_num_1
+pdbx_reference_entity_link.comp_id_1
+pdbx_reference_entity_link.atom_id_1
+pdbx_reference_entity_link.ref_entity_id_2
+pdbx_reference_entity_link.entity_seq_num_2
+pdbx_reference_entity_link.comp_id_2
+pdbx_reference_entity_link.atom_id_2
+pdbx_reference_entity_link.value_order
+pdbx_reference_entity_link.component_1
+pdbx_reference_entity_link.component_2
+pdbx_reference_entity_link.details
+
+pdbx_reference_entity_list.prd_id
+pdbx_reference_entity_list.ref_entity_id
+pdbx_reference_entity_list.component_id
+pdbx_reference_entity_list.type
+pdbx_reference_entity_list.details
+
+pdbx_reference_entity_poly_link.prd_id
+pdbx_reference_entity_poly_link.ref_entity_id
+pdbx_reference_entity_poly_link.link_id
+pdbx_reference_entity_poly_link.atom_id_1
+pdbx_reference_entity_poly_link.comp_id_1
+pdbx_reference_entity_poly_link.entity_seq_num_1
+pdbx_reference_entity_poly_link.atom_id_2
+pdbx_reference_entity_poly_link.comp_id_2
+pdbx_reference_entity_poly_link.entity_seq_num_2
+pdbx_reference_entity_poly_link.value_order
+pdbx_reference_entity_poly_link.component_id
+
+pdbx_struct_assembly.id
+pdbx_struct_assembly.details
+pdbx_struct_assembly.method_details
+pdbx_struct_assembly.oligomeric_details
+pdbx_struct_assembly.oligomeric_count
+
+pdbx_struct_assembly_gen.assembly_id
+pdbx_struct_assembly_gen.oper_expression
+pdbx_struct_assembly_gen.asym_id_list
+
+pdbx_struct_oper_list.id
+pdbx_struct_oper_list.type
+pdbx_struct_oper_list.name
+pdbx_struct_oper_list.symmetry_operation
+pdbx_struct_oper_list.matrix
+pdbx_struct_oper_list.vector
+
+pdbx_struct_mod_residue.id
+pdbx_struct_mod_residue.label_asym_id
+pdbx_struct_mod_residue.label_seq_id
+pdbx_struct_mod_residue.label_comp_id
+pdbx_struct_mod_residue.auth_asym_id
+pdbx_struct_mod_residue.auth_seq_id
+pdbx_struct_mod_residue.auth_comp_id
+pdbx_struct_mod_residue.PDB_ins_code
+pdbx_struct_mod_residue.parent_comp_id
+pdbx_struct_mod_residue.details
+
+pdbx_unobs_or_zero_occ_residues.id
+pdbx_unobs_or_zero_occ_residues.PDB_model_num
+pdbx_unobs_or_zero_occ_residues.polymer_flag
+pdbx_unobs_or_zero_occ_residues.occupancy_flag
+pdbx_unobs_or_zero_occ_residues.auth_asym_id
+pdbx_unobs_or_zero_occ_residues.auth_comp_id
+pdbx_unobs_or_zero_occ_residues.auth_seq_id
+pdbx_unobs_or_zero_occ_residues.PDB_ins_code
+pdbx_unobs_or_zero_occ_residues.label_asym_id
+pdbx_unobs_or_zero_occ_residues.label_comp_id
+pdbx_unobs_or_zero_occ_residues.label_seq_id
+
+ihm_struct_assembly.id
+ihm_struct_assembly.name
+ihm_struct_assembly.description
+
+ihm_struct_assembly_details.id
+ihm_struct_assembly_details.assembly_id
+ihm_struct_assembly_details.parent_assembly_id
+ihm_struct_assembly_details.entity_description
+ihm_struct_assembly_details.entity_id
+ihm_struct_assembly_details.asym_id
+ihm_struct_assembly_details.entity_poly_segment_id
+
+ihm_model_representation.id
+ihm_model_representation.name
+ihm_model_representation.details
+
+ihm_model_representation_details.id
+ihm_model_representation_details.representation_id
+ihm_model_representation_details.entity_id
+ihm_model_representation_details.entity_description
+ihm_model_representation_details.entity_asym_id
+ihm_model_representation_details.entity_poly_segment_id
+ihm_model_representation_details.model_object_primitive
+ihm_model_representation_details.starting_model_id
+ihm_model_representation_details.model_mode
+ihm_model_representation_details.model_granularity
+ihm_model_representation_details.model_object_count
+
+ihm_external_reference_info.reference_id
+ihm_external_reference_info.reference_provider
+ihm_external_reference_info.reference_type
+ihm_external_reference_info.reference
+ihm_external_reference_info.refers_to
+ihm_external_reference_info.associated_url
+
+ihm_external_files.id
+ihm_external_files.reference_id
+ihm_external_files.file_path
+ihm_external_files.content_type
+ihm_external_files.file_size_bytes
+ihm_external_files.details
+
+ihm_dataset_list.id
+ihm_dataset_list.data_type
+ihm_dataset_list.database_hosted
+
+ihm_dataset_group.id
+ihm_dataset_group.name
+ihm_dataset_group.application
+ihm_dataset_group.details
+
+ihm_dataset_group_link.group_id
+ihm_dataset_group_link.dataset_list_id
+
+ihm_dataset_external_reference.id
+ihm_dataset_external_reference.dataset_list_id
+ihm_dataset_external_reference.file_id
+
+ihm_dataset_related_db_reference.id
+ihm_dataset_related_db_reference.dataset_list_id
+ihm_dataset_related_db_reference.db_name
+ihm_dataset_related_db_reference.accession_code
+ihm_dataset_related_db_reference.version
+ihm_dataset_related_db_reference.details
+
+ihm_related_datasets.dataset_list_id_derived
+ihm_related_datasets.dataset_list_id_primary
+
+ihm_poly_residue_feature.ordinal_id
+ihm_poly_residue_feature.feature_id
+ihm_poly_residue_feature.entity_id
+ihm_poly_residue_feature.asym_id
+ihm_poly_residue_feature.seq_id_begin
+ihm_poly_residue_feature.comp_id_begin
+ihm_poly_residue_feature.seq_id_end
+ihm_poly_residue_feature.comp_id_end
+
+ihm_feature_list.feature_id
+ihm_feature_list.feature_type
+ihm_feature_list.entity_type
+
+ihm_cross_link_list.id
+ihm_cross_link_list.group_id
+ihm_cross_link_list.entity_description_1
+ihm_cross_link_list.entity_id_1
+ihm_cross_link_list.seq_id_1
+ihm_cross_link_list.comp_id_1
+ihm_cross_link_list.entity_description_2
+ihm_cross_link_list.entity_id_2
+ihm_cross_link_list.seq_id_2
+ihm_cross_link_list.comp_id_2
+ihm_cross_link_list.linker_type
+ihm_cross_link_list.dataset_list_id
+
+ihm_cross_link_restraint.id
+ihm_cross_link_restraint.group_id
+ihm_cross_link_restraint.entity_id_1
+ihm_cross_link_restraint.asym_id_1
+ihm_cross_link_restraint.seq_id_1
+ihm_cross_link_restraint.atom_id_1
+ihm_cross_link_restraint.comp_id_1
+ihm_cross_link_restraint.entity_id_2
+ihm_cross_link_restraint.asym_id_2
+ihm_cross_link_restraint.seq_id_2
+ihm_cross_link_restraint.atom_id_2
+ihm_cross_link_restraint.comp_id_2
+ihm_cross_link_restraint.restraint_type
+ihm_cross_link_restraint.conditional_crosslink_flag
+ihm_cross_link_restraint.model_granularity
+ihm_cross_link_restraint.distance_threshold
+ihm_cross_link_restraint.psi
+ihm_cross_link_restraint.sigma_1
+ihm_cross_link_restraint.sigma_2
+
+ihm_cross_link_result_parameters.id
+ihm_cross_link_result_parameters.restraint_id
+ihm_cross_link_result_parameters.model_id
+ihm_cross_link_result_parameters.psi
+ihm_cross_link_result_parameters.sigma_1
+ihm_cross_link_result_parameters.sigma_2
+
+ihm_sas_restraint.id
+ihm_sas_restraint.dataset_list_id
+ihm_sas_restraint.model_id
+ihm_sas_restraint.struct_assembly_id
+ihm_sas_restraint.profile_segment_flag
+ihm_sas_restraint.fitting_atom_type
+ihm_sas_restraint.fitting_method
+ihm_sas_restraint.fitting_state
+ihm_sas_restraint.radius_of_gyration
+ihm_sas_restraint.chi_value
+ihm_sas_restraint.details
+
+ihm_derived_distance_restraint.id
+ihm_derived_distance_restraint.group_id
+ihm_derived_distance_restraint.feature_id_1
+ihm_derived_distance_restraint.feature_id_2
+ihm_derived_distance_restraint.group_conditionality
+ihm_derived_distance_restraint.restraint_type
+ihm_derived_distance_restraint.distance_upper_limit
+ihm_derived_distance_restraint.random_exclusion_fraction
+ihm_derived_distance_restraint.dataset_list_id
+
+ihm_2dem_class_average_restraint.id
+ihm_2dem_class_average_restraint.dataset_list_id
+ihm_2dem_class_average_restraint.number_raw_micrographs
+ihm_2dem_class_average_restraint.pixel_size_width
+ihm_2dem_class_average_restraint.pixel_size_height
+ihm_2dem_class_average_restraint.image_resolution
+ihm_2dem_class_average_restraint.image_segment_flag
+ihm_2dem_class_average_restraint.number_of_projections
+ihm_2dem_class_average_restraint.struct_assembly_id
+ihm_2dem_class_average_restraint.details
+
+ihm_2dem_class_average_fitting.id
+ihm_2dem_class_average_fitting.restraint_id
+ihm_2dem_class_average_fitting.model_id
+ihm_2dem_class_average_fitting.cross_correlation_coefficient
+ihm_2dem_class_average_fitting.rot_matrix
+ihm_2dem_class_average_fitting.tr_vector
+
+ihm_3dem_restraint.id
+ihm_3dem_restraint.dataset_list_id
+ihm_3dem_restraint.fitting_method
+ihm_3dem_restraint.struct_assembly_id
+ihm_3dem_restraint.number_of_gaussians
+ihm_3dem_restraint.model_id
+ihm_3dem_restraint.cross_correlation_coefficient
+
+ihm_predicted_contact_restraint.id
+ihm_predicted_contact_restraint.group_id
+ihm_predicted_contact_restraint.entity_id_1
+ihm_predicted_contact_restraint.asym_id_1
+ihm_predicted_contact_restraint.seq_id_1
+ihm_predicted_contact_restraint.comp_id_1
+ihm_predicted_contact_restraint.rep_atom_1
+ihm_predicted_contact_restraint.entity_id_2
+ihm_predicted_contact_restraint.asym_id_2
+ihm_predicted_contact_restraint.seq_id_2
+ihm_predicted_contact_restraint.comp_id_2
+ihm_predicted_contact_restraint.rep_atom_2
+ihm_predicted_contact_restraint.restraint_type
+ihm_predicted_contact_restraint.distance_lower_limit
+ihm_predicted_contact_restraint.distance_upper_limit
+ihm_predicted_contact_restraint.probability
+ihm_predicted_contact_restraint.model_granularity
+ihm_predicted_contact_restraint.dataset_list_id
+ihm_predicted_contact_restraint.software_id
+
+ihm_starting_model_details.starting_model_id
+ihm_starting_model_details.entity_id
+ihm_starting_model_details.entity_description
+ihm_starting_model_details.asym_id
+ihm_starting_model_details.entity_poly_segment_id
+ihm_starting_model_details.starting_model_source
+ihm_starting_model_details.starting_model_auth_asym_id
+ihm_starting_model_details.starting_model_sequence_offset
+ihm_starting_model_details.dataset_list_id
+
+ihm_starting_comparative_models.id
+ihm_starting_comparative_models.starting_model_id
+ihm_starting_comparative_models.starting_model_auth_asym_id
+ihm_starting_comparative_models.starting_model_seq_id_begin
+ihm_starting_comparative_models.starting_model_seq_id_end
+ihm_starting_comparative_models.template_auth_asym_id
+ihm_starting_comparative_models.template_seq_id_begin
+ihm_starting_comparative_models.template_seq_id_end
+ihm_starting_comparative_models.template_sequence_identity
+ihm_starting_comparative_models.template_sequence_identity_denominator
+ihm_starting_comparative_models.template_dataset_list_id
+ihm_starting_comparative_models.alignment_file_id
+
+ihm_starting_model_coord.starting_model_id
+ihm_starting_model_coord.group_PDB
+ihm_starting_model_coord.id
+ihm_starting_model_coord.type_symbol
+ihm_starting_model_coord.atom_id
+ihm_starting_model_coord.comp_id
+ihm_starting_model_coord.entity_id
+ihm_starting_model_coord.asym_id
+ihm_starting_model_coord.seq_id
+ihm_starting_model_coord.Cartn_x
+ihm_starting_model_coord.Cartn_y
+ihm_starting_model_coord.Cartn_z
+ihm_starting_model_coord.B_iso_or_equiv
+ihm_starting_model_coord.ordinal_id
+
+ihm_starting_model_seq_dif.id
+ihm_starting_model_seq_dif.entity_id
+ihm_starting_model_seq_dif.asym_id
+ihm_starting_model_seq_dif.seq_id
+ihm_starting_model_seq_dif.comp_id
+ihm_starting_model_seq_dif.starting_model_id
+ihm_starting_model_seq_dif.db_asym_id
+ihm_starting_model_seq_dif.db_seq_id
+ihm_starting_model_seq_dif.db_comp_id
+ihm_starting_model_seq_dif.details
+
+ihm_modeling_protocol.id
+ihm_modeling_protocol.protocol_name
+ihm_modeling_protocol.num_steps
+
+ihm_modeling_protocol_details.id
+ihm_modeling_protocol_details.protocol_id
+ihm_modeling_protocol_details.step_id
+ihm_modeling_protocol_details.struct_assembly_id
+ihm_modeling_protocol_details.dataset_group_id
+ihm_modeling_protocol_details.struct_assembly_description
+ihm_modeling_protocol_details.step_name
+ihm_modeling_protocol_details.step_method
+ihm_modeling_protocol_details.num_models_begin
+ihm_modeling_protocol_details.num_models_end
+ihm_modeling_protocol_details.multi_scale_flag
+ihm_modeling_protocol_details.multi_state_flag
+ihm_modeling_protocol_details.ordered_flag
+ihm_modeling_protocol_details.software_id
+ihm_modeling_protocol_details.script_file_id
+
+ihm_modeling_post_process.id
+ihm_modeling_post_process.protocol_id
+ihm_modeling_post_process.analysis_id
+ihm_modeling_post_process.step_id
+ihm_modeling_post_process.type
+ihm_modeling_post_process.feature
+ihm_modeling_post_process.num_models_begin
+ihm_modeling_post_process.num_models_end
+
+ihm_ensemble_info.ensemble_id
+ihm_ensemble_info.ensemble_name
+ihm_ensemble_info.post_process_id
+ihm_ensemble_info.model_group_id
+ihm_ensemble_info.ensemble_clustering_method
+ihm_ensemble_info.ensemble_clustering_feature
+ihm_ensemble_info.num_ensemble_models
+ihm_ensemble_info.num_ensemble_models_deposited
+ihm_ensemble_info.ensemble_precision_value
+ihm_ensemble_info.ensemble_file_id
+
+ihm_localization_density_files.id
+ihm_localization_density_files.file_id
+ihm_localization_density_files.ensemble_id
+ihm_localization_density_files.entity_id
+ihm_localization_density_files.asym_id
+ihm_localization_density_files.entity_poly_segment_id
+
+ihm_model_list.model_id
+ihm_model_list.model_name
+ihm_model_list.assembly_id
+ihm_model_list.protocol_id
+ihm_model_list.representation_id
+
+ihm_model_group.id
+ihm_model_group.name
+ihm_model_group.details
+
+ihm_model_group_link.group_id
+ihm_model_group_link.model_id
+
+ihm_model_representative.id
+ihm_model_representative.model_group_id
+ihm_model_representative.model_id
+ihm_model_representative.selection_criteria
+
+ihm_sphere_obj_site.id
+ihm_sphere_obj_site.entity_id
+ihm_sphere_obj_site.seq_id_begin
+ihm_sphere_obj_site.seq_id_end
+ihm_sphere_obj_site.asym_id
+ihm_sphere_obj_site.Cartn_x
+ihm_sphere_obj_site.Cartn_y
+ihm_sphere_obj_site.Cartn_z
+ihm_sphere_obj_site.object_radius
+ihm_sphere_obj_site.rmsf
+ihm_sphere_obj_site.model_id
+
+ihm_gaussian_obj_site.id
+ihm_gaussian_obj_site.entity_id
+ihm_gaussian_obj_site.seq_id_begin
+ihm_gaussian_obj_site.seq_id_end
+ihm_gaussian_obj_site.asym_id
+ihm_gaussian_obj_site.mean_Cartn_x
+ihm_gaussian_obj_site.mean_Cartn_y
+ihm_gaussian_obj_site.mean_Cartn_z
+ihm_gaussian_obj_site.weight
+ihm_gaussian_obj_site.covariance_matrix
+ihm_gaussian_obj_site.model_id
+
+ihm_gaussian_obj_ensemble.id
+ihm_gaussian_obj_ensemble.entity_id
+ihm_gaussian_obj_ensemble.seq_id_begin
+ihm_gaussian_obj_ensemble.seq_id_end
+ihm_gaussian_obj_ensemble.asym_id
+ihm_gaussian_obj_ensemble.mean_Cartn_x
+ihm_gaussian_obj_ensemble.mean_Cartn_y
+ihm_gaussian_obj_ensemble.mean_Cartn_z
+ihm_gaussian_obj_ensemble.weight
+ihm_gaussian_obj_ensemble.covariance_matrix
+ihm_gaussian_obj_ensemble.ensemble_id
+
+ihm_multi_state_modeling.state_id
+ihm_multi_state_modeling.state_group_id
+ihm_multi_state_modeling.population_fraction
+ihm_multi_state_modeling.population_fraction_sd
+ihm_multi_state_modeling.state_type
+ihm_multi_state_modeling.state_name
+ihm_multi_state_modeling.experiment_type
+ihm_multi_state_modeling.details

+ 76 - 0
data/cif-field-names/mmtf-filter.csv

@@ -0,0 +1,76 @@
+cell.length_a
+cell.length_b
+cell.length_c
+cell.angle_alpha
+cell.angle_beta
+cell.angle_gamma
+
+symmetry.space_group_name_H-M
+
+entry.id
+
+struct.title
+
+pdbx_database_status.recvd_initial_deposition_date
+
+pdbx_audit_revision_history.revision_date
+
+struct_ncs_oper
+
+pdbx_struct_assembly_gen
+
+pdbx_struct_oper_list
+
+entity.id
+entity.type
+entity.pdbx_description
+
+entity_poly.entity_id
+entity_poly.pdbx_seq_one_letter_code
+entity_poly.pdbx_strand_id
+
+exptl.method
+
+refine.ls_d_res_low
+refine.ls_R_factor_R_free
+refine.ls_R_factor_R_work
+
+atom_site.pdbx_formal_charge
+atom_site.label_atom_id
+atom_site.type_symbol
+
+chem_comp.id
+chem_comp.type
+chem_comp.name
+
+chem_comp_bond
+
+atom_site.Cartn_x
+atom_site.Cartn_y
+atom_site.Cartn_z
+atom_site.B_iso_or_equiv
+atom_site.id
+atom_site.label_alt_id
+atom_site.occupancy
+atom_site.label_seq_id
+atom_site.label_comp_id
+
+struct_sheet_range.id
+struct_sheet_range.beg_label_asym_id
+struct_sheet_range.beg_label_seq_id
+struct_sheet_range.pdbx_beg_PDB_ins_code
+struct_sheet_range.end_label_asym_id
+struct_sheet_range.end_label_seq_id
+struct_sheet_range.pdbx_end_PDB_ins_code
+struct_conf.conf_type_id
+struct_conf.id
+struct_conf.beg_label_asym_id
+struct_conf.beg_label_seq_id
+struct_conf.pdbx_beg_PDB_ins_code
+struct_conf.end_label_asym_id
+struct_conf.end_label_seq_id
+struct_conf.pdbx_end_PDB_ins_code
+
+atom_site.pdbx_PDB_ins_code
+atom_site.label_asym_id
+atom_site.auth_asym_id

+ 1 - 1
data/rcsb-graphql/codegen.yml

@@ -1,4 +1,4 @@
-schema: https://data-beta.rcsb.org/graphql
+schema: https://data.rcsb.org/graphql
 documents: './src/mol-model-props/rcsb/graphql/symmetry.gql.ts'
 generates:
   './src/mol-model-props/rcsb/graphql/types.ts':

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.6.4",
+  "version": "0.6.7",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 3 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "0.6.4",
+  "version": "0.6.7",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -38,6 +38,8 @@
     "lib/"
   ],
   "bin": {
+    "cif2bcif": "lib/apps/cif2bcif/index.js",
+    "cifschema": "lib/apps/cifschema/index.js",
     "model-server": "lib/servers/model/server.js",
     "model-server-query": "lib/servers/model/local.js",
     "model-server-preprocess": "lib/servers/model/preprocess.js",

+ 0 - 30
src/apps/basic-wrapper/controls.tsx

@@ -1,30 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { PluginUIComponent } from '../../mol-plugin-ui/base';
-import * as React from 'react';
-import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
-
-export class BasicWrapperControls extends PluginUIComponent {
-
-    render() {
-        return <div style={{ overflowY: 'auto', display: 'block', height: '100%' }}>
-            <TransformUpdaterControl nodeRef='asm' />
-            <TransformUpdaterControl nodeRef='seq-visual' header={{ name: 'Sequence Visual' }} />
-            <TransformUpdaterControl nodeRef='het-visual' header={{ name: 'HET Visual' }} />
-            <TransformUpdaterControl nodeRef='water-visual' header={{ name: 'Water Visual' }} initiallyCollapsed={true} />
-            <TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
-        </div>;
-    }
-}
-
-export class CustomToastMessage extends PluginUIComponent {
-    render() {
-        return <>
-            Custom <i>Toast</i> content. No timeout.
-        </>;
-    }
-}

+ 0 - 99
src/apps/basic-wrapper/helpers.ts

@@ -1,99 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
-import { PluginContext } from '../../mol-plugin/context';
-import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
-import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
-import { StateBuilder } from '../../mol-state';
-import Expression from '../../mol-script/language/expression';
-import { ColorTheme } from '../../mol-theme/color';
-import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
-type SupportedFormats = 'cif' | 'pdb'
-
-export namespace StateHelper {
-    export function download(b: StateBuilder.To<PSO.Root>, url: string, ref?: string) {
-        return b.apply(StateTransforms.Data.Download, { url, isBinary: false }, { ref });
-    }
-
-    export function getModel(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, modelIndex = 0) {
-        const parsed = format === 'cif'
-            ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
-            : b.apply(StateTransforms.Model.TrajectoryFromPDB);
-
-        return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex });
-    }
-
-    export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
-        return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
-    };
-
-    export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
-        const expression = MS.struct.generator.atomGroups({
-            'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
-        })
-        return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Chain ${auth_asym_id}` });
-    }
-
-    export function select(b: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression) {
-        return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression });
-    }
-
-    export function selectSurroundingsOfFirstResidue(b: StateBuilder.To<PSO.Molecule.Structure>, comp_id: string, radius: number) {
-        const expression = MS.struct.modifier.includeSurroundings({
-            0: MS.struct.filter.first([
-                MS.struct.generator.atomGroups({
-                    'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
-                    'group-by': MS.struct.atomProperty.macromolecular.residueKey()
-                })
-            ]),
-            radius
-        })
-        return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Surr. ${comp_id} (${radius} ang)` });
-    }
-
-    export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) {
-        return b.apply(StateTransforms.Model.TransformStructureConformation,
-            { transform: { name: 'components', params: { axis: Vec3.create(1, 0, 0), angle: 0, translation: Vec3.zero() } } },
-            { tags: 'transform' });
-    }
-
-    export function transform(b: StateBuilder.To<PSO.Molecule.Structure>, matrix: Mat4) {
-        return b.apply(StateTransforms.Model.TransformStructureConformation, {
-            transform: { name: 'matrix', params: matrix }
-        }, { tags: 'transform' });
-    }
-
-    export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
-        const props = {
-            type: {
-                name: 'assembly' as const,
-                params: { id: id || 'deposited' }
-            }
-        }
-        return b.apply(StateTransforms.Model.StructureFromModel, props, { tags: 'asm' })
-    }
-
-    export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
-        return visualRoot;
-    }
-
-    export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
-        visualRoot
-            .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
-        return visualRoot;
-    }
-
-}

+ 0 - 219
src/apps/basic-wrapper/index.ts

@@ -1,219 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
-import './index.html'
-import { PluginContext } from '../../mol-plugin/context';
-import { PluginCommands } from '../../mol-plugin/commands';
-import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { Color } from '../../mol-util/color';
-import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
-import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
-import { StateBuilder, StateTransform } from '../../mol-state';
-import { StripedResidues } from './coloring';
-import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
-import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
-import { CustomToastMessage } from './controls';
-import { EmptyLoci } from '../../mol-model/loci';
-import { StructureSelection } from '../../mol-model/structure';
-import { Script } from '../../mol-script/script';
-import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
-require('mol-plugin-ui/skin/light.scss')
-
-type SupportedFormats = 'cif' | 'pdb'
-type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
-
-class BasicWrapper {
-    plugin: PluginContext;
-
-    init(target: string | HTMLElement) {
-        this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
-            ...DefaultPluginSpec,
-            layout: {
-                initial: {
-                    isExpanded: false,
-                    showControls: false
-                },
-                controls: {
-                    // left: 'none',
-                    // right: BasicWrapperControls
-                }
-            },
-            components: {
-                remoteState: 'none'
-            }
-        });
-
-        this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
-        this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
-        this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
-    }
-
-    private download(b: StateBuilder.To<PSO.Root>, url: string) {
-        return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
-    }
-
-    private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
-        const parsed = format === 'cif'
-            ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
-            : b.apply(StateTransforms.Model.TrajectoryFromPDB);
-
-        const props = {
-            type: {
-                name: 'assembly' as const,
-                params: { id: assemblyId || 'deposited' }
-            }
-        }
-        return parsed
-            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
-            .apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
-            .apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
-    }
-
-    private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
-        return visualRoot;
-    }
-
-    private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
-    async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
-        let loadType: 'full' | 'update' = 'full';
-
-        const state = this.plugin.state.data;
-
-        if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
-            loadType = 'full';
-        } else if (this.loadedParams.url === url) {
-            if (state.select('asm').length > 0) loadType = 'update';
-        }
-
-        let tree: StateBuilder.Root;
-        if (loadType === 'full') {
-            await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
-            tree = state.build();
-            this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
-        } else {
-            const props = {
-                type: {
-                    name: 'assembly' as const,
-                    params: { id: assemblyId || 'deposited' }
-                }
-            }
-
-            tree = state.build();
-            tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
-        }
-
-        await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
-        this.loadedParams = { url, format, assemblyId };
-        PluginCommands.Camera.Reset(this.plugin, { });
-    }
-
-    setBackground(color: number) {
-        const renderer = this.plugin.canvas3d!.props.renderer;
-        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer,  backgroundColor: Color(color) } } });
-    }
-
-    toggleSpin() {
-        if (!this.plugin.canvas3d) return;
-
-        const trackball = this.plugin.canvas3d.props.trackball;
-        const spinning = trackball.spin;
-        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
-        if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
-    }
-
-    animate = {
-        modelIndex: {
-            maxFPS: 8,
-            onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
-            onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
-            palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
-            loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
-            stop: () => this.plugin.state.animation.stop()
-        }
-    }
-
-    coloring = {
-        applyStripes: async () => {
-            const state = this.plugin.state.data;
-
-            const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
-            const tree = state.build();
-            const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
-
-            for (const v of visuals) {
-                tree.to(v).update(old => ({ ...old, colorTheme }));
-            }
-
-            await PluginCommands.State.Update(this.plugin, { state, tree });
-        }
-    }
-
-    interactivity = {
-        highlightOn: () => {
-            const seq_id = 7;
-            const data = (this.plugin.state.data.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
-            const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
-                'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
-                'group-by': Q.struct.atomProperty.macromolecular.residueKey()
-            }), data);
-            const loci = StructureSelection.toLociWithSourceUnits(sel);
-            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
-        },
-        clearHighlight: () => {
-            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
-        }
-    }
-
-    tests = {
-        staticSuperposition: async () => {
-            const state = this.plugin.state.data;
-            const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
-            await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
-            await PluginCommands.State.Update(this.plugin, { state, tree });
-        },
-        dynamicSuperposition: async () => {
-            await PluginCommands.State.RemoveObject(this.plugin, { state: this.plugin.state.data, ref: StateTransform.RootRef });
-            await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
-        },
-        toggleValidationTooltip: async () => {
-            const state = this.plugin.state.behaviors;
-            const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
-            await PluginCommands.State.Update(this.plugin, { state, tree });
-        },
-        showToasts: () => {
-            PluginCommands.Toast.Show(this.plugin, {
-                title: 'Toast 1',
-                message: 'This is an example text, timeout 3s',
-                key: 'toast-1',
-                timeoutMs: 3000
-            });
-            PluginCommands.Toast.Show(this.plugin, {
-                title: 'Toast 2',
-                message: CustomToastMessage,
-                key: 'toast-2'
-            });
-        },
-        hideToasts: () => {
-            PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
-            PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
-        }
-    }
-}
-
-(window as any).BasicMolStarWrapper = new BasicWrapper();

+ 0 - 108
src/apps/basic-wrapper/superposition.ts

@@ -1,108 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
-
-// TODO: move to an "example"
-
-import { PluginContext } from '../../mol-plugin/context';
-import { Mat4 } from '../../mol-math/linear-algebra';
-import { StateHelper } from './helpers';
-import { PluginCommands } from '../../mol-plugin/commands';
-import { StateSelection, StateBuilder } from '../../mol-state';
-import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
-import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
-import { compile } from '../../mol-script/runtime/query/compiler';
-import { StructureSelection, QueryContext } from '../../mol-model/structure';
-import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
-import Expression from '../../mol-script/language/expression';
-
-export type SuperpositionTestInput = {
-    pdbId: string,
-    auth_asym_id: string,
-    matrix: Mat4
-}[];
-
-// function getAxisAngleTranslation(m: Mat4) {
-//     const translation = Mat4.getTranslation(Vec3.zero(), m);
-//     const axis = Vec3.zero();
-//     const angle = 180 / Math.PI * Quat.getAxisAngle(axis, Mat4.getRotation(Quat.zero(), m));
-//     return { translation, axis, angle };
-// }
-
-export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) {
-    const b = ctx.state.data.build().toRoot();
-    for (const s of src) {
-        StateHelper.visual(ctx,
-            StateHelper.transform(
-                StateHelper.selectChain(
-                    StateHelper.structure(
-                        StateHelper.getModel(StateHelper.download(b, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`), 'cif')),
-                    s.auth_asym_id
-                ),
-                s.matrix
-            )
-        );
-    }
-    return b;
-}
-
-export const StaticSuperpositionTestData: SuperpositionTestInput = [
-    { pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() },
-    { pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
-        [0.406, 0.879, 0.248, -200.633],
-        [0.693, -0.473, 0.544, 73.403],
-        [0.596, -0.049, -0.802, -14.209],
-        [0, 0, 0, 1]] )},
-    { pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
-        [-0.053, -0.077, 0.996, -45.633],
-        [-0.312, 0.949, 0.057, -12.255],
-        [-0.949, -0.307, -0.074, 53.562],
-        [0, 0, 0, 1]] )}
-];
-
-export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
-    const state = ctx.state.data;
-
-    const structures = state.build().toRoot();
-    for (const s of src) {
-        StateHelper.structure(
-            StateHelper.getModel(StateHelper.download(structures, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`), 'cif'));
-    }
-
-    await PluginCommands.State.Update(ctx, { state, tree: structures });
-
-    const pivot = MS.struct.filter.first([
-        MS.struct.generator.atomGroups({
-            'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
-            'group-by': MS.struct.atomProperty.macromolecular.residueKey()
-        })
-    ]);
-    const rest = MS.struct.modifier.exceptBy({
-        0: MS.struct.generator.all(),
-        by: pivot
-    });
-
-    const query = compile<StructureSelection>(pivot);
-    const xs = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
-    const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.obj!.data))));
-
-    const transforms = superposeStructures(selections);
-    const visuals = state.build();
-
-    siteVisual(ctx, StateHelper.selectSurroundingsOfFirstResidue(visuals.to(xs[0].transform.ref), 'HEM', 7), pivot, rest);
-    for (let i = 1; i < selections.length; i++) {
-        const root = visuals.to(xs[i].transform.ref);
-        siteVisual(ctx,
-            StateHelper.transform(StateHelper.selectSurroundingsOfFirstResidue(root, 'HEM', 7), transforms[i - 1].bTransform),
-            pivot, rest);
-    }
-
-    await PluginCommands.State.Update(ctx, { state, tree: visuals });
-}
-
-function siteVisual(ctx: PluginContext, b: StateBuilder.To<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
-    StateHelper.ballsAndSticks(ctx, b, pivot, 'residue-name');
-    StateHelper.ballsAndSticks(ctx, b, rest, 'uniform');
-}

+ 119 - 0
src/apps/cif2bcif/converter.ts

@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { CIF, CifCategory, getCifFieldType, CifField, CifFile } from '../../mol-io/reader/cif'
+import { CifWriter, EncodingStrategyHint } from '../../mol-io/writer/cif'
+import * as util from 'util'
+import * as fs from 'fs'
+import * as zlib from 'zlib'
+import { Progress, Task, RuntimeContext } from '../../mol-task';
+import { classifyFloatArray, classifyIntArray } from '../../mol-io/common/binary-cif';
+import { BinaryEncodingProvider } from '../../mol-io/writer/cif/encoder/binary';
+import { Category } from '../../mol-io/writer/cif/encoder';
+import { ReaderResult } from '../../mol-io/reader/result';
+
+function showProgress(p: Progress) {
+    process.stdout.write(`\r${new Array(80).join(' ')}`);
+    process.stdout.write(`\r${Progress.format(p)}`);
+}
+
+const readFileAsync = util.promisify(fs.readFile);
+const unzipAsync = util.promisify<zlib.InputType, Buffer>(zlib.unzip);
+
+async function readFile(ctx: RuntimeContext, filename: string): Promise<ReaderResult<CifFile>> {
+    const isGz = /\.gz$/i.test(filename);
+    if (filename.match(/\.bcif/)) {
+        let input = await readFileAsync(filename)
+        if (isGz) input = await unzipAsync(input);
+        return await CIF.parseBinary(new Uint8Array(input)).runInContext(ctx);
+    } else {
+        let str: string;
+        if (isGz) {
+            const data = await unzipAsync(await readFileAsync(filename));
+            str = data.toString('utf8');
+        } else {
+            str = await readFileAsync(filename, 'utf8');
+        }
+        return await CIF.parseText(str).runInContext(ctx);
+    }
+}
+
+async function getCIF(ctx: RuntimeContext, filename: string) {
+    const parsed = await readFile(ctx, filename);
+    if (parsed.isError) {
+        throw new Error(parsed.toString());
+    }
+    return parsed.result;
+}
+
+function getCategoryInstanceProvider(cat: CifCategory, fields: CifWriter.Field[]): CifWriter.Category {
+    return {
+        name: cat.name,
+        instance: () => CifWriter.categoryInstance(fields, { data: cat, rowCount: cat.rowCount })
+    };
+}
+
+function classify(name: string, field: CifField): CifWriter.Field {
+    const type = getCifFieldType(field);
+    if (type['@type'] === 'str') {
+        return { name, type: CifWriter.Field.Type.Str, value: field.str, valueKind: field.valueKind };
+    } else if (type['@type'] === 'float') {
+        const encoder = classifyFloatArray(field.toFloatArray({ array: Float64Array }));
+        return CifWriter.Field.float(name, field.float, { valueKind: field.valueKind, encoder, typedArray: Float64Array });
+    } else {
+        const encoder = classifyIntArray(field.toIntArray({ array: Int32Array }));
+        return CifWriter.Field.int(name, field.int, { valueKind: field.valueKind, encoder, typedArray: Int32Array });
+    }
+}
+
+export default function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
+    return Task.create<Uint8Array>('BinaryCIF', async ctx => {
+        const encodingProvider: BinaryEncodingProvider = hints
+            ? CifWriter.createEncodingProviderFromJsonConfig(hints)
+            : { get: (c, f) => void 0 };
+        const cif = await getCIF(ctx, path);
+
+        const encoder = CifWriter.createEncoder({
+            binary: !asText,
+            encoderName: 'mol*/ciftools cif2bcif',
+            binaryAutoClassifyEncoding: true,
+            binaryEncodingPovider: encodingProvider
+        });
+
+        if (filter) {
+            encoder.setFilter(Category.filterOf(filter));
+        }
+
+        let maxProgress = 0;
+        for (const b of cif.blocks) {
+            maxProgress += b.categoryNames.length;
+            for (const c of b.categoryNames) maxProgress += b.categories[c].fieldNames.length;
+        }
+
+        let current = 0;
+        for (const b of cif.blocks) {
+            encoder.startDataBlock(b.header);
+            for (const c of b.categoryNames) {
+                const cat = b.categories[c];
+                const fields: CifWriter.Field[] = [];
+                for (const f of cat.fieldNames) {
+                    fields.push(classify(f, cat.getField(f)!))
+                    current++;
+                    if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
+                }
+
+                encoder.writeCategory(getCategoryInstanceProvider(b.categories[c], fields));
+                current++;
+                if (ctx.shouldUpdate) await ctx.update({ message: 'Encoding...', current, max: maxProgress });
+            }
+        }
+        await ctx.update('Exporting...');
+        const ret = encoder.getData() as Uint8Array;
+        await ctx.update('Done.\n');
+        return ret;
+    }).run(showProgress, 250);
+}

+ 67 - 0
src/apps/cif2bcif/index.ts

@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2017-2019 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>
+ */
+
+import * as argparse from 'argparse'
+import * as util from 'util'
+import * as fs from 'fs'
+import * as zlib from 'zlib'
+import convert from './converter'
+
+require('util.promisify').shim();
+
+async function process(srcPath: string, outPath: string, configPath?: string, filterPath?: string) {
+    const config = configPath ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : void 0;
+    const filter = filterPath ? fs.readFileSync(filterPath, 'utf8') : void 0;
+
+    const res = await convert(srcPath, false, config, filter);
+    await write(outPath, res);
+}
+
+const zipAsync = util.promisify<zlib.InputType, Buffer>(zlib.gzip);
+
+async function write(outPath: string, res: Uint8Array) {
+    const isGz = /\.gz$/i.test(outPath);
+    if (isGz) {
+        res = await zipAsync(res);
+    }
+    fs.writeFileSync(outPath, res);
+}
+
+function run(args: Args) {
+    process(args.src, args.out, args.config, args.filter)
+}
+
+const parser = new argparse.ArgumentParser({
+    addHelp: true,
+    description: 'Convert any CIF file to a BCIF file'
+});
+parser.addArgument([ 'src' ], {
+    help: 'Source CIF path'
+});
+parser.addArgument([ 'out' ], {
+    help: 'Output BCIF path'
+});
+parser.addArgument([ '-c', '--config' ], {
+    help: 'Optional encoding strategy/precision config path',
+    required: false
+});
+parser.addArgument([ '-f', '--filter' ], {
+    help: 'Optional filter whitelist/blacklist path',
+    required: false
+});
+
+interface Args {
+    src: string
+    out: string
+    config?: string
+    filter?: string
+}
+const args: Args = parser.parseArgs();
+
+if (args) {
+    run(args)
+}

+ 274 - 0
src/apps/cifschema/index.ts

@@ -0,0 +1,274 @@
+/**
+ * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import * as argparse from 'argparse'
+import * as fs from 'fs'
+import * as path from 'path'
+import fetch from 'node-fetch'
+
+import { parseCsv } from '../../mol-io/reader/csv/parser'
+import { CifFrame, CifBlock } from '../../mol-io/reader/cif'
+import parseText from '../../mol-io/reader/cif/text/parser'
+import { generateSchema } from './util/cif-dic'
+import { generate } from './util/generate'
+import { Filter, Database } from './util/schema'
+import { parseImportGet } from './util/helper'
+
+function getDicVersion(block: CifBlock) {
+    return block.categories.dictionary.getField('version')!.str(0)
+}
+
+function getDicNamespace(block: CifBlock) {
+    return block.categories.dictionary.getField('namespace')!.str(0)
+}
+
+async function runGenerateSchemaMmcif(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
+    await ensureMmcifDicAvailable()
+    const mmcifDic = await parseText(fs.readFileSync(MMCIF_DIC_PATH, 'utf8')).run();
+    if (mmcifDic.isError) throw mmcifDic
+
+    await ensureIhmDicAvailable()
+    const ihmDic = await parseText(fs.readFileSync(IHM_DIC_PATH, 'utf8')).run();
+    if (ihmDic.isError) throw ihmDic
+
+    await ensureCarbBranchDicAvailable()
+    const carbBranchDic = await parseText(fs.readFileSync(CARB_BRANCH_DIC_PATH, 'utf8')).run();
+    if (carbBranchDic.isError) throw carbBranchDic
+
+    await ensureCarbCompDicAvailable()
+    const carbCompDic = await parseText(fs.readFileSync(CARB_COMP_DIC_PATH, 'utf8')).run();
+    if (carbCompDic.isError) throw carbCompDic
+
+    const mmcifDicVersion = getDicVersion(mmcifDic.result.blocks[0])
+    const ihmDicVersion = getDicVersion(ihmDic.result.blocks[0])
+    const carbDicVersion = 'draft'
+    const version = `Dictionary versions: mmCIF ${mmcifDicVersion}, IHM ${ihmDicVersion}, CARB ${carbDicVersion}.`
+
+    const frames: CifFrame[] = [...mmcifDic.result.blocks[0].saveFrames, ...ihmDic.result.blocks[0].saveFrames, ...carbBranchDic.result.blocks[0].saveFrames, ...carbCompDic.result.blocks[0].saveFrames]
+    const schema = generateSchema(frames)
+
+    await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases)
+}
+
+async function runGenerateSchemaCifCore(name: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
+    await ensureCifCoreDicAvailable()
+    const cifCoreDic = await parseText(fs.readFileSync(CIF_CORE_DIC_PATH, 'utf8')).run();
+    if (cifCoreDic.isError) throw cifCoreDic
+
+    const cifCoreDicVersion = getDicVersion(cifCoreDic.result.blocks[0])
+    const version = `Dictionary versions: CifCore ${cifCoreDicVersion}.`
+
+    const frames: CifFrame[] = [...cifCoreDic.result.blocks[0].saveFrames]
+    const imports = await resolveImports(frames, DIC_DIR)
+    const schema = generateSchema(frames, imports)
+
+    await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases)
+}
+
+async function resolveImports(frames: CifFrame[], baseDir: string): Promise<Map<string, CifFrame[]>> {
+    const imports = new Map<string, CifFrame[]>()
+
+    for (const d of frames) {
+        if ('import' in d.categories) {
+            const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0))
+            for (const g of importGet) {
+                const { file } = g
+                if (!file) continue
+                if (imports.has(file)) continue
+
+                const dic = await parseText(fs.readFileSync(path.join(baseDir, file), 'utf8')).run();
+                if (dic.isError) throw dic
+
+                imports.set(file, [...dic.result.blocks[0].saveFrames])
+            }
+        }
+    }
+
+    return imports
+}
+
+async function runGenerateSchemaDic(name: string, dicPath: string, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
+    const dic = await parseText(fs.readFileSync(dicPath, 'utf8')).run();
+    if (dic.isError) throw dic
+
+    const dicVersion = getDicVersion(dic.result.blocks[0])
+    const dicName = getDicNamespace(dic.result.blocks[0])
+    const version = `Dictionary versions: ${dicName} ${dicVersion}.`
+
+    const frames: CifFrame[] = [...dic.result.blocks[0].saveFrames]
+    const imports = await resolveImports(frames, path.dirname(dicPath))
+    const schema = generateSchema(frames, imports)
+
+    await runGenerateSchema(name, version, schema, fieldNamesPath, typescript, out, moldbImportPath, addAliases)
+}
+
+async function runGenerateSchema(name: string, version: string, schema: Database, fieldNamesPath: string, typescript = false, out: string, moldbImportPath: string, addAliases: boolean) {
+    const filter = fieldNamesPath ? await getFieldNamesFilter(fieldNamesPath) : undefined
+    const output = typescript ? generate(name, version, schema, filter, moldbImportPath, addAliases) : JSON.stringify(schema, undefined, 4)
+
+    if (out) {
+        fs.writeFileSync(out, output)
+    } else {
+        console.log(output)
+    }
+}
+
+async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
+    const fieldNamesStr = fs.readFileSync(fieldNamesPath, 'utf8')
+    const parsed = await parseCsv(fieldNamesStr, { noColumnNames: true }).run();
+    if (parsed.isError) throw parser.error
+    const csvFile = parsed.result;
+
+    const fieldNamesCol = csvFile.table.getColumn('0')
+    if (!fieldNamesCol) throw 'error getting fields columns'
+    const fieldNames = fieldNamesCol.toStringArray()
+
+    const filter: Filter = {}
+    fieldNames.forEach((name, i) => {
+        const [ category, field ] = name.split('.')
+        // console.log(category, field)
+        if (!filter[ category ]) filter[ category ] = {}
+        filter[ category ][ field ] = true
+    })
+    return filter
+}
+
+async function ensureMmcifDicAvailable() { await ensureDicAvailable(MMCIF_DIC_PATH, MMCIF_DIC_URL) }
+async function ensureIhmDicAvailable() { await ensureDicAvailable(IHM_DIC_PATH, IHM_DIC_URL) }
+async function ensureCarbBranchDicAvailable() { await ensureDicAvailable(CARB_BRANCH_DIC_PATH, CARB_BRANCH_DIC_URL) }
+async function ensureCarbCompDicAvailable() { await ensureDicAvailable(CARB_COMP_DIC_PATH, CARB_COMP_DIC_URL) }
+async function ensureCifCoreDicAvailable() {
+    await ensureDicAvailable(CIF_CORE_DIC_PATH, CIF_CORE_DIC_URL)
+    await ensureDicAvailable(CIF_CORE_ENUM_PATH, CIF_CORE_ENUM_URL)
+    await ensureDicAvailable(CIF_CORE_ATTR_PATH, CIF_CORE_ATTR_URL)
+}
+
+async function ensureDicAvailable(dicPath: string, dicUrl: string) {
+    if (FORCE_DIC_DOWNLOAD || !fs.existsSync(dicPath)) {
+        const name = dicUrl.substr(dicUrl.lastIndexOf('/') + 1)
+        console.log(`downloading ${name}...`)
+        const data = await fetch(dicUrl)
+        if (!fs.existsSync(DIC_DIR)) {
+            fs.mkdirSync(DIC_DIR);
+        }
+        fs.writeFileSync(dicPath, await data.text())
+        console.log(`done downloading ${name}`)
+    }
+}
+
+const DIC_DIR = path.resolve(__dirname, '../dics/')
+const MMCIF_DIC_PATH = `${DIC_DIR}/mmcif_pdbx_v50.dic`
+const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.dic'
+const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`
+const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic'
+const CARB_BRANCH_DIC_PATH = `${DIC_DIR}/entity_branch-extension.dic`
+const CARB_BRANCH_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/entity_branch-extension.dic'
+const CARB_COMP_DIC_PATH = `${DIC_DIR}/chem_comp-extension.dic`
+const CARB_COMP_DIC_URL = 'https://raw.githubusercontent.com/pdbxmmcifwg/carbohydrate-extension/master/dict/chem_comp-extension.dic'
+
+const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`
+const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic'
+const CIF_CORE_ENUM_PATH = `${DIC_DIR}/templ_enum.cif`
+const CIF_CORE_ENUM_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_enum.cif'
+const CIF_CORE_ATTR_PATH = `${DIC_DIR}/templ_attr.cif`
+const CIF_CORE_ATTR_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_attr.cif'
+
+const parser = new argparse.ArgumentParser({
+    addHelp: true,
+    description: 'Create schema from mmcif dictionary (v50 plus IHM and entity_branch extensions, downloaded from wwPDB)'
+});
+parser.addArgument([ '--preset', '-p' ], {
+    defaultValue: '',
+    choices: ['', 'mmCIF', 'CCD', 'BIRD', 'CifCore'],
+    help: 'Preset name'
+});
+parser.addArgument([ '--name', '-n' ], {
+    defaultValue: '',
+    help: 'Schema name'
+});
+parser.addArgument([ '--out', '-o' ], {
+    help: 'Generated schema output path, if not given printed to stdout'
+});
+parser.addArgument([ '--targetFormat', '-tf' ], {
+    defaultValue: 'typescript-molstar',
+    choices: ['typescript-molstar', 'json-internal'],
+    help: 'Target format'
+});
+parser.addArgument([ '--dicPath', '-d' ], {
+    defaultValue: '',
+    help: 'Path to dictionary'
+});
+parser.addArgument([ '--fieldNamesPath', '-fn' ], {
+    defaultValue: '',
+    help: 'Field names to include'
+});
+parser.addArgument([ '--forceDicDownload', '-f' ], {
+    action: 'storeTrue',
+    help: 'Force download of dictionaries'
+});
+parser.addArgument([ '--moldataImportPath', '-mip' ], {
+    defaultValue: 'molstar/lib/mol-data',
+    help: 'mol-data import path (for typescript target only)'
+});
+parser.addArgument([ '--addAliases', '-aa' ], {
+    action: 'storeTrue',
+    help: 'Add field name/path aliases'
+});
+interface Args {
+    name: string
+    preset: '' | 'mmCIF' | 'CCD' | 'BIRD' | 'CifCore'
+    forceDicDownload: boolean
+    dic: '' | 'mmCIF' | 'CifCore'
+    dicPath: string,
+    fieldNamesPath: string
+    targetFormat: 'typescript-molstar' | 'json-internal'
+    out: string,
+    moldataImportPath: string
+    addAliases: boolean
+}
+const args: Args = parser.parseArgs();
+
+const FORCE_DIC_DOWNLOAD = args.forceDicDownload
+
+switch (args.preset) {
+    case 'mmCIF':
+        args.name = 'mmCIF'
+        args.dic = 'mmCIF'
+        args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/mmcif-field-names.csv')
+        break
+    case 'CCD':
+        args.name = 'CCD'
+        args.dic = 'mmCIF'
+        args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/ccd-field-names.csv')
+        break
+    case 'BIRD':
+        args.name = 'BIRD'
+        args.dic = 'mmCIF'
+        args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/bird-field-names.csv')
+        break
+    case 'CifCore':
+        args.name = 'CifCore'
+        args.dic = 'CifCore'
+        args.fieldNamesPath = path.resolve(__dirname, '../../../data/cif-field-names/cif-core-field-names.csv')
+        break
+}
+
+if (args.name) {
+    const typescript = args.targetFormat === 'typescript-molstar'
+    if (args.dicPath) {
+        runGenerateSchemaDic(args.name, args.dicPath, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
+            console.error(e)
+        })
+    } else if (args.dic === 'mmCIF') {
+        runGenerateSchemaMmcif(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
+            console.error(e)
+        })
+    } else if (args.dic === 'CifCore') {
+        runGenerateSchemaCifCore(args.name, args.fieldNamesPath, typescript, args.out, args.moldataImportPath, args.addAliases).catch(e => {
+            console.error(e)
+        })
+    }
+}

+ 446 - 0
src/apps/cifschema/util/cif-dic.ts

@@ -0,0 +1,446 @@
+/**
+ * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Database, Column, EnumCol, StrCol, IntCol, ListCol, FloatCol, CoordCol, MatrixCol, VectorCol } from './schema'
+import { parseImportGet } from './helper'
+import * as Data from '../../../mol-io/reader/cif/data-model'
+import { CifFrame } from '../../../mol-io/reader/cif/data-model';
+
+export function getFieldType(type: string, description: string, values?: string[], container?: string): Column {
+    switch (type) {
+        // mmCIF
+        case 'code':
+        case 'ucode':
+        case 'line':
+        case 'uline':
+        case 'text':
+        case 'char':
+        case 'uchar3':
+        case 'uchar1':
+        case 'boolean':
+            return values && values.length ? EnumCol(values, 'str', description) : StrCol(description)
+        case 'aliasname':
+        case 'name':
+        case 'idname':
+        case 'any':
+        case 'atcode':
+        case 'fax':
+        case 'phone':
+        case 'email':
+        case 'code30':
+        case 'seq-one-letter-code':
+        case 'author':
+        case 'orcid_id':
+        case 'sequence_dep':
+        case 'pdb_id':
+        case 'emd_id':
+        // todo, consider adding specialised fields
+        case 'yyyy-mm-dd':
+        case 'yyyy-mm-dd:hh:mm':
+        case 'yyyy-mm-dd:hh:mm-flex':
+        case 'int-range':
+        case 'float-range':
+        case 'binary':
+        case 'operation_expression':
+        case 'point_symmetry':
+        case '4x3_matrix':
+        case '3x4_matrices':
+        case 'point_group':
+        case 'point_group_helical':
+        case 'symmetry_operation':
+        case 'date_dep':
+        case 'url':
+        case 'symop':
+        case 'exp_data_doi':
+        case 'asym_id':
+            return StrCol(description)
+        case 'int':
+        case 'non_negative_int':
+        case 'positive_int':
+            return values && values.length ? EnumCol(values, 'int', description) : IntCol(description)
+        case 'float':
+            return FloatCol(description)
+        case 'ec-type':
+        case 'ucode-alphanum-csv':
+        case 'id_list':
+            return ListCol('str', ',', description)
+        case 'id_list_spc':
+            return ListCol('str', ' ', description)
+
+        // cif
+        case 'Text':
+        case 'Code':
+        case 'Complex':
+        case 'Symop':
+        case 'List':
+        case 'List(Real,Real)':
+        case 'List(Real,Real,Real,Real)':
+        case 'Date':
+        case 'Datetime':
+        case 'Tag':
+        case 'Implied':
+            return wrapContainer('str', ',', description, container)
+        case 'Real':
+            return wrapContainer('float', ',', description, container)
+        case 'Integer':
+            return wrapContainer('int', ',', description, container)
+
+    }
+    console.log(`unknown type '${type}'`)
+    return StrCol(description)
+}
+
+function ColFromType(type: 'int' | 'str' | 'float' | 'coord', description: string): Column {
+    switch (type) {
+        case 'int': return IntCol(description)
+        case 'str': return StrCol(description)
+        case 'float': return FloatCol(description)
+        case 'coord': return CoordCol(description)
+    }
+}
+
+function wrapContainer(type: 'int' | 'str' | 'float' | 'coord', separator: string, description: string, container?: string) {
+    return container && container === 'List' ? ListCol(type, separator, description) : ColFromType(type, description)
+}
+
+type FrameCategories = { [category: string]: Data.CifFrame }
+type FrameLinks = { [k: string]: string }
+
+interface FrameData {
+    categories: FrameCategories
+    links: FrameLinks
+}
+
+type Imports = Map<string, CifFrame[]>
+
+function getImportFrames(d: Data.CifFrame, imports: Imports) {
+    const frames: Data.CifFrame[] = []
+    if (!('import' in d.categories)) return frames
+
+    const importGet = parseImportGet(d.categories['import'].getField('get')!.str(0))
+    for (const g of importGet) {
+        const { file, save } = g
+        if (!file || !save) {
+            console.warn(`missing 'save' or 'file' for import in '${d.header}'`)
+            continue
+        }
+        const importFrames = imports.get(file)
+        if (!importFrames) {
+            console.warn(`missing '${file}' entry in imports`)
+            continue
+        }
+        const importSave = importFrames.find(id => id.header.toLowerCase() === save.toLowerCase())
+        if (!importSave) {
+            console.warn(`missing '${save}' save frame in '${file}'`)
+            continue
+        }
+
+        frames.push(importSave)
+    }
+
+    return frames
+}
+
+/** get field from given or linked category */
+function getField(category: string, field: string, d: Data.CifFrame, imports: Imports, ctx: FrameData): Data.CifField|undefined {
+    const { categories, links } = ctx
+    const cat = d.categories[category]
+    if (cat) {
+        return cat.getField(field)
+    } else if (d.header in links) {
+        const linkName = links[d.header]
+        if (linkName in categories) {
+            return getField(category, field, categories[linkName], imports, ctx)
+        } else {
+            // console.log(`link '${linkName}' not found`)
+        }
+    } else {
+        const importFrames = getImportFrames(d, imports)
+        for (const idf of importFrames) {
+            return getField(category, field, idf, imports, ctx)
+        }
+    }
+}
+
+function getEnums(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
+    const value = getField('item_enumeration', 'value', d, imports, ctx)
+    const enums: string[] = []
+    if (value) {
+        for (let i = 0; i < value.rowCount; ++i) {
+            enums.push(value.str(i))
+            // console.log(value.str(i))
+        }
+        return enums
+    } else {
+        // console.log(`item_enumeration.value not found for '${d.header}'`)
+    }
+}
+
+function getContainer(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
+    const value = getField('type', 'container', d, imports, ctx)
+    return value ? value.str(0) : undefined
+}
+
+function getCode(d: Data.CifFrame, imports: Imports, ctx: FrameData): [string, string[] | undefined, string | undefined ] | undefined {
+    const code = getField('item_type', 'code', d, imports, ctx) || getField('type', 'contents', d, imports, ctx)
+    if (code) {
+        return [ code.str(0), getEnums(d, imports, ctx), getContainer(d, imports, ctx) ]
+    } else {
+        console.log(`item_type.code or type.contents not found for '${d.header}'`)
+    }
+}
+
+function getSubCategory(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
+    const value = getField('item_sub_category', 'id', d, imports, ctx)
+    if (value) {
+        return value.str(0)
+    }
+}
+
+function getDescription(d: Data.CifFrame, imports: Imports, ctx: FrameData): string | undefined {
+    const value = getField('item_description', 'description', d, imports, ctx) || getField('description', 'text', d, imports, ctx)
+    if (value) {
+        // trim (after newlines) and remove references to square brackets
+        return value.str(0).trim()
+            .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n')
+            .replace(/(\[[1-3]\])+ element/, 'elements')
+            .replace(/(\[[1-3]\])+/, '')
+    }
+}
+
+function getAliases(d: Data.CifFrame, imports: Imports, ctx: FrameData): string[] | undefined {
+    const value = getField('item_aliases', 'alias_name', d, imports, ctx) || getField('alias', 'definition_id', d, imports, ctx)
+    return value ? value.toStringArray().map(v => v.substr(1)) : undefined
+}
+
+const reMatrixField = /\[[1-3]\]\[[1-3]\]/
+const reVectorField = /\[[1-3]\]/
+
+const FORCE_INT_FIELDS = [
+    '_atom_site.id',
+    '_atom_site.auth_seq_id',
+    '_atom_site_anisotrop.id',
+    '_pdbx_struct_mod_residue.auth_seq_id',
+    '_struct_conf.beg_auth_seq_id',
+    '_struct_conf.end_auth_seq_id',
+    '_struct_conn.ptnr1_auth_seq_id',
+    '_struct_conn.ptnr2_auth_seq_id',
+    '_struct_sheet_range.beg_auth_seq_id',
+    '_struct_sheet_range.end_auth_seq_id',
+];
+
+const COMMA_SEPARATED_LIST_FIELDS = [
+    '_atom_site.pdbx_struct_group_id',
+    '_chem_comp.mon_nstd_parent_comp_id',
+    '_diffrn_radiation.pdbx_wavelength_list',
+    '_diffrn_source.pdbx_wavelength_list',
+    '_em_diffraction.tilt_angle_list', // 20,40,50,55
+    '_em_entity_assembly.entity_id_list',
+    '_entity.pdbx_description', // Endolysin,Beta-2 adrenergic receptor
+    '_entity.pdbx_ec',
+    '_entity_poly.pdbx_strand_id', // A,B
+    '_entity_src_gen.pdbx_gene_src_gene', // ADRB2, ADRB2R, B2AR
+    '_pdbx_depui_entry_details.experimental_methods',
+    '_pdbx_depui_entry_details.requested_accession_types',
+    '_pdbx_soln_scatter_model.software_list', // INSIGHT II, HOMOLOGY, DISCOVERY, BIOPOLYMER, DELPHI
+    '_pdbx_soln_scatter_model.software_author_list', // MSI
+    '_pdbx_soln_scatter_model.entry_fitting_list', // Odd example: 'PDB CODE 1HFI, 1HCC, 1HFH, 1VCC'
+    '_pdbx_struct_assembly_gen.entity_inst_id',
+    '_pdbx_struct_assembly_gen.asym_id_list',
+    '_pdbx_struct_assembly_gen.auth_asym_id_list',
+    '_pdbx_struct_assembly_gen_depositor_info.asym_id_list',
+    '_pdbx_struct_assembly_gen_depositor_info.chain_id_list',
+    '_pdbx_struct_group_list.group_enumeration_type',
+    '_reflns.pdbx_diffrn_id',
+    '_refine.pdbx_diffrn_id',
+    '_reflns_shell.pdbx_diffrn_id',
+    '_struct_keywords.text',
+];
+
+const SPACE_SEPARATED_LIST_FIELDS = [
+    '_chem_comp.pdbx_subcomponent_list', // TSM DPH HIS CHF EMR
+    '_pdbx_soln_scatter.data_reduction_software_list', // OTOKO
+    '_pdbx_soln_scatter.data_analysis_software_list', // SCTPL5 GNOM
+];
+
+const SEMICOLON_SEPARATED_LIST_FIELDS = [
+    '_chem_comp.pdbx_synonyms' // GLYCERIN; PROPANE-1,2,3-TRIOL
+]
+
+/**
+ * Useful when a dictionary extension will add enum values to an existing dictionary.
+ * By adding them here, the dictionary extension can be tested before the added enum
+ * values are available in the existing dictionary.
+ */
+const EXTRA_ENUM_VALUES: { [k: string]: string[] } = {
+
+}
+
+export function generateSchema(frames: CifFrame[], imports: Imports = new Map()): Database {
+
+    const tables: Database['tables'] = {}
+    const aliases: Database['aliases'] = {}
+
+    const categories: FrameCategories = {}
+    const links: FrameLinks = {}
+    const ctx = { categories, links }
+
+    // get category metadata
+    frames.forEach(d => {
+        // category definitions in mmCIF start with '_' and don't include a '.'
+        // category definitions in cif don't include a '.'
+        if (d.header[0] === '_'  || d.header.includes('.')) return
+        const categoryName = d.header.toLowerCase()
+        // console.log(d.header, d.categoryNames, d.categories)
+        let descriptionField: Data.CifField | undefined
+        const categoryKeyNames = new Set<string>()
+
+        if ('category' in d.categories && 'category_key' in d.categories) {
+            const category = d.categories['category']
+            const categoryKey = d.categories['category_key']
+            if (categoryKey) {
+                const categoryKey_names = categoryKey.getField('name')
+                if (categoryKey_names) {
+                    for (let i = 0, il = categoryKey_names.rowCount; i < il; ++i) {
+                        categoryKeyNames.add(categoryKey_names.str(i))
+                    }
+                }
+            }
+
+            descriptionField = category.getField('description')
+
+            if (categoryKeyNames.size === 0) {
+                console.log(`no key given for category '${categoryName}'`)
+            }
+        }
+
+        if ('description' in d.categories) {
+            descriptionField = d.categories['description'].getField('text')
+        }
+
+        let description = ''
+        if (descriptionField) {
+            description = descriptionField.str(0).trim()
+                .replace(/(\r\n|\r|\n)([ \t]+)/g, '\n') // remove padding after newlines
+        } else {
+            console.log(`no description given for category '${categoryName}'`)
+        }
+
+        tables[categoryName] = { description, key: categoryKeyNames, columns: {} }
+
+        // console.log('++++++++++++++++++++++++++++++++++++++++++')
+        // console.log('name', categoryName)
+        // console.log('desc', description)
+        // console.log('key', categoryKeyNames)
+    })
+
+    // build list of links between categories
+    frames.forEach(d => {
+        if (d.header[0] !== '_' && !d.header.includes('.')) return
+        categories[d.header] = d
+        const item_linked = d.categories['item_linked']
+        if (item_linked) {
+            const child_name = item_linked.getField('child_name')
+            const parent_name = item_linked.getField('parent_name')
+            if (child_name && parent_name) {
+                for (let i = 0; i < item_linked.rowCount; ++i) {
+                    const childName = child_name.str(i)
+                    const parentName = parent_name.str(i)
+                    if (childName in links && links[childName] !== parentName) {
+                        console.log(`${childName} linked to ${links[childName]}, ignoring link to ${parentName}`)
+                    }
+                    links[childName] = parentName
+                }
+            }
+        }
+    })
+
+    // get field data
+    Object.keys(categories).forEach(fullName => {
+        const d = categories[fullName]
+        if (!d) {
+            console.log(`'${fullName}' not found, moving on`)
+            return
+        }
+
+        const categoryName = d.header.substring(d.header[0] === '_' ? 1 : 0, d.header.indexOf('.'))
+        const itemName = d.header.substring(d.header.indexOf('.') + 1)
+        let fields: { [k: string]: Column }
+        if (categoryName in tables) {
+            fields = tables[categoryName].columns
+            tables[categoryName].key.add(itemName)
+        } else if (categoryName.toLowerCase() in tables) {
+            // take case from category name in 'field' data as it is better if data is from cif dictionaries
+            tables[categoryName] = tables[categoryName.toLowerCase()]
+            fields = tables[categoryName].columns
+        } else {
+            console.log(`category '${categoryName}' has no metadata`)
+            fields = {}
+            tables[categoryName] = {
+                description: '',
+                key: new Set(),
+                columns: fields
+            }
+        }
+
+        const itemAliases = getAliases(d, imports, ctx)
+        if (itemAliases) aliases[`${categoryName}.${itemName}`] = itemAliases
+
+        const description = getDescription(d, imports, ctx) || ''
+
+        // need to use regex to check for matrix or vector items
+        // as sub_category assignment is missing for some entries
+        const subCategory = getSubCategory(d, imports, ctx)
+        if (subCategory === 'cartesian_coordinate' || subCategory === 'fractional_coordinate') {
+            fields[itemName] = CoordCol(description)
+        } else if (FORCE_INT_FIELDS.includes(d.header)) {
+            fields[itemName] = IntCol(description)
+            console.log(`forcing int: ${d.header}`)
+        } else if (subCategory === 'matrix') {
+            fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description)
+        } else if (subCategory === 'vector') {
+            fields[itemName.replace(reVectorField, '')] = VectorCol(3, description)
+        } else {
+            if (itemName.match(reMatrixField)) {
+                fields[itemName.replace(reMatrixField, '')] = MatrixCol(3, 3, description)
+                console.log(`${d.header} should have 'matrix' _item_sub_category.id`)
+            } else if (itemName.match(reVectorField)) {
+                fields[itemName.replace(reVectorField, '')] = VectorCol(3, description)
+                console.log(`${d.header} should have 'vector' _item_sub_category.id`)
+            } else {
+                const code = getCode(d, imports, ctx)
+                if (code) {
+                    let fieldType = getFieldType(code[0], description, code[1], code[2]);
+                    if (fieldType.type === 'str') {
+                        if (COMMA_SEPARATED_LIST_FIELDS.includes(d.header)) {
+                            fieldType = ListCol('str', ',', description)
+                            console.log(`forcing comma separated: ${d.header}`)
+                        } else if (SPACE_SEPARATED_LIST_FIELDS.includes(d.header)) {
+                            fieldType = ListCol('str', ' ', description)
+                            console.log(`forcing space separated: ${d.header}`)
+                        } else if (SEMICOLON_SEPARATED_LIST_FIELDS.includes(d.header)) {
+                            fieldType = ListCol('str', ';', description)
+                            console.log(`forcing space separated: ${d.header}`)
+                        }
+                    }
+                    if (d.header in EXTRA_ENUM_VALUES) {
+                        if (fieldType.type === 'enum') {
+                            fieldType.values.push(...EXTRA_ENUM_VALUES[d.header])
+                        } else {
+                            console.warn(`expected enum: ${d.header}`)
+                        }
+                    }
+                    fields[itemName] = fieldType
+                } else {
+                    fields[itemName] = StrCol(description)
+                    // console.log(`could not determine code for '${d.header}'`)
+                }
+            }
+        }
+    })
+
+    return { tables, aliases }
+}

+ 151 - 0
src/apps/cifschema/util/generate.ts

@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Database, Filter, Column } from './schema'
+import { indentString } from '../../../mol-util/string'
+import { FieldPath } from '../../../mol-io/reader/cif/schema';
+
+function header (name: string, info: string, moldataImportPath: string) {
+    return `/**
+ * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * Code-generated '${name}' schema file. ${info}
+ *
+ * @author molstar/ciftools package
+ */
+
+import { Database, Column } from '${moldataImportPath}/db'
+
+import Schema = Column.Schema`
+}
+
+function footer (name: string) {
+    return `
+export type ${name}_Schema = typeof ${name}_Schema;
+export interface ${name}_Database extends Database<${name}_Schema> {}`
+}
+
+function getTypeShorthands(schema: Database, fields?: Filter) {
+    const types = new Set<string>()
+    Object.keys(schema.tables).forEach(table => {
+        if (fields && !fields[table]) return
+        const { columns } = schema.tables[table]
+        Object.keys(columns).forEach(columnName => {
+            if (fields && !fields[table][columnName]) return
+            types.add(schema.tables[table].columns[columnName].type)
+        })
+    })
+    const shorthands: string[] = []
+    types.forEach(type => {
+        switch (type) {
+            case 'str': shorthands.push('const str = Schema.str;'); break
+            case 'int': shorthands.push('const int = Schema.int;'); break
+            case 'float': shorthands.push('const float = Schema.float;'); break
+            case 'coord': shorthands.push('const coord = Schema.coord;'); break
+            case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break
+            case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break
+            case 'vector': shorthands.push('const Vector = Schema.Vector;'); break
+            case 'list': shorthands.push('const List = Schema.List;'); break
+        }
+    })
+    return shorthands.join('\n')
+}
+
+function getTypeDef(c: Column): string {
+    switch (c.type) {
+        case 'str': return 'str'
+        case 'int': return 'int'
+        case 'float': return 'float'
+        case 'coord': return 'coord'
+        case 'enum':
+            return `Aliased<'${c.values.map(v => v.replace(/'/g, '\\\'')).join(`' | '`)}'>(${c.subType})`
+        case 'matrix':
+            return `Matrix(${c.rows}, ${c.columns})`
+        case 'vector':
+            return `Vector(${c.length})`
+        case 'list':
+            if (c.subType === 'int') {
+                return `List('${c.separator}', x => parseInt(x, 10))`
+            } else if (c.subType === 'float' || c.subType === 'coord') {
+                return `List('${c.separator}', x => parseFloat(x))`
+            } else {
+                return `List('${c.separator}', x => x)`
+            }
+    }
+}
+
+const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/
+function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'` }
+
+function doc(description: string, spacesCount: number) {
+    const spaces = ' '.repeat(spacesCount)
+    return [
+        `${spaces}/**`,
+        `${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'),
+        `${spaces} */`
+    ].join('\n')
+}
+
+export function generate (name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
+    const codeLines: string[] = []
+
+    if (fields) {
+        Object.keys(fields).forEach(table => {
+            if (table in schema.tables) {
+                const schemaTable = schema.tables[table]
+                Object.keys(fields[table]).forEach(column => {
+                    if (!(column in schemaTable.columns)) {
+                        console.log(`filter field '${table}.${column}' not found in schema`)
+                    }
+                })
+            } else {
+                console.log(`filter category '${table}' not found in schema`)
+            }
+        })
+    }
+
+    codeLines.push(`export const ${name}_Schema = {`)
+    Object.keys(schema.tables).forEach(table => {
+        if (fields && !fields[table]) return
+        const { description, columns } = schema.tables[table]
+        if (description) codeLines.push(doc(description, 4))
+        codeLines.push(`    ${safePropertyString(table)}: {`)
+        Object.keys(columns).forEach(columnName => {
+            if (fields && !fields[table][columnName]) return
+            const c = columns[columnName]
+            const typeDef = getTypeDef(c)
+            if (c.description) codeLines.push(doc(c.description, 8))
+            codeLines.push(`        ${safePropertyString(columnName)}: ${typeDef},`)
+        })
+        codeLines.push('    },')
+    })
+    codeLines.push('}')
+
+    if (addAliases) {
+        codeLines.push('')
+        codeLines.push(`export const ${name}_Aliases = {`)
+        Object.keys(schema.aliases).forEach(path => {
+            const [ table, columnName ] = path.split('.')
+            if (fields && !fields[table]) return
+            if (fields && !fields[table][columnName]) return
+
+            const filteredAliases = new Set<string>()
+            schema.aliases[path].forEach(p => {
+                if (!FieldPath.equal(p, path)) filteredAliases.add(FieldPath.canonical(p))
+            })
+
+            if (filteredAliases.size === 0) return
+            codeLines.push(`    ${safePropertyString(path)}: [`)
+            filteredAliases.forEach(alias => {
+                codeLines.push(`        '${alias}',`)
+            })
+            codeLines.push('    ],')
+        })
+        codeLines.push('}')
+    }
+
+    return `${header(name, info, moldataImportPath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}`
+}

+ 20 - 0
src/apps/cifschema/util/helper.ts

@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export type Import = { save?: string, file?: string }
+
+export function parseImportGet(s: string): Import[] {
+    // [{'save':hi_ang_Fox_coeffs  'file':templ_attr.cif}   {'save':hi_ang_Fox_c0  'file':templ_enum.cif}]
+    // [{"file":'templ_enum.cif' "save":'H_M_ref'}]
+    return s.trim().substring(2, s.length - 2).split(/}[ \n\t]*{/g).map(s => {
+        const save = s.match(/('save'|"save"):([^ \t\n]+)/)
+        const file = s.match(/('file'|"file"):([^ \t\n]+)/)
+        return {
+            save: save ? save[0].substr(7).replace(/['"]/g, '') : undefined,
+            file: file ? file[0].substr(7).replace(/['"]/g, '') : undefined
+        }
+    })
+}

+ 77 - 0
src/apps/cifschema/util/schema.ts

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export interface Database {
+    tables: { [ tableName: string ]: Table }
+    aliases: { [ path: string ]: string[] }
+}
+export interface Table {
+    description: string
+    key: Set<string>
+    columns: { [ columnName: string ]: Column }
+}
+export type Column = IntCol | StrCol | FloatCol | CoordCol | EnumCol | VectorCol | MatrixCol | ListCol
+
+type BaseCol = { description: string }
+
+export type IntCol = { type: 'int' } & BaseCol
+export function IntCol(description: string): IntCol { return { type: 'int', description } }
+
+export type StrCol = { type: 'str' } & BaseCol
+export function StrCol(description: string): StrCol { return { type: 'str', description } }
+
+export type FloatCol = { type: 'float' } & BaseCol
+export function FloatCol(description: string): FloatCol { return { type: 'float', description } }
+
+export type CoordCol = { type: 'coord' } & BaseCol
+export function CoordCol(description: string): CoordCol { return { type: 'coord', description } }
+
+export type EnumCol = { type: 'enum', subType: 'int' | 'str', values: string[] } & BaseCol
+export function EnumCol(values: string[], subType: 'int' | 'str', description: string): EnumCol {
+    return { type: 'enum', description, values, subType }
+}
+
+export type VectorCol = { type: 'vector', length: number } & BaseCol
+export function VectorCol(length: number, description: string): VectorCol {
+    return { type: 'vector', description, length }
+}
+
+export type MatrixCol = { type: 'matrix', rows: number, columns: number } & BaseCol
+export function MatrixCol(columns: number, rows: number, description: string): MatrixCol {
+    return { type: 'matrix', description, columns, rows }
+}
+
+export type ListCol = { type: 'list', subType: 'int' | 'str' | 'float' | 'coord', separator: string } & BaseCol
+export function ListCol(subType: 'int' | 'str' | 'float' | 'coord', separator: string, description: string): ListCol {
+    return { type: 'list', description, separator, subType }
+}
+
+export type Filter = { [ table: string ]: { [ column: string ]: true } }
+
+export function mergeFilters (...filters: Filter[]) {
+    const n = filters.length
+    const mergedFilter: Filter = {}
+    const fields: Map<string, number> = new Map()
+    filters.forEach(filter => {
+        Object.keys(filter).forEach(category => {
+            Object.keys(filter[ category ]).forEach(field => {
+                const key = `${category}.${field}`
+                const value = fields.get(key) || 0
+                fields.set(key, value + 1)
+            })
+        })
+    })
+    fields.forEach((v, k) => {
+        if (v !== n) return
+        const [categoryName, fieldName] = k.split('.')
+        if (categoryName in mergedFilter) {
+            mergedFilter[categoryName][fieldName] = true
+        } else {
+            mergedFilter[categoryName] = { fieldName: true }
+        }
+    })
+    return mergedFilter
+}

+ 0 - 178
src/apps/demos/lighting/index.ts

@@ -1,178 +0,0 @@
-/**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
-import './index.html'
-import { PluginContext } from '../../../mol-plugin/context';
-import { PluginCommands } from '../../../mol-plugin/commands';
-import { StateTransforms } from '../../../mol-plugin-state/transforms';
-import { PluginStateObject as PSO } from '../../../mol-plugin-state/objects';
-import { StateBuilder } from '../../../mol-state';
-import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
-import { createStructureRepresentationParams } from '../../../mol-plugin-state/helpers/structure-representation-params';
-require('mol-plugin-ui/skin/light.scss')
-
-type SupportedFormats = 'cif' | 'pdb'
-type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
-
-const Canvas3DPresets = {
-    illustrative: {
-        multiSample: {
-            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
-        },
-        postprocessing: {
-            occlusionEnable: true,
-            occlusionBias: 0.8,
-            occlusionKernelSize: 6,
-            outlineEnable: true,
-        },
-        renderer: {
-            ambientIntensity: 1,
-            lightIntensity: 0,
-        }
-    },
-    occlusion: {
-        multiSample: {
-            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
-        },
-        postprocessing: {
-            occlusionEnable: true,
-            occlusionBias: 0.8,
-            occlusionKernelSize: 6,
-            outlineEnable: false,
-        },
-        renderer: {
-            ambientIntensity: 0.4,
-            lightIntensity: 0.6,
-        }
-    },
-    standard: {
-        multiSample: {
-            mode: 'off' as Canvas3DProps['multiSample']['mode']
-        },
-        postprocessing: {
-            occlusionEnable: false,
-            outlineEnable: false,
-        },
-        renderer: {
-            ambientIntensity: 0.4,
-            lightIntensity: 0.6,
-        }
-    }
-}
-
-type Canvas3DPreset = keyof typeof Canvas3DPresets
-
-function getPreset(preset: Canvas3DPreset) {
-    switch (preset) {
-        case 'illustrative': return Canvas3DPresets['illustrative']
-        case 'standard': return Canvas3DPresets['standard']
-        case 'occlusion': return Canvas3DPresets['occlusion']
-    }
-}
-
-class LightingDemo {
-    plugin: PluginContext;
-
-    init(target: string | HTMLElement) {
-        this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
-            ...DefaultPluginSpec,
-            layout: {
-                initial: {
-                    isExpanded: false,
-                    showControls: false
-                },
-                controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
-            }
-        });
-
-        this.setPreset('illustrative');
-    }
-
-    setPreset(preset: Canvas3DPreset) {
-        const props = getPreset(preset)
-        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
-            ...props,
-            multiSample: {
-                ...this.plugin.canvas3d!.props.multiSample,
-                ...props.multiSample
-            },
-            renderer: {
-                ...this.plugin.canvas3d!.props.renderer,
-                ...props.renderer
-            },
-            postprocessing: {
-                ...this.plugin.canvas3d!.props.postprocessing,
-                ...props.postprocessing
-            },
-        }});
-    }
-
-    private download(b: StateBuilder.To<PSO.Root>, url: string) {
-        return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
-    }
-
-    private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
-        const parsed = format === 'cif'
-            ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
-            : b.apply(StateTransforms.Model.TrajectoryFromPDB);
-
-        const props = {
-            type: {
-                name: 'assembly' as const,
-                params: { id: assemblyId || 'deposited' }
-            }
-        }
-        return parsed
-            .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
-            .apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
-    }
-
-    private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
-        visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
-            .apply(StateTransforms.Representation.StructureRepresentation3D,
-                createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
-        return visualRoot;
-    }
-
-    private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
-    async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
-        let loadType: 'full' | 'update' = 'full';
-
-        const state = this.plugin.state.data;
-
-        if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
-            loadType = 'full';
-        } else if (this.loadedParams.url === url) {
-            if (state.select('asm').length > 0) loadType = 'update';
-        }
-
-        let tree: StateBuilder.Root;
-        if (loadType === 'full') {
-            await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
-            tree = state.build();
-            this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
-        } else {
-            const props = {
-                type: {
-                    name: 'assembly' as const,
-                    params: { id: assemblyId || 'deposited' }
-                }
-            }
-            tree = state.build();
-            tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
-        }
-
-        await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
-        this.loadedParams = { url, format, assemblyId };
-        PluginCommands.Camera.Reset(this.plugin, { });
-    }
-}
-
-(window as any).LightingDemo = new LightingDemo();

+ 57 - 107
src/apps/viewer/extensions/cellpack/model.ts

@@ -8,9 +8,8 @@ import { StateAction, StateBuilder, StateTransformer, State } from '../../../../
 import { PluginContext } from '../../../../mol-plugin/context';
 import { PluginStateObject as PSO } from '../../../../mol-plugin-state/objects';
 import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
-import { Ingredient,IngredientSource, CellPacking } from './data';
-import { getFromPdb, getFromCellPackDB } from './util';
-import { computeStructureBoundary } from '../../../../mol-model/structure/structure/util/boundary';
+import { Ingredient, CellPacking } from './data';
+import { getFromPdb, getFromCellPackDB, IngredientFiles, parseCif, parsePDBfile } from './util';
 import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
 import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
 import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
@@ -35,54 +34,52 @@ function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`
 }
 
-async function getModel(id: string, source:IngredientSource, baseUrl: string) {
+async function getModel(id: string, baseUrl: string, file?: File) {
     let model: Model;
-    const mid = source.model? parseInt(source.model) : 0;
-    if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
+    if (file) {
+        const text = await file.text()
+        if (file.name.endsWith('.cif')) {
+            const cif = (await parseCif(text)).blocks[0]
+            model = (await trajectoryFromMmCIF(cif).run())[0]
+        } else if (file.name.endsWith('.pdb')) {
+            const pdb = await parsePDBfile(text, id)
+
+            model = (await trajectoryFromPDB(pdb).run())[0]
+        } else {
+            throw new Error(`unsupported file type '${file.name}'`)
+        }
+    } else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
         // return
         const cif = await getFromPdb(id)
-        model = (await trajectoryFromMmCIF(cif).run())[mid]
+        model = (await trajectoryFromMmCIF(cif).run())[0]
     } else {
         const pdb = await getFromCellPackDB(id, baseUrl)
-        model = (await trajectoryFromPDB(pdb).run())[mid]
+        model = (await trajectoryFromPDB(pdb).run())[0]
     }
     return model
 }
 
-async function getStructure(model: Model, source:IngredientSource, props: { assembly?: string } = {}) {
+async function getStructure(model: Model, props: { assembly?: string } = {}) {
     let structure = Structure.ofModel(model)
     const { assembly } = props
 
     if (assembly) {
         structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
     }
-    if (source.selection){
-        //use NGL selection string or :A or :B etc...
-        const asymIds:string[] = source.selection.replace(" :","").split(" or")
-        console.log(asymIds)
-        const query = MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'chain-test': MS.core.set.has([MS.set(...asymIds), MS.ammp('label_asym_id')])
-            })
-        ])
-        const compiled = compile<StructureSelection>(query)
-        const result = compiled(new QueryContext(structure))
-        structure = StructureSelection.unionStructure(result)
-    }
-    else {
-        const query = MS.struct.modifier.union([
-            MS.struct.generator.atomGroups({
-                'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
-            })
-        ])
-        const compiled = compile<StructureSelection>(query)
-        const result = compiled(new QueryContext(structure))
-        structure = StructureSelection.unionStructure(result)
-    }
+
+    const query = MS.struct.modifier.union([
+        MS.struct.generator.atomGroups({
+            'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
+        })
+    ])
+    const compiled = compile<StructureSelection>(query)
+    const result = compiled(new QueryContext(structure))
+    structure = StructureSelection.unionStructure(result)
+
     return structure
 }
 
-function getTransformLegacy(trans: Vec3, rot: Quat) {
+function getTransform(trans: Vec3, rot: Quat) {
     const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
     const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
     Mat4.transpose(m, m)
@@ -91,25 +88,13 @@ function getTransformLegacy(trans: Vec3, rot: Quat) {
     return m
 }
 
-function getTransform(trans: Vec3, rot: Quat) {
-    const q: Quat = Quat.create(rot[0], rot[1], rot[2], rot[3])
-    const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
-    const p: Vec3 = Vec3.create(trans[0],trans[1],trans[2])
-    Mat4.setTranslation(m, p)
-    return m
-}
-
-function getResultTransforms(results: Ingredient['results'],legacy:boolean) {
-    if (legacy) return results.map((r: Ingredient['results'][0]) => getTransformLegacy(r[0], r[1]))
-    else return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
+function getResultTransforms(results: Ingredient['results']) {
+    return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
 }
 
 function getCurveTransforms(ingredient: Ingredient) {
     const n = ingredient.nbCurve || 0
     const instances: Mat4[] = []
-    const segmentLength = (ingredient.radii)? ((ingredient.radii[0].radii)?ingredient.radii[0].radii[0]*2.0:3.4):3.4;
-    console.log(ingredient.radii);
-    console.log(segmentLength);
 
     for (let i = 0; i < n; ++i) {
         const cname = `curve${i}`
@@ -124,7 +109,7 @@ function getCurveTransforms(ingredient: Ingredient) {
         }
         const points = new Float32Array(_points.length * 3)
         for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
-        const newInstances = getMatFromResamplePoints(points,segmentLength)
+        const newInstances = getMatFromResamplePoints(points)
         instances.push(...newInstances)
     }
 
@@ -239,7 +224,7 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
     };
 }
 
-async function getCurve(name: string, ingredient:Ingredient, transforms: Mat4[], model: Model) {
+async function getCurve(name: string, transforms: Mat4[], model: Model) {
     const cif = getCifCurve(name, transforms, model)
 
     const curveModelTask = Task.create('Curve Model', async ctx => {
@@ -249,78 +234,41 @@ async function getCurve(name: string, ingredient:Ingredient, transforms: Mat4[],
     })
 
     const curveModel = await curveModelTask.run()
-    return getStructure(curveModel,ingredient.source)
+    return getStructure(curveModel)
 }
 
-async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
+async function getIngredientStructure(ingredient: Ingredient, baseUrl: string, ingredientFiles: IngredientFiles) {
     const { name, source, results, nbCurve } = ingredient
-
-    // TODO can these be added to the library?
-    if (name === 'HIV1_CAhex_0_1_0') return
-    if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
-    if (name === 'iLDL') return
-    if (name === 'peptides') return
-    if (name === 'lypoglycane') return
-
     if (source.pdb === 'None') return
 
-    const model = await getModel(source.pdb || name,source, baseUrl)
+    const file = ingredientFiles[source.pdb]
+    if (!file) {
+        // TODO can these be added to the library?
+        if (name === 'HIV1_CAhex_0_1_0') return
+        if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
+        if (name === 'iLDL') return
+        if (name === 'peptides') return
+        if (name === 'lypoglycane') return
+    }
+
+    const model = await getModel(source.pdb || name, baseUrl, file)
     if (!model) return
 
     if (nbCurve) {
-        return getCurve(name,ingredient, getCurveTransforms(ingredient), model)
+        return getCurve(name, getCurveTransforms(ingredient), model)
     } else {
-        let bu:string|undefined = source.bu ? source.bu : undefined;
-        if (bu){
-            if (bu=="AU") {bu = undefined;}
-            else {
-                bu=bu.slice(2)
-            }
-        }
-        let structure = await getStructure(model,source, { assembly: bu })
-        //transform with offset and pcp
-        let legacy:boolean = true
-        if (ingredient.offset || ingredient.principalAxis)
-        //center the structure
-        {
-            legacy=false
-            const boundary = computeStructureBoundary(structure)
-            let modelCenter:Vec3 = Vec3.zero()
-            Vec3.scale(modelCenter,boundary.sphere.center,-1.0);//Model.getCenter(structure.models[0])
-            const m1: Mat4 = Mat4.identity()
-            Mat4.setTranslation(m1, modelCenter)
-            structure = Structure.transform(structure,m1)
-            if (ingredient.offset)
-            {
-                if (!Vec3.exactEquals(ingredient.offset,Vec3.zero()))
-                { 
-                    const m: Mat4 = Mat4.identity();
-                    Mat4.setTranslation(m, ingredient.offset)
-                    structure = Structure.transform(structure,m);
-                }
-            }
-            if (ingredient.principalAxis)
-            {
-                if (!Vec3.exactEquals(ingredient.principalAxis,Vec3.create(0, 0, 1)))
-                {            
-                    const q: Quat = Quat.identity();
-                    Quat.rotationTo(q,ingredient.principalAxis,Vec3.create(0, 0, 1))
-                    const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
-                    structure = Structure.transform(structure,m);
-                }
-            }
-        }
-        return getAssembly(getResultTransforms(results,legacy), structure)
+        const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
+        return getAssembly(getResultTransforms(results), structure)
     }
 }
 
-export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
+export function createStructureFromCellPack(packing: CellPacking, baseUrl: string, ingredientFiles: IngredientFiles) {
     return Task.create('Create Packing Structure', async ctx => {
         const { ingredients, name } = packing
         const structures: Structure[] = []
         for (const iName in ingredients) {
             if (ctx.shouldUpdate) await ctx.update(iName)
-            const s = await getIngredientStructure(ingredients[iName], baseUrl)
+            const s = await getIngredientStructure(ingredients[iName], baseUrl, ingredientFiles)
             if (s) structures.push(s)
         }
 
@@ -411,7 +359,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
     await handleHivRna({ runtime, fetch: plugin.fetch }, packings, params.baseUrl)
 
     for (let i = 0, il = packings.length; i < il; ++i) {
-        const p = { packing: i, baseUrl: params.baseUrl }
+        const p = { packing: i, baseUrl: params.baseUrl, ingredientFiles: params.ingredients.files }
 
         const packing = state.build().to(cellPackBuilder.ref).apply(StructureFromCellpack, p)
         await plugin.updateDataState(packing, { revertOnError: true });
@@ -439,12 +387,14 @@ const LoadCellPackModelParams = {
             ['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
             ['hiv_lipids.bcif', 'hiv_lipids'],
             ['influenza_model1.json', 'influenza_model1'],
-            ['ExosomeModel.json', 'ExosomeModel'],
             ['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
         ] as const),
         'file': PD.File({ accept: 'id' }),
     }, { options: [['id', 'Id'], ['file', 'File']] }),
     baseUrl: PD.Text(DefaultCellPackBaseUrl),
+    ingredients : PD.Group({
+        files: PD.FileList({ accept: '.cif,.pdb' })
+    }, { isExpanded: true }),
     preset: PD.Group({
         traceOnly: PD.Boolean(false),
         representation: PD.Select('gaussian-surface', PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid']))
@@ -466,4 +416,4 @@ export const LoadCellPackModel = StateAction.build({
     } else {
         await loadPackings(ctx, taskCtx, state, params)
     }
-}));
+}));

+ 11 - 3
src/apps/viewer/extensions/cellpack/state.ts

@@ -9,6 +9,7 @@ import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
 import { Task } from '../../../../mol-task';
 import { CellPack as _CellPack, Cell, CellPacking } from './data';
 import { createStructureFromCellPack } from './model';
+import { IngredientFiles } from './util';
 
 export const DefaultCellPackBaseUrl = 'https://mesoscope.scripps.edu/data/cellPACK_data/cellPACK_database_1.1.0/'
 
@@ -53,20 +54,27 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
         if (!a) {
             return {
                 packing: PD.Numeric(0, {}, { description: 'Packing Index' }),
-                baseUrl: PD.Text(DefaultCellPackBaseUrl)
+                baseUrl: PD.Text(DefaultCellPackBaseUrl),
+                ingredientFiles: PD.FileList({ accept: '.cif,.pdb' })
             };
         }
         const options = a.data.packings.map((d, i) => [i, d.name] as [number, string])
         return {
             packing: PD.Select(0, options),
-            baseUrl: PD.Text(DefaultCellPackBaseUrl)
+            baseUrl: PD.Text(DefaultCellPackBaseUrl),
+            ingredientFiles: PD.FileList({ accept: '.cif,.pdb' })
         }
     }
 })({
     apply({ a, params }) {
         return Task.create('Structure from CellPack', async ctx => {
             const packing = a.data.packings[params.packing]
-            const structure = await createStructureFromCellPack(packing, params.baseUrl).runInContext(ctx)
+            const ingredientFiles: IngredientFiles = {}
+            for (let i = 0, il = params.ingredientFiles.length; i < il; ++i) {
+                const file = params.ingredientFiles.item(i)
+                if (file) ingredientFiles[file.name] = file
+            }
+            const structure = await createStructureFromCellPack(packing, params.baseUrl, ingredientFiles).runInContext(ctx)
             return new PSO.Molecule.Structure(structure, { label: packing.name })
         });
     }

+ 5 - 3
src/apps/viewer/extensions/cellpack/util.ts

@@ -7,14 +7,14 @@
 import { CIF } from '../../../../mol-io/reader/cif'
 import { parsePDB } from '../../../../mol-io/reader/pdb/parser';
 
-async function parseCif(data: string|Uint8Array) {
+export async function parseCif(data: string|Uint8Array) {
     const comp = CIF.parse(data);
     const parsed = await comp.run();
     if (parsed.isError) throw parsed;
     return parsed.result;
 }
 
-async function parsePDBfile(data: string, id: string) {
+export async function parsePDBfile(data: string, id: string) {
     const comp = parsePDB(data, id);
     const parsed = await comp.run();
     if (parsed.isError) throw parsed;
@@ -45,4 +45,6 @@ export async function getFromCellPackDB(id: string, baseUrl: string) {
     const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
     const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
     return parsed;
-}
+}
+
+export type IngredientFiles = { [name: string]: File }

+ 0 - 0
src/apps/basic-wrapper/coloring.ts → src/examples/basic-wrapper/coloring.ts


+ 16 - 0
src/examples/basic-wrapper/controls.tsx

@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { PluginUIComponent } from '../../mol-plugin-ui/base';
+import * as React from 'react';
+
+export class CustomToastMessage extends PluginUIComponent {
+    render() {
+        return <>
+            Custom <i>Toast</i> content. No timeout.
+        </>;
+    }
+}

+ 3 - 2
src/apps/basic-wrapper/index.html → src/examples/basic-wrapper/index.html

@@ -50,7 +50,7 @@
             <input type='text' id='url' placeholder='url' />
             <input type='text' id='assemblyId' placeholder='assembly id' />
             <select id='format'>
-                <option value='cif' selected>CIF</option>
+                <option value='mmcif' selected>mmCIF</option>
                 <option value='pdb'>PDB</option>
             </select>
         </div>
@@ -60,7 +60,7 @@
         
             var pdbId = '1grm', assemblyId= '1';
             var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
-            var format = 'cif';
+            var format = 'mmcif';
             
             $('url').value = url;
             $('url').onchange = function (e) { url = e.target.value; }
@@ -104,6 +104,7 @@
             addHeader('Misc');
 
             addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
+            addControl('Default Coloring', () => BasicMolStarWrapper.coloring.applyDefault());
 
             addHeader('Interactivity');
             addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());

+ 155 - 0
src/examples/basic-wrapper/index.ts

@@ -0,0 +1,155 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { EmptyLoci } from '../../mol-model/loci';
+import { StructureSelection } from '../../mol-model/structure';
+import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
+import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
+import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
+import { PluginStateObject } from '../../mol-plugin-state/objects';
+import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
+import { PluginCommands } from '../../mol-plugin/commands';
+import { PluginContext } from '../../mol-plugin/context';
+import { Script } from '../../mol-script/script';
+import { Color } from '../../mol-util/color';
+import { StripedResidues } from './coloring';
+import { CustomToastMessage } from './controls';
+import './index.html';
+import { buildStaticSuperposition, dynamicSuperpositionTest, StaticSuperpositionTestData } from './superposition';
+require('mol-plugin-ui/skin/light.scss')
+
+type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
+
+class BasicWrapper {
+    plugin: PluginContext;
+
+    init(target: string | HTMLElement) {
+        this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
+            ...DefaultPluginSpec,
+            layout: {
+                initial: {
+                    isExpanded: false,
+                    showControls: false
+                },
+                controls: {
+                    // left: 'none'
+                }
+            },
+            components: {
+                remoteState: 'none'
+            }
+        });
+
+        this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
+        this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
+        this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
+    }
+
+    async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {
+        await this.plugin.clear();
+
+        const data = await this.plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
+        const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
+
+        await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'default', {
+            structure: assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } },
+            showUnitcell: false,
+            representationPreset: 'auto'
+        });
+    }
+
+    setBackground(color: number) {
+        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: props => { props.renderer.backgroundColor = Color(color) } });
+    }
+
+    toggleSpin() {
+        if (!this.plugin.canvas3d) return;
+
+        PluginCommands.Canvas3D.SetSettings(this.plugin, {
+            settings: props => {
+                props.trackball.spin = !props.trackball.spin;
+            }
+        });
+        if (!this.plugin.canvas3d.props.trackball.spin) PluginCommands.Camera.Reset(this.plugin, {});
+    }
+
+    animate = {
+        modelIndex: {
+            maxFPS: 8,
+            onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
+            onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
+            palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
+            loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
+            stop: () => this.plugin.state.animation.stop()
+        }
+    }
+
+    coloring = {
+        applyStripes: async () => {
+            this.plugin.dataTransaction(async () => {
+                for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
+                    await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: StripedResidues.propertyProvider.descriptor.name as any })
+                }
+            });
+        },
+        applyDefault: async () => {
+            this.plugin.dataTransaction(async () => {
+                for (const s of this.plugin.managers.structure.hierarchy.current.structures) {
+                    await this.plugin.managers.structure.component.updateRepresentationsTheme(s.components, { color: 'default' })
+                }
+            });
+        }
+    }
+
+    interactivity = {
+        highlightOn: () => {
+            const seq_id = 7;
+            const data = (this.plugin.state.data.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
+            const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
+                'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
+                'group-by': Q.struct.atomProperty.macromolecular.residueKey()
+            }), data);
+            const loci = StructureSelection.toLociWithSourceUnits(sel);
+            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
+        },
+        clearHighlight: () => {
+            this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
+        }
+    }
+
+    tests = {
+        staticSuperposition: async () => {
+            await this.plugin.clear();
+            return buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
+        },
+        dynamicSuperposition: async () => {
+            await this.plugin.clear();
+            return dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
+        },
+        toggleValidationTooltip: () => {
+            return this.plugin.state.updateBehavior(PDBeStructureQualityReport, params => { params.showTooltip = !params.showTooltip });
+        },
+        showToasts: () => {
+            PluginCommands.Toast.Show(this.plugin, {
+                title: 'Toast 1',
+                message: 'This is an example text, timeout 3s',
+                key: 'toast-1',
+                timeoutMs: 3000
+            });
+            PluginCommands.Toast.Show(this.plugin, {
+                title: 'Toast 2',
+                message: CustomToastMessage,
+                key: 'toast-2'
+            });
+        },
+        hideToasts: () => {
+            PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
+            PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
+        }
+    }
+}
+
+(window as any).BasicMolStarWrapper = new BasicWrapper();

+ 118 - 0
src/examples/basic-wrapper/superposition.ts

@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Mat4 } from '../../mol-math/linear-algebra';
+import { QueryContext, StructureSelection } from '../../mol-model/structure';
+import { superposeStructures } from '../../mol-model/structure/structure/util/superposition';
+import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
+import { PluginContext } from '../../mol-plugin/context';
+import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
+import Expression from '../../mol-script/language/expression';
+import { compile } from '../../mol-script/runtime/query/compiler';
+import { StateObjectRef } from '../../mol-state';
+import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
+import { StateTransforms } from '../../mol-plugin-state/transforms';
+
+export type SuperpositionTestInput = {
+    pdbId: string,
+    auth_asym_id: string,
+    matrix: Mat4
+}[];
+
+export function buildStaticSuperposition(plugin: PluginContext, src: SuperpositionTestInput) {
+    return plugin.dataTransaction(async () => {
+        for (const s of src) {
+            const { structure } = await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`, 'mmcif');
+            await transform(plugin, structure, s.matrix);
+            const chain = await plugin.builders.structure.tryCreateComponentFromExpression(structure, chainSelection(s.auth_asym_id), `Chain ${s.auth_asym_id}`);
+            if (chain) await plugin.builders.structure.representation.addRepresentation(chain, { type: 'cartoon' });
+        }
+    })
+}
+
+export const StaticSuperpositionTestData: SuperpositionTestInput = [
+    {
+        pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity()
+    },
+    {
+        pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
+            [0.406, 0.879, 0.248, -200.633],
+            [0.693, -0.473, 0.544, 73.403],
+            [0.596, -0.049, -0.802, -14.209],
+            [0, 0, 0, 1]])
+    },
+    {
+        pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
+            [-0.053, -0.077, 0.996, -45.633],
+            [-0.312, 0.949, 0.057, -12.255],
+            [-0.949, -0.307, -0.074, 53.562],
+            [0, 0, 0, 1]])
+    }
+];
+
+export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], comp_id: string) {
+    return plugin.dataTransaction(async () => {
+        for (const s of src) {
+            await loadStructure(plugin, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`, 'mmcif');
+        }
+
+        const pivot = MS.struct.filter.first([
+            MS.struct.generator.atomGroups({
+                'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
+                'group-by': MS.struct.atomProperty.macromolecular.residueKey()
+            })
+        ]);
+
+        const rest = MS.struct.modifier.exceptBy({
+            0: MS.struct.modifier.includeSurroundings({
+                0: pivot,
+                radius: 5
+            }),
+            by: pivot
+        });
+
+        const query = compile<StructureSelection>(pivot);
+        const xs = plugin.managers.structure.hierarchy.current.structures;
+        const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.cell.obj!.data))));
+
+        const transforms = superposeStructures(selections);
+
+        await siteVisual(plugin, xs[0].cell, pivot, rest);
+        for (let i = 1; i < selections.length; i++) {
+            await transform(plugin, xs[i].cell, transforms[i - 1].bTransform);
+            await siteVisual(plugin, xs[i].cell, pivot, rest);
+        }
+    });
+}
+
+async function siteVisual(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {
+    const center = await plugin.builders.structure.tryCreateComponentFromExpression(s, pivot, 'pivot');
+    if (center) await plugin.builders.structure.representation.addRepresentation(center, { type: 'ball-and-stick', color: 'residue-name' });
+
+    const surr = await plugin.builders.structure.tryCreateComponentFromExpression(s, rest, 'rest');
+    if (surr) await plugin.builders.structure.representation.addRepresentation(surr, { type: 'ball-and-stick', color: 'uniform', size: 'uniform', sizeParams: { value: 0.33 } });
+}
+
+async function loadStructure(plugin: PluginContext, url: string, format: BuiltInTrajectoryFormat, assemblyId?: string) {
+    const data = await plugin.builders.data.download({ url });
+    const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
+    const model = await plugin.builders.structure.createModel(trajectory);
+    const structure = await plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : void 0);
+
+    return { data, trajectory, model, structure };
+}
+
+function chainSelection(auth_asym_id: string) {
+    return MS.struct.generator.atomGroups({
+        'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
+    });
+}
+
+function transform(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, matrix: Mat4) {
+    const b = plugin.state.data.build().to(s)
+        .insert(StateTransforms.Model.TransformStructureConformation, { transform: { name: 'matrix', params: matrix } });
+    return plugin.runTask(plugin.state.data.updateTree(b));
+}

+ 0 - 0
src/apps/domain-annotation-server/mapping.ts → src/examples/domain-annotation-server/mapping.ts


+ 0 - 0
src/apps/domain-annotation-server/schemas.ts → src/examples/domain-annotation-server/schemas.ts


+ 1 - 1
src/apps/domain-annotation-server/server.ts → src/examples/domain-annotation-server/server.ts

@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as express from 'express'
+import express from 'express'
 import fetch from 'node-fetch'
 import createMapping from './mapping'
 

+ 0 - 0
src/apps/domain-annotation-server/test.ts → src/examples/domain-annotation-server/test.ts


+ 0 - 0
src/apps/demos/lighting/index.html → src/examples/lighting/index.html


+ 117 - 0
src/examples/lighting/index.ts

@@ -0,0 +1,117 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
+import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
+import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
+import { PluginCommands } from '../../mol-plugin/commands';
+import { PluginContext } from '../../mol-plugin/context';
+import './index.html';
+require('mol-plugin-ui/skin/light.scss')
+
+type LoadParams = { url: string, format?: BuiltInTrajectoryFormat, isBinary?: boolean, assemblyId?: string }
+
+type _Preset = Pick<Canvas3DProps, 'multiSample' | 'postprocessing' | 'renderer'>
+type Preset = { [K in keyof _Preset]: Partial<_Preset[K]> }
+
+const Canvas3DPresets = {
+    illustrative: <Preset> {
+        multiSample: {
+            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusion: { name: 'on', params: { bias: 0.8, kernelSize: 6, radius: 64 } },
+            outline: { name: 'on', params: { scale: 1, threshold: 0.8 } }
+        },
+        renderer: {
+            ambientIntensity: 1,
+            lightIntensity: 0,
+        }
+    },
+    occlusion: <Preset> {
+        multiSample: {
+            mode: 'temporal' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusion: { name: 'on', params: { bias: 0.8, kernelSize: 6, radius: 64 } },
+            outline: { name: 'off', params: { } }
+        },
+        renderer: {
+            ambientIntensity: 0.4,
+            lightIntensity: 0.6,
+        }
+    },
+    standard: <Preset> {
+        multiSample: {
+            mode: 'off' as Canvas3DProps['multiSample']['mode']
+        },
+        postprocessing: {
+            occlusion: { name: 'off', params: { } },
+            outline: { name: 'off', params: { } }
+        },
+        renderer: {
+            ambientIntensity: 0.4,
+            lightIntensity: 0.6,
+        }
+    }
+}
+
+type Canvas3DPreset = keyof typeof Canvas3DPresets
+
+class LightingDemo {
+    plugin: PluginContext;
+
+    init(target: string | HTMLElement) {
+        this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
+            ...DefaultPluginSpec,
+            layout: {
+                initial: {
+                    isExpanded: false,
+                    showControls: false
+                },
+                controls: { left: 'none', right: 'none', top: 'none', bottom: 'none' }
+            }
+        });
+
+        this.setPreset('illustrative');
+    }
+
+    setPreset(preset: Canvas3DPreset) {
+        const props = Canvas3DPresets[preset]
+        PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
+            ...props,
+            multiSample: {
+                ...this.plugin.canvas3d!.props.multiSample,
+                ...props.multiSample
+            },
+            renderer: {
+                ...this.plugin.canvas3d!.props.renderer,
+                ...props.renderer
+            },
+            postprocessing: {
+                ...this.plugin.canvas3d!.props.postprocessing,
+                ...props.postprocessing
+            },
+        }});
+    }
+
+    async load({ url, format = 'mmcif', isBinary = false, assemblyId = '' }: LoadParams) {
+        await this.plugin.clear();
+
+        const data = await this.plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
+        const trajectory = await this.plugin.builders.structure.parseTrajectory(data, format);
+        const model = await this.plugin.builders.structure.createModel(trajectory);
+        const structure = await this.plugin.builders.structure.createStructure(model, assemblyId ? { name: 'assembly', params: { id: assemblyId } } : { name: 'deposited', params: { } });
+
+        const polymer = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer');
+        if (polymer) await this.plugin.builders.structure.representation.addRepresentation(polymer, { type: 'spacefill', color: 'illustrative' });
+
+        const ligand = await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand');
+        if (ligand) await this.plugin.builders.structure.representation.addRepresentation(ligand, { type: 'ball-and-stick' });
+    }
+}
+
+(window as any).LightingDemo = new LightingDemo();

+ 37 - 11
src/mol-canvas3d/canvas3d.ts

@@ -35,6 +35,7 @@ import { ImagePass, ImageProps } from './passes/image';
 import { Sphere3D } from '../mol-math/geometry';
 import { isDebugMode } from '../mol-util/debug';
 import { CameraHelperParams } from './helper/camera-helper';
+import { produce } from 'immer';
 
 export const Canvas3DParams = {
     camera: PD.Group({
@@ -93,9 +94,8 @@ interface Canvas3D {
     requestCameraReset(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
     readonly camera: Camera
     readonly boundingSphere: Readonly<Sphere3D>
-    downloadScreenshot(): void
     getPixelData(variant: GraphicsRenderVariant): PixelData
-    setProps(props: Partial<Canvas3DProps>): void
+    setProps(props: Partial<Canvas3DProps> | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void)): void
     getImagePass(props: Partial<ImageProps>): ImagePass
 
     /** Returns a copy of the current Canvas3D instance props */
@@ -197,11 +197,6 @@ namespace Canvas3D {
         const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
         const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
 
-        const contextRestoredSub = contextRestored.subscribe(() => {
-            pickPass.pickDirty = true
-            draw(true)
-        })
-
         let drawPending = false
         let cameraResetRequested = false
         let nextCameraResetDuration: number | undefined = void 0
@@ -412,8 +407,38 @@ namespace Canvas3D {
             }
         }
 
+        function getProps(): Canvas3DProps {
+            const radius = scene.boundingSphere.radius > 0
+                ? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
+                : 0
+
+            return {
+                camera: {
+                    mode: camera.state.mode,
+                    helper: { ...drawPass.props.cameraHelper }
+                },
+                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 },
+                cameraResetDurationMs: p.cameraResetDurationMs,
+                transparentBackground: p.transparentBackground,
+
+                postprocessing: { ...postprocessing.props },
+                multiSample: { ...multiSample.props },
+                renderer: { ...renderer.props },
+                trackball: { ...controls.props },
+                debug: { ...debugHelper.props }
+            }
+        }
+
         handleResize()
 
+        const contextRestoredSub = contextRestored.subscribe(() => {
+            pickPass.pickDirty = true
+            draw(true)
+        })
+
         return {
             webgl,
 
@@ -463,9 +488,6 @@ namespace Canvas3D {
             },
             camera,
             boundingSphere: scene.boundingSphere,
-            downloadScreenshot: () => {
-                // TODO
-            },
             getPixelData: (variant: GraphicsRenderVariant) => {
                 switch (variant) {
                     case 'color': return webgl.getDrawingBufferPixelData()
@@ -477,7 +499,11 @@ namespace Canvas3D {
             },
             didDraw,
             reprCount,
-            setProps: (props: Partial<Canvas3DProps>) => {
+            setProps: (properties) => {
+                const props: Partial<Canvas3DProps> = typeof properties === 'function'
+                    ? produce(getProps(), properties)
+                    : properties;
+
                 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

+ 3 - 4
src/mol-model-props/computed/interactions/hydrogen-bonds.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author Fred Ludlow <Fred.Ludlow@astx.com>
@@ -32,7 +32,7 @@ type GeometryProps = PD.Values<GeometryParams>
 
 const HydrogenBondsParams = {
     ...GeometryParams,
-    water: PD.Boolean(true, { description: 'Include water-to-water hydrogen bonds' }),
+    water: PD.Boolean(false, { description: 'Include water-to-water hydrogen bonds' }),
     sulfurDistanceMax: PD.Numeric(4.1, { min: 1, max: 5, step: 0.1 }),
 }
 type HydrogenBondsParams = typeof HydrogenBondsParams
@@ -174,9 +174,8 @@ function addUnitHydrogenAcceptors(structure: Structure, unit: Unit.Atomic, build
     }
 }
 
-
 function isWater(unit: Unit.Atomic, index: StructureElement.UnitIndex) {
-    return unit.model.atomicHierarchy.derived.residue.moleculeType[unit.elements[index]] === MoleculeType.Water
+    return unit.model.atomicHierarchy.derived.residue.moleculeType[unit.residueIndex[unit.elements[index]]] === MoleculeType.Water
 }
 
 function isBackbone(unit: Unit.Atomic, index: StructureElement.UnitIndex) {

+ 1 - 1
src/mol-model-props/computed/interactions/metal.ts

@@ -94,7 +94,7 @@ function addMetalBinding(structure: Structure, unit: Unit.Atomic, builder: Featu
                     dative = true
                     ionic = true
                 }
-            } else if (element === Elements.S && 'CYS' === resname) {
+            } else if (element === Elements.S && (resname === 'CYS' || resname === 'MET')) {
                 dative = true
                 ionic = true
             } else if (element === Elements.N) {

+ 3 - 2
src/mol-model-props/computed/representations/interactions.ts

@@ -8,11 +8,12 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
 import { ThemeRegistryContext } from '../../../mol-theme/theme';
 import { Structure } from '../../../mol-model/structure';
-import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation, getUnitKindsParam } from '../../../mol-repr/structure/representation';
+import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
 import { InteractionsIntraUnitParams, InteractionsIntraUnitVisual } from './interactions-intra-unit-cylinder';
 import { InteractionsProvider } from '../interactions';
 import { InteractionsInterUnitParams, InteractionsInterUnitVisual } from './interactions-inter-unit-cylinder';
 import { CustomProperty } from '../../common/custom-property';
+import { getUnitKindsParam } from '../../../mol-repr/structure/params';
 
 const InteractionsVisuals = {
     'intra-unit': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, InteractionsIntraUnitParams>) => UnitsRepresentation('Intra-unit interactions cylinder', ctx, getParams, InteractionsIntraUnitVisual),
@@ -23,7 +24,7 @@ export const InteractionsParams = {
     ...InteractionsIntraUnitParams,
     ...InteractionsInterUnitParams,
     unitKinds: getUnitKindsParam(['atomic']),
-    sizeFactor: PD.Numeric(0.15, { min: 0.01, max: 1, step: 0.01 }),
+    sizeFactor: PD.Numeric(0.2, { min: 0.01, max: 1, step: 0.01 }),
     visuals: PD.MultiSelect(['intra-unit', 'inter-unit'], PD.objectToOptions(InteractionsVisuals)),
 }
 export type InteractionsParams = typeof InteractionsParams

+ 1 - 1
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -47,7 +47,7 @@ export namespace AssemblySymmetry {
         Representation = 'rcsb-assembly-symmetry-3d'
     }
 
-    export const DefaultServerUrl = 'https://data-beta.rcsb.org/graphql'
+    export const DefaultServerUrl = 'https://data.rcsb.org/graphql'
 
     export function isApplicable(structure?: Structure): boolean {
         return (

+ 3 - 1
src/mol-model-props/rcsb/graphql/types.ts

@@ -1,7 +1,7 @@
 /* eslint-disable */
 export type Maybe<T> = T | null;
 
-// Generated in 2020-03-30T11:30:30-07:00
+// Generated in 2020-04-08T16:22:40-07:00
 
 /** All built-in and custom scalars, mapped to their actual values */
 export type Scalars = {
@@ -800,6 +800,8 @@ export type PdbxAuditRevisionCategory = {
 
 export type PdbxAuditRevisionDetails = {
   readonly data_content_type: Scalars['String'];
+  readonly description?: Maybe<Scalars['String']>;
+  readonly details?: Maybe<Scalars['String']>;
   readonly ordinal: Scalars['Int'];
   readonly provider?: Maybe<Scalars['String']>;
   readonly revision_ordinal: Scalars['Int'];

+ 2 - 1
src/mol-model-props/rcsb/representations/validation-report-clashes.ts

@@ -13,7 +13,7 @@ import { PickingId } from '../../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci, DataLoci } from '../../../mol-model/loci';
 import { Interval } from '../../../mol-data/int';
 import { RepresentationContext, RepresentationParamsGetter, Representation } from '../../../mol-repr/representation';
-import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation, getUnitKindsParam } from '../../../mol-repr/structure/representation';
+import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
 import { VisualContext } from '../../../mol-repr/visual';
 import { createLinkCylinderMesh, LinkCylinderParams, LinkCylinderStyle } from '../../../mol-repr/structure/visual/util/link';
 import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../../../mol-repr/structure/units-visual';
@@ -27,6 +27,7 @@ import { MarkerActions } from '../../../mol-util/marker-action';
 import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
 import { Sphere3D } from '../../../mol-math/geometry';
 import { bondLabel } from '../../../mol-theme/label';
+import { getUnitKindsParam } from '../../../mol-repr/structure/params';
 
 //
 

+ 2 - 2
src/mol-plugin-state/builder/structure.ts

@@ -149,12 +149,12 @@ export class StructureBuilder {
         }, key, params?.tags);
     }
 
-    tryCreateComponentStatic(structure: StateObjectRef<SO.Molecule.Structure>, type: StaticStructureComponentType, key: string, params?: { label?: string, tags?: string[] }) {
+    tryCreateComponentStatic(structure: StateObjectRef<SO.Molecule.Structure>, type: StaticStructureComponentType, params?: { label?: string, tags?: string[] }) {
         return this.tryCreateComponent(structure, {
             type: { name: 'static', params: type },
             nullIfEmpty: true,
             label: (params?.label || '').trim()
-        }, key, params?.tags);
+        },  `static-${type}`, params?.tags);
     }
 
     tryCreateComponentFromSelection(structure: StateObjectRef<SO.Molecule.Structure>, selection: StructureSelectionQuery, key: string, params?: { label?: string, tags?: string[] }): Promise<StateObjectSelector<SO.Molecule.Structure> | undefined> {

+ 1 - 1
src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -245,7 +245,7 @@ const atomicDetail = StructureRepresentationPresetProvider({
 });
 
 export function presetStaticComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, type: StaticStructureComponentType, params?: { label?: string, tags?: string[] }) {
-    return plugin.builders.structure.tryCreateComponentStatic(structure, type, `static-${type}`, params);
+    return plugin.builders.structure.tryCreateComponentStatic(structure, type, params);
 }
 
 export function presetSelectionComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, query: keyof typeof Q, params?: { label?: string, tags?: string[] }) {

+ 3 - 0
src/mol-plugin-state/manager/loci-label.ts

@@ -15,6 +15,8 @@ export type LociLabel = JSX.Element | string
 export type LociLabelProvider = {
     label: (loci: Loci, repr?: Representation<any>) => LociLabel | undefined
     group?: (entry: LociLabel) => string
+    /** Labels from providers with higher priority are shown first */
+    priority?: number
 }
 
 export class LociLabelManager {
@@ -22,6 +24,7 @@ export class LociLabelManager {
 
     addProvider(provider: LociLabelProvider) {
         this.providers.push(provider);
+        this.providers.sort((a, b) => (b.priority || 0) - (a.priority || 0))
         this.isDirty = true
         this.showLabels()
     }

+ 1 - 1
src/mol-plugin-state/manager/structure/hierarchy.ts

@@ -82,7 +82,7 @@ export class StructureHierarchyManager extends PluginComponent {
     }
 
     private sync(notify: boolean) {
-        if (!notify && this.dataState.behaviors.isUpdating.value) return;
+        if (!notify && this.dataState.inUpdate) return;
 
         if (this.state.syncedTree === this.dataState.tree) {
             if (notify && !this.state.notified) {

+ 6 - 1
src/mol-plugin-ui/controls.tsx

@@ -251,9 +251,14 @@ export class SelectionViewportControls extends PluginUIComponent {
         this.subscribe(this.plugin.behaviors.interaction.selectionMode, () => this.forceUpdate());
     }
 
+    onMouseMove = (e: React.MouseEvent) => {
+        // ignore mouse moves when no button is held
+        if (e.buttons === 0) e.stopPropagation()
+    }
+
     render() {
         if (!this.plugin.selectionMode) return null;
-        return <div className='msp-selection-viewport-controls'>
+        return <div className='msp-selection-viewport-controls' onMouseMove={this.onMouseMove}>
             <StructureSelectionActionsControls />
         </div>;
     }

+ 2 - 1
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -181,7 +181,8 @@ export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
         }
 
         const assemblySymmetry = await tryCreateAssemblySymmetry(plugin, structureCell);
-        const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: Tag.Cluster as any }, plugin);
+        const globalThemeName = assemblySymmetry.isOk ? Tag.Cluster as any : undefined
+        const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName }, plugin);
 
         return { components: preset.components, representations: { ...preset.representations, assemblySymmetry } };
     }

+ 9 - 8
src/mol-plugin/behavior/dynamic/custom-props/rcsb/ui/assembly-symmetry.tsx

@@ -104,15 +104,16 @@ export class AssemblySymmetryControls extends CollapsableControls<{}, AssemblySy
             await this.plugin.builders.structure.insertStructureProperties(s.cell, params);
         }
 
-        const components = this.plugin.managers.structure.hierarchy.currentComponentGroups[0];
-        if (values.symmetryIndex === -1) {
-            const name = components[0]?.representations[0]?.cell.transform.params?.colorTheme.name;
-            if (name === AssemblySymmetry.Tag.Cluster) {
-                await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: 'default' })
+        for (const components of this.plugin.managers.structure.hierarchy.currentComponentGroups) {
+            if (values.symmetryIndex === -1) {
+                const name = components[0]?.representations[0]?.cell.transform.params?.colorTheme.name;
+                if (name === AssemblySymmetry.Tag.Cluster) {
+                    await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: 'default' })
+                }
+            } else {
+                tryCreateAssemblySymmetry(this.plugin, s.cell)
+                await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: AssemblySymmetry.Tag.Cluster as any })
             }
-        } else {
-            tryCreateAssemblySymmetry(this.plugin, s.cell)
-            await this.plugin.managers.structure.component.updateRepresentationsTheme(components, { color: AssemblySymmetry.Tag.Cluster as any })
         }
     }
 

+ 2 - 1
src/mol-plugin/behavior/dynamic/representation.ts

@@ -186,7 +186,8 @@ export const DefaultLociLabelProvider = PluginBehavior.create({
                 label.push(lociLabel(loci))
                 return label.join('</br>')
             },
-            group: (label: LociLabel) => label.toString().replace(/Model [0-9]+/g, 'Models')
+            group: (label: LociLabel) => label.toString().replace(/Model [0-9]+/g, 'Models'),
+            priority: 100
         };
         register() { this.ctx.managers.lociLabels.addProvider(this.f); }
         unregister() { this.ctx.managers.lociLabels.removeProvider(this.f); }

+ 8 - 1
src/mol-plugin/behavior/static/misc.ts

@@ -7,14 +7,21 @@
 
 import { PluginContext } from '../../../mol-plugin/context';
 import { PluginCommands } from '../../commands';
+import { DefaultCanvas3DParams } from '../../../mol-canvas3d/canvas3d';
 
 export function registerDefault(ctx: PluginContext) {
     Canvas3DSetSettings(ctx);
 }
 
 export function Canvas3DSetSettings(ctx: PluginContext) {
+    PluginCommands.Canvas3D.ResetSettings.subscribe(ctx, () => {
+        ctx.canvas3d?.setProps(DefaultCanvas3DParams);
+    });
+
     PluginCommands.Canvas3D.SetSettings.subscribe(ctx, e => {
+        if (!ctx.canvas3d) return;
+
         ctx.canvas3d?.setProps(e.settings);
         ctx.events.canvas3d.settingsUpdated.next();
-    })
+    });
 }

+ 2 - 1
src/mol-plugin/commands.ts

@@ -70,6 +70,7 @@ export const PluginCommands = {
         }
     },
     Canvas3D: {
-        SetSettings: PluginCommand<{ settings: Partial<Canvas3DProps> }>()
+        SetSettings: PluginCommand<{ settings: Partial<Canvas3DProps> | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void) }>(),
+        ResetSettings: PluginCommand<{ }>()
     }
 }

+ 7 - 2
src/mol-plugin/context.ts

@@ -8,7 +8,7 @@
 import { setAutoFreeze } from 'immer';
 import { List } from 'immutable';
 import { merge } from 'rxjs';
-import { Canvas3D } from '../mol-canvas3d/canvas3d';
+import { Canvas3D, DefaultCanvas3DParams } from '../mol-canvas3d/canvas3d';
 import { CustomProperty } from '../mol-model-props/common/custom-property';
 import { Model, Structure } from '../mol-model/structure';
 import { DataFormatRegistry } from '../mol-plugin-state/actions/data-format';
@@ -30,7 +30,7 @@ import { StateTransformParameters } from '../mol-plugin-ui/state/common';
 import { Representation } from '../mol-repr/representation';
 import { StructureRepresentationRegistry } from '../mol-repr/structure/registry';
 import { VolumeRepresentationRegistry } from '../mol-repr/volume/registry';
-import { State, StateBuilder, StateTree } from '../mol-state';
+import { State, StateBuilder, StateTree, StateTransform } from '../mol-state';
 import { Progress, Task } from '../mol-task';
 import { ColorTheme } from '../mol-theme/color';
 import { SizeTheme } from '../mol-theme/size';
@@ -234,6 +234,11 @@ export class PluginContext {
         this.tasks.requestAbort(progress, reason);
     }
 
+    clear(resetViewportSettings = false) {
+        if (resetViewportSettings) this.canvas3d?.setProps(DefaultCanvas3DParams);
+        return PluginCommands.State.RemoveObject(this, { state: this.state.data, ref: StateTransform.RootRef });
+    }
+
     dispose() {
         if (this.disposed) return;
         this.commands.dispose();

+ 2 - 1
src/mol-repr/structure/complex-representation.ts

@@ -6,7 +6,7 @@
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { ComplexVisual, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState, StructureParams } from './representation';
+import { ComplexVisual, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState } from './representation';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
 import { Structure, StructureElement, Bond } from '../../mol-model/structure';
 import { Subject } from 'rxjs';
@@ -17,6 +17,7 @@ import { PickingId } from '../../mol-geo/geometry/picking';
 import { EmptyLoci, Loci, isEveryLoci, isDataLoci } from '../../mol-model/loci';
 import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
 import { Overpaint } from '../../mol-theme/overpaint';
+import { StructureParams } from './params';
 
 export function ComplexRepresentation<P extends StructureParams>(label: string, ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, P>, visualCtor: (materialId: number) => ComplexVisual<P>): StructureRepresentation<P> {
     let version = 0

+ 1 - 1
src/mol-repr/structure/complex-visual.ts

@@ -5,7 +5,6 @@
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { StructureMeshParams, StructureDirectVolumeParams, StructureTextParams, StructureParams } from './representation';
 import { Visual, VisualContext } from '../visual';
 import { Structure, StructureElement } from '../../mol-model/structure';
 import { Geometry, GeometryUtils } from '../../mol-geo/geometry/geometry';
@@ -30,6 +29,7 @@ import { Text } from '../../mol-geo/geometry/text/text';
 import { SizeTheme } from '../../mol-theme/size';
 import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
 import { createMarkers } from '../../mol-geo/geometry/marker-data';
+import { StructureParams, StructureMeshParams, StructureTextParams, StructureDirectVolumeParams } from './params';
 
 export interface  ComplexVisual<P extends StructureParams> extends Visual<Structure, P> { }
 

+ 47 - 0
src/mol-repr/structure/params.ts

@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
+import { Lines } from '../../mol-geo/geometry/lines/lines';
+import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
+import { Points } from '../../mol-geo/geometry/points/points';
+import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
+import { Text } from '../../mol-geo/geometry/text/text';
+import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { UnitKind, UnitKindOptions } from './visual/util/common';
+
+export function getUnitKindsParam(defaultValue: UnitKind[]) {
+    return PD.MultiSelect<UnitKind>(defaultValue, UnitKindOptions, { description: 'For which kinds of units/chains to show the representation visuals.' })
+}
+
+export const StructureParams = {
+    unitKinds: getUnitKindsParam(['atomic', 'spheres']),
+}
+export type StructureParams = typeof StructureParams
+
+export const StructureMeshParams = { ...Mesh.Params }
+export type StructureMeshParams = typeof StructureMeshParams
+
+export const StructureSpheresParams = { ...Spheres.Params }
+export type StructureSpheresParams = typeof StructureSpheresParams
+
+export const StructurePointsParams = { ...Points.Params }
+export type StructurePointsParams = typeof StructurePointsParams
+
+
+export const StructureLinesParams = { ...Lines.Params }
+export type StructureLinesParams = typeof StructureLinesParams
+
+export const StructureTextParams = { ...Text.Params }
+export type StructureTextParams = typeof StructureTextParams
+
+export const StructureDirectVolumeParams = { ...DirectVolume.Params }
+export type StructureDirectVolumeParams = typeof StructureDirectVolumeParams
+
+export const StructureTextureMeshParams = { ...TextureMesh.Params }
+export type StructureTextureMeshParams = typeof StructureTextureMeshParams

+ 1 - 39
src/mol-repr/structure/representation.ts

@@ -5,27 +5,10 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
-import { Lines } from '../../mol-geo/geometry/lines/lines';
-import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
-import { Points } from '../../mol-geo/geometry/points/points';
-import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
-import { Text } from '../../mol-geo/geometry/text/text';
-import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
 import { Structure } from '../../mol-model/structure';
 import { StructureUnitTransforms } from '../../mol-model/structure/structure/util/unit-transforms';
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
 import { Representation, RepresentationProps, RepresentationProvider } from '../representation';
-import { UnitKind, UnitKindOptions } from './visual/util/common';
-
-export function getUnitKindsParam(defaultValue: UnitKind[]) {
-    return PD.MultiSelect<UnitKind>(defaultValue, UnitKindOptions, { description: 'For which kinds of units/chains to show the representation visuals.' })
-}
-
-export const StructureParams = {
-    unitKinds: getUnitKindsParam(['atomic', 'spheres']),
-}
-export type StructureParams = typeof StructureParams
 
 export interface StructureRepresentationState extends Representation.State {
     unitTransforms: StructureUnitTransforms | null,
@@ -50,29 +33,8 @@ export interface StructureRepresentation<P extends RepresentationProps = {}> ext
 export type StructureRepresentationProvider<P extends PD.Params, Id extends string = string> = RepresentationProvider<Structure, P, StructureRepresentationState, Id>
 export function StructureRepresentationProvider<P extends PD.Params, Id extends string>(p: StructureRepresentationProvider<P, Id>): StructureRepresentationProvider<P, Id> { return p; }
 //
-
-export const StructureMeshParams = { ...Mesh.Params }
-export type StructureMeshParams = typeof StructureMeshParams
-
-export const StructureSpheresParams = { ...Spheres.Params }
-export type StructureSpheresParams = typeof StructureSpheresParams
-
-export const StructurePointsParams = { ...Points.Params }
-export type StructurePointsParams = typeof StructurePointsParams
-
-export const StructureLinesParams = { ...Lines.Params }
-export type StructureLinesParams = typeof StructureLinesParams
-
-export const StructureTextParams = { ...Text.Params }
-export type StructureTextParams = typeof StructureTextParams
-
-export const StructureDirectVolumeParams = { ...DirectVolume.Params }
-export type StructureDirectVolumeParams = typeof StructureDirectVolumeParams
-
-export const StructureTextureMeshParams = { ...TextureMesh.Params }
-export type StructureTextureMeshParams = typeof StructureTextureMeshParams
-
 export { ComplexRepresentation } from './complex-representation';
 export { ComplexVisual } from './complex-visual';
 export { UnitsRepresentation } from './units-representation';
 export { UnitsVisual } from './units-visual';
+

+ 2 - 1
src/mol-repr/structure/representation/ball-and-stick.ts

@@ -10,10 +10,11 @@ import { InterUnitBondVisual, InterUnitBondParams } from '../visual/bond-inter-u
 import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { UnitsRepresentation } from '../units-representation';
 import { ComplexRepresentation } from '../complex-representation';
-import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, getUnitKindsParam } from '../representation';
+import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder } from '../representation';
 import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
 import { ThemeRegistryContext } from '../../../mol-theme/theme';
 import { Structure } from '../../../mol-model/structure';
+import { getUnitKindsParam } from '../params';
 
 const BallAndStickVisuals = {
     'element-sphere': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Element sphere mesh', ctx, getParams, getElementSphereVisual(ctx.webgl)),

+ 1 - 0
src/mol-repr/structure/representation/cartoon.ts

@@ -35,6 +35,7 @@ export const CartoonParams = {
     sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
     visuals: PD.MultiSelect(['polymer-trace', 'polymer-gap', 'nucleotide-block'], PD.objectToOptions(CartoonVisuals)),
 }
+
 export type CartoonParams = typeof CartoonParams
 export function getCartoonParams(ctx: ThemeRegistryContext, structure: Structure) {
     const params = PD.clone(CartoonParams)

+ 2 - 1
src/mol-repr/structure/representation/ellipsoid.ts

@@ -8,11 +8,12 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
 import { RepresentationParamsGetter, RepresentationContext, Representation } from '../../../mol-repr/representation';
 import { ThemeRegistryContext } from '../../../mol-theme/theme';
 import { Structure } from '../../../mol-model/structure';
-import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation, getUnitKindsParam } from '../../../mol-repr/structure/representation';
+import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
 import { EllipsoidMeshParams, EllipsoidMeshVisual } from '../visual/ellipsoid-mesh';
 import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/property/anisotropic';
 import { IntraUnitBondParams, IntraUnitBondVisual } from '../visual/bond-intra-unit-cylinder';
 import { InterUnitBondParams, InterUnitBondVisual } from '../visual/bond-inter-unit-cylinder';
+import { getUnitKindsParam } from '../params';
 
 const EllipsoidVisuals = {
     'ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidMeshParams>) => UnitsRepresentation('Ellipsoid Mesh', ctx, getParams, EllipsoidMeshVisual),

+ 2 - 1
src/mol-repr/structure/units-representation.ts

@@ -6,7 +6,7 @@
  */
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition';
-import { StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState, StructureParams } from './representation';
+import { StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationState } from './representation';
 import { Visual } from '../visual';
 import { StructureGroup } from './units-visual';
 import { RepresentationContext, RepresentationParamsGetter } from '../representation';
@@ -22,6 +22,7 @@ import { Overpaint } from '../../mol-theme/overpaint';
 import { Transparency } from '../../mol-theme/transparency';
 import { Mat4, EPSILON } from '../../mol-math/linear-algebra';
 import { Interval } from '../../mol-data/int';
+import { StructureParams } from './params';
 
 export interface UnitsVisual<P extends StructureParams> extends Visual<StructureGroup, P> { }
 

+ 1 - 1
src/mol-repr/structure/units-visual.ts

@@ -26,7 +26,6 @@ import { createColors } from '../../mol-geo/geometry/color-data';
 import { Mat4 } from '../../mol-math/linear-algebra';
 import { Overpaint } from '../../mol-theme/overpaint';
 import { Transparency } from '../../mol-theme/transparency';
-import { StructureMeshParams, StructureSpheresParams, StructurePointsParams, StructureLinesParams, StructureDirectVolumeParams, StructureTextureMeshParams, StructureTextParams, StructureParams } from './representation';
 import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
 import { SizeTheme } from '../../mol-theme/size';
 import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
@@ -36,6 +35,7 @@ import { Text } from '../../mol-geo/geometry/text/text';
 import { DirectVolume } from '../../mol-geo/geometry/direct-volume/direct-volume';
 import { TextureMesh } from '../../mol-geo/geometry/texture-mesh/texture-mesh';
 import { SizeValues } from '../../mol-gl/renderable/schema';
+import { StructureParams, StructureMeshParams, StructureSpheresParams, StructurePointsParams, StructureLinesParams, StructureTextParams, StructureDirectVolumeParams, StructureTextureMeshParams } from './params';
 
 export type StructureGroup = { structure: Structure, group: Unit.SymmetryGroup }
 

+ 173 - 0
src/mol-script/runtime/query/base.ts

@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import Expression from '../../language/expression';
+import { QueryContext, QueryFn, Structure, CustomPropertyDescriptor } from '../../../mol-model/structure';
+import { MSymbol } from '../../language/symbol';
+
+export class QueryRuntimeTable {
+    private map = new Map<string, QuerySymbolRuntime>();
+
+    addSymbol(runtime: QuerySymbolRuntime) {
+        if (this.map.has(runtime.symbol.id)) {
+            throw new Error(`Symbol '${runtime.symbol.id}' already added.`);
+        }
+        this.map.set(runtime.symbol.id, runtime);
+    }
+
+    addCustomProp(desc: CustomPropertyDescriptor<any>) {
+        if (!desc.symbols) return;
+
+        for (const k of Object.keys(desc.symbols)) {
+            this.addSymbol((desc.symbols as any)[k]);
+        }
+    }
+
+    getRuntime(id: string) {
+        return this.map.get(id);
+    }
+}
+
+export const DefaultQueryRuntimeTable = new QueryRuntimeTable();
+
+export class QueryCompilerCtx {
+    constQueryContext: QueryContext = new QueryContext(Structure.Empty);
+
+    constructor(public table: QueryRuntimeTable) {
+
+    }
+}
+
+export type ConstQuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
+export type QuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
+
+
+export type QueryCompiledSymbolRuntime = { kind: 'const', value: any } | { kind: 'dynamic', runtime: QuerySymbolFn }
+
+export type CompiledQueryFn<T = any> = { isConst: boolean, fn: QueryFn }
+
+export namespace QueryCompiledSymbol {
+    export function Const(value: any): QueryCompiledSymbolRuntime  {
+        return { kind: 'const', value }
+    }
+
+    export function Dynamic(runtime: QuerySymbolFn): QueryCompiledSymbolRuntime {
+        return { kind: 'dynamic', runtime };
+    }
+}
+
+export namespace CompiledQueryFn {
+    export function Const(value: any): CompiledQueryFn  {
+        return { isConst: true, fn: function CompiledQueryFn_Const(ctx) { return value } };
+    }
+
+    export function Dynamic(fn: QueryFn): CompiledQueryFn {
+        return { isConst: false, fn };
+    }
+}
+
+export interface QuerySymbolRuntime {
+    symbol: MSymbol,
+    compile(ctx: QueryCompilerCtx, args?: Expression.Arguments): CompiledQueryFn
+}
+
+export type QueryRuntimeArguments<S extends MSymbol> =
+    { length?: number } & { [P in keyof S['args']['@type']]: QueryFn<S['args']['@type'][P]> }
+
+export namespace QueryRuntimeArguments {
+    export function forEachEval<S extends MSymbol, Ctx>(xs: QueryRuntimeArguments<S>, queryCtx: QueryContext, f: (arg: any, i: number, ctx: Ctx) => void, ctx: Ctx): Ctx {
+        if (typeof xs.length === 'number') {
+            for (let i = 0, _i = xs.length; i < _i; i++) f((xs as any)[i](queryCtx), i, ctx);
+        } else {
+            let i = 0;
+            for (const k of Object.keys(xs)) f((xs as any)[k](queryCtx), i++, ctx);
+        }
+        return ctx;
+    }
+}
+
+export namespace QuerySymbolRuntime {
+    export function Const<S extends MSymbol<any>>(symbol: S, fn: ConstQuerySymbolFn<S>): QuerySymbolRuntime {
+        return new SymbolRuntimeImpl(symbol, fn, true);
+    }
+
+    export function Dynamic<S extends MSymbol<any>>(symbol: S, fn: QuerySymbolFn<S>): QuerySymbolRuntime {
+        return new SymbolRuntimeImpl(symbol, fn, false);
+    }
+}
+
+class SymbolRuntimeImpl<S extends MSymbol> implements QuerySymbolRuntime {
+    compile(ctx: QueryCompilerCtx, inputArgs?: Expression.Arguments): CompiledQueryFn {
+        let args: any, constArgs = false;
+        if (!inputArgs) {
+            args = void 0;
+            constArgs = true;
+        } else if (Expression.isArgumentsArray(inputArgs)) {
+            args = [];
+            constArgs = false;
+            for (const arg of inputArgs) {
+                const compiled = _compile(ctx, arg);
+                constArgs = constArgs && compiled.isConst;
+                args.push(compiled.fn);
+            }
+        } else {
+            args = Object.create(null);
+            constArgs = false;
+            for (const key of Object.keys(inputArgs)) {
+                const compiled = _compile(ctx, inputArgs[key]);
+                constArgs = constArgs && compiled.isConst;
+                args[key] = compiled.fn;
+            }
+        }
+
+        if (this.isConst) {
+            if (this.isConst && constArgs) {
+                return CompiledQueryFn.Const(this.fn(ctx.constQueryContext, args))
+            }
+
+            return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
+        }
+
+        return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
+    }
+
+    constructor(public symbol: S, private fn: QuerySymbolFn<S>, private isConst: boolean) {
+
+    }
+}
+
+function createDynamicFn<S extends MSymbol>(fn: QuerySymbolFn<S>, args: any): QueryFn {
+    return function DynamicFn(ctx) { return fn(ctx, args) };
+}
+
+function _compile(ctx: QueryCompilerCtx, expression: Expression): CompiledQueryFn {
+    if (Expression.isLiteral(expression)) {
+        return CompiledQueryFn.Const(expression);
+    }
+
+    if (Expression.isSymbol(expression)) {
+        const runtime = ctx.table.getRuntime(expression.name);
+        if (!runtime) return CompiledQueryFn.Const(expression.name);
+
+        return runtime.compile(ctx);
+    }
+
+    if (!Expression.isSymbol(expression.head)) {
+        throw new Error('Can only apply symbols.');
+    }
+
+    const compiler = ctx.table.getRuntime(expression.head.name);
+    if (!compiler) {
+        throw new Error(`Symbol '${expression.head.name}' is not implemented.`);
+    }
+
+    return compiler.compile(ctx, expression.args);
+}
+
+export function compile<T = any>(expression: Expression): QueryFn<T> {
+    const ctx = new QueryCompilerCtx(DefaultQueryRuntimeTable);
+    return _compile(ctx, expression).fn;
+}

+ 1 - 168
src/mol-script/runtime/query/compiler.ts

@@ -4,172 +4,5 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import Expression from '../../language/expression';
-import { QueryContext, QueryFn, Structure, CustomPropertyDescriptor } from '../../../mol-model/structure';
-import { MSymbol } from '../../language/symbol';
-
-export class QueryRuntimeTable {
-    private map = new Map<string, QuerySymbolRuntime>();
-
-    addSymbol(runtime: QuerySymbolRuntime) {
-        if (this.map.has(runtime.symbol.id)) {
-            throw new Error(`Symbol '${runtime.symbol.id}' already added.`);
-        }
-        this.map.set(runtime.symbol.id, runtime);
-    }
-
-    addCustomProp(desc: CustomPropertyDescriptor<any>) {
-        if (!desc.symbols) return;
-
-        for (const k of Object.keys(desc.symbols)) {
-            this.addSymbol((desc.symbols as any)[k]);
-        }
-    }
-
-    getRuntime(id: string) {
-        return this.map.get(id);
-    }
-}
-
-export const DefaultQueryRuntimeTable = new QueryRuntimeTable();
-
-export class QueryCompilerCtx {
-    constQueryContext: QueryContext = new QueryContext(Structure.Empty);
-
-    constructor(public table: QueryRuntimeTable) {
-
-    }
-}
-
-export type ConstQuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
-export type QuerySymbolFn<S extends MSymbol = MSymbol> = (ctx: QueryContext, args: QueryRuntimeArguments<S>) => any
-
-
-export type QueryCompiledSymbolRuntime = { kind: 'const', value: any } | { kind: 'dynamic', runtime: QuerySymbolFn }
-
-export type CompiledQueryFn<T = any> = { isConst: boolean, fn: QueryFn }
-
-export namespace QueryCompiledSymbol {
-    export function Const(value: any): QueryCompiledSymbolRuntime  {
-        return { kind: 'const', value }
-    }
-
-    export function Dynamic(runtime: QuerySymbolFn): QueryCompiledSymbolRuntime {
-        return { kind: 'dynamic', runtime };
-    }
-}
-
-export namespace CompiledQueryFn {
-    export function Const(value: any): CompiledQueryFn  {
-        return { isConst: true, fn: function CompiledQueryFn_Const(ctx) { return value } };
-    }
-
-    export function Dynamic(fn: QueryFn): CompiledQueryFn {
-        return { isConst: false, fn };
-    }
-}
-
-export interface QuerySymbolRuntime {
-    symbol: MSymbol,
-    compile(ctx: QueryCompilerCtx, args?: Expression.Arguments): CompiledQueryFn
-}
-
-export type QueryRuntimeArguments<S extends MSymbol> =
-    { length?: number } & { [P in keyof S['args']['@type']]: QueryFn<S['args']['@type'][P]> }
-
-export namespace QueryRuntimeArguments {
-    export function forEachEval<S extends MSymbol, Ctx>(xs: QueryRuntimeArguments<S>, queryCtx: QueryContext, f: (arg: any, i: number, ctx: Ctx) => void, ctx: Ctx): Ctx {
-        if (typeof xs.length === 'number') {
-            for (let i = 0, _i = xs.length; i < _i; i++) f((xs as any)[i](queryCtx), i, ctx);
-        } else {
-            let i = 0;
-            for (const k of Object.keys(xs)) f((xs as any)[k](queryCtx), i++, ctx);
-        }
-        return ctx;
-    }
-}
-
-export namespace QuerySymbolRuntime {
-    export function Const<S extends MSymbol<any>>(symbol: S, fn: ConstQuerySymbolFn<S>): QuerySymbolRuntime {
-        return new SymbolRuntimeImpl(symbol, fn, true);
-    }
-
-    export function Dynamic<S extends MSymbol<any>>(symbol: S, fn: QuerySymbolFn<S>): QuerySymbolRuntime {
-        return new SymbolRuntimeImpl(symbol, fn, false);
-    }
-}
-
-class SymbolRuntimeImpl<S extends MSymbol> implements QuerySymbolRuntime {
-    compile(ctx: QueryCompilerCtx, inputArgs?: Expression.Arguments): CompiledQueryFn {
-        let args: any, constArgs = false;
-        if (!inputArgs) {
-            args = void 0;
-            constArgs = true;
-        } else if (Expression.isArgumentsArray(inputArgs)) {
-            args = [];
-            constArgs = false;
-            for (const arg of inputArgs) {
-                const compiled = _compile(ctx, arg);
-                constArgs = constArgs && compiled.isConst;
-                args.push(compiled.fn);
-            }
-        } else {
-            args = Object.create(null);
-            constArgs = false;
-            for (const key of Object.keys(inputArgs)) {
-                const compiled = _compile(ctx, inputArgs[key]);
-                constArgs = constArgs && compiled.isConst;
-                args[key] = compiled.fn;
-            }
-        }
-
-        if (this.isConst) {
-            if (this.isConst && constArgs) {
-                return CompiledQueryFn.Const(this.fn(ctx.constQueryContext, args))
-            }
-
-            return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
-        }
-
-        return CompiledQueryFn.Dynamic(createDynamicFn(this.fn, args));
-    }
-
-    constructor(public symbol: S, private fn: QuerySymbolFn<S>, private isConst: boolean) {
-
-    }
-}
-
-function createDynamicFn<S extends MSymbol>(fn: QuerySymbolFn<S>, args: any): QueryFn {
-    return function DynamicFn(ctx) { return fn(ctx, args) };
-}
-
-function _compile(ctx: QueryCompilerCtx, expression: Expression): CompiledQueryFn {
-    if (Expression.isLiteral(expression)) {
-        return CompiledQueryFn.Const(expression);
-    }
-
-    if (Expression.isSymbol(expression)) {
-        const runtime = ctx.table.getRuntime(expression.name);
-        if (!runtime) return CompiledQueryFn.Const(expression.name);
-
-        return runtime.compile(ctx);
-    }
-
-    if (!Expression.isSymbol(expression.head)) {
-        throw new Error('Can only apply symbols.');
-    }
-
-    const compiler = ctx.table.getRuntime(expression.head.name);
-    if (!compiler) {
-        throw new Error(`Symbol '${expression.head.name}' is not implemented.`);
-    }
-
-    return compiler.compile(ctx, expression.args);
-}
-
-export function compile<T = any>(expression: Expression): QueryFn<T> {
-    const ctx = new QueryCompilerCtx(DefaultQueryRuntimeTable);
-    return _compile(ctx, expression).fn;
-}
-
+export * from './base'
 import './table'

+ 4 - 3
src/mol-script/runtime/query/table.ts

@@ -6,17 +6,18 @@
  */
 
 import { MolScriptSymbolTable as MolScript } from '../../language/symbol-table';
-import { DefaultQueryRuntimeTable, QuerySymbolRuntime, QueryRuntimeArguments } from './compiler';
+import { DefaultQueryRuntimeTable, QuerySymbolRuntime, QueryRuntimeArguments } from './base';
 import { Queries, StructureProperties, StructureElement, QueryContext, UnitRing } from '../../../mol-model/structure';
 import { ElementSymbol, BondType, SecondaryStructureType } from '../../../mol-model/structure/model/types';
 import { SetUtils } from '../../../mol-util/set';
 import { upperCaseAny } from '../../../mol-util/string';
 import { VdwRadius, AtomWeight, AtomNumber } from '../../../mol-model/structure/model/properties/atomic';
 import { cantorPairing } from '../../../mol-data/util';
-import C = QuerySymbolRuntime.Const
-import D = QuerySymbolRuntime.Dynamic
 import { bundleElementImpl, bundleGenerator } from '../../../mol-model/structure/query/queries/internal';
 
+const C = QuerySymbolRuntime.Const
+const D = QuerySymbolRuntime.Dynamic
+
 const symbols = [
     // ============= TYPES =============
 

+ 10 - 0
src/mol-state/state.ts

@@ -218,6 +218,13 @@ class State {
         });
     }
 
+    private _inUpdate = false;
+    /**
+     * Determines whether the state is currently "inside" updateTree function.
+     * This is different from "isUpdating" which wraps entire transactions.
+     */
+    get inUpdate() { return this._inUpdate; }
+
     /**
      * Queues up a reconciliation of the existing state tree.
      *
@@ -233,6 +240,8 @@ class State {
             const removed = await this.updateQueue.enqueue(params);
             if (!removed) return;
 
+            this._inUpdate = true;
+
             const snapshot = options?.canUndo ? this._tree.asImmutable() : void 0;
             let reverted = false;
 
@@ -248,6 +257,7 @@ class State {
 
                 return ret.cell;
             } finally {
+                this._inUpdate = false;
                 this.updateQueue.handled(params);
                 if (!this.inTransaction) {
                     this.behaviors.isUpdating.next(false);

+ 2 - 2
src/servers/model/server.ts

@@ -5,8 +5,8 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as compression from 'compression'
-import * as express from 'express'
+import compression from 'compression'
+import express from 'express'
 import { ConsoleLogger } from '../../mol-util/console-logger'
 import { PerformanceMonitor } from '../../mol-util/performance-monitor'
 import { configureServer, ModelServerConfig as ServerConfig } from './config'

+ 3 - 3
src/servers/plugin-state/index.ts

@@ -4,9 +4,9 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import * as express from 'express'
-import * as compression from 'compression'
-import * as cors from 'cors'
+import express from 'express'
+import compression from 'compression'
+import cors from 'cors'
 import * as bodyParser from 'body-parser'
 import * as fs from 'fs'
 import * as path from 'path'

+ 2 - 2
src/servers/volume/server.ts

@@ -8,8 +8,8 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import * as compression from 'compression'
-import * as express from 'express'
+import compression from 'compression'
+import express from 'express'
 import { ConsoleLogger } from '../../mol-util/console-logger'
 import { configureServer, ServerConfig } from './config'
 import { State } from './server/state'

+ 3 - 1
tsconfig.json

@@ -9,7 +9,9 @@
         "noUnusedLocals": true,
         "strictNullChecks": true,
         "strictFunctionTypes": true,
-        // "downlevelIteration": true,
+        "module": "commonjs",
+        "esModuleInterop": true,
+        "moduleResolution": "node",
         "importHelpers": true,
         "noEmitHelpers": true,
         "jsx": "react",

+ 2 - 0
webpack.config.common.js

@@ -91,12 +91,14 @@ function createNodeEntryPoint(name, dir, out) {
 }
 
 function createApp(name) { return createEntryPoint('index', `apps/${name}`, name) }
+function createExample(name) { return createEntry(`examples/${name}/index`, `examples/${name}`, 'index') }
 function createBrowserTest(name) { return createEntryPoint(name, 'tests/browser', 'tests') }
 function createNodeApp(name) { return createNodeEntryPoint('index', `apps/${name}`, name) }
 
 module.exports = {
     createApp,
     createEntry,
+    createExample,
     createBrowserTest,
     createNodeEntryPoint,
     createNodeApp

+ 12 - 17
webpack.config.js

@@ -1,20 +1,15 @@
-const common = require('./webpack.config.common.js');
-const createApp = common.createApp; 
-const createEntry = common.createEntry;
-const createBrowserTest = common.createBrowserTest;
+const { createApp, createExample, createBrowserTest } = require('./webpack.config.common.js');
 
-module.exports = [
-    createApp('viewer'),
-    createApp('basic-wrapper'),
-    createEntry('examples/proteopedia-wrapper/index', 'examples/proteopedia-wrapper', 'index'),
-    createEntry('apps/demos/lighting/index', 'demos/lighting', 'index'),
+const apps = ['viewer'];
+const examples = ['proteopedia-wrapper', 'basic-wrapper', 'lighting'];
+const tests = [
+    'font-atlas',
+    'marching-cubes',
+    'render-lines', 'render-mesh', 'render-shape', 'render-spheres', 'render-structure', 'render-text'
+];
 
-    createBrowserTest('font-atlas'),
-    createBrowserTest('marching-cubes'),
-    createBrowserTest('render-lines'),
-    createBrowserTest('render-mesh'),
-    createBrowserTest('render-shape'),
-    createBrowserTest('render-spheres'),
-    createBrowserTest('render-structure'),
-    createBrowserTest('render-text'),
+module.exports = [
+    ...apps.map(createApp),
+    ...examples.map(createExample),
+    ...tests.map(createBrowserTest)
 ]