diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000000000000000000000000000000000..1a52bf384c81e38a291f36a0ad13191c99357ccb --- /dev/null +++ b/.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 \ No newline at end of file diff --git a/README.md b/README.md index 81160af21366484a03a8b104294ed8adcaf497b7..5b5ead33d2cf3cd0239daa67e14d2cc5833bc64d 100644 --- a/README.md +++ b/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 diff --git a/data/cif-field-names/bird-field-names.csv b/data/cif-field-names/bird-field-names.csv new file mode 100644 index 0000000000000000000000000000000000000000..c214ad3694cf0ce92eb573244b5a33985c576e3d --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/cif-field-names/ccd-field-names.csv b/data/cif-field-names/ccd-field-names.csv new file mode 100644 index 0000000000000000000000000000000000000000..811f7bdc5aa5865b60c24b30f86ee95d3695c66c --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/cif-field-names/cif-core-field-names.csv b/data/cif-field-names/cif-core-field-names.csv new file mode 100644 index 0000000000000000000000000000000000000000..25ce0320c9e0e7c60a833309094d6a27814b7a4c --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/cif-field-names/mmcif-field-names.csv b/data/cif-field-names/mmcif-field-names.csv new file mode 100644 index 0000000000000000000000000000000000000000..825f2b931474b08d233bd113c7c5854f65b58d20 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/cif-field-names/mmtf-filter.csv b/data/cif-field-names/mmtf-filter.csv new file mode 100644 index 0000000000000000000000000000000000000000..5a9b2bf0f7d216debfe761f89513d0a2d7d09074 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/data/rcsb-graphql/codegen.yml b/data/rcsb-graphql/codegen.yml index 726a47193be3b15ac8085a40842d210863f722c0..d42a711fef623fe14bc2189a9f92466b65b219a9 100644 --- a/data/rcsb-graphql/codegen.yml +++ b/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': diff --git a/package-lock.json b/package-lock.json index 215f72bdf42f6a34bba0cb24a63b1b4e8117e302..d6547d6854e46f89bd92643e8e040a937b282ca6 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 0ca273aad9eae9b9afcdbd83cc4a4aea56777cd5..34f59326c9d625d3b5eb83ea8e5da411d337b021 100644 --- a/package.json +++ b/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", diff --git a/src/apps/basic-wrapper/controls.tsx b/src/apps/basic-wrapper/controls.tsx deleted file mode 100644 index c45a76010edd1bc18d52f4635fd482ae1b9ee7cd..0000000000000000000000000000000000000000 --- a/src/apps/basic-wrapper/controls.tsx +++ /dev/null @@ -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. - </>; - } -} \ No newline at end of file diff --git a/src/apps/basic-wrapper/helpers.ts b/src/apps/basic-wrapper/helpers.ts deleted file mode 100644 index b0b364e73b432527d018844dc5befd194a64e252..0000000000000000000000000000000000000000 --- a/src/apps/basic-wrapper/helpers.ts +++ /dev/null @@ -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; - } - -} \ No newline at end of file diff --git a/src/apps/basic-wrapper/index.ts b/src/apps/basic-wrapper/index.ts deleted file mode 100644 index 1d30fbf1e5db8bf5a3937dddfa02375fada5c119..0000000000000000000000000000000000000000 --- a/src/apps/basic-wrapper/index.ts +++ /dev/null @@ -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(); \ No newline at end of file diff --git a/src/apps/basic-wrapper/superposition.ts b/src/apps/basic-wrapper/superposition.ts deleted file mode 100644 index 2fd361526afeff62872aae0a5831fc36f3765419..0000000000000000000000000000000000000000 --- a/src/apps/basic-wrapper/superposition.ts +++ /dev/null @@ -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'); -} \ No newline at end of file diff --git a/src/apps/cif2bcif/converter.ts b/src/apps/cif2bcif/converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1cf3308971ad3fdf41782e923933df6d515d605 --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/src/apps/cif2bcif/index.ts b/src/apps/cif2bcif/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6987c9cf51da9e1839b1f7d6f0d6fefd42176ce --- /dev/null +++ b/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) +} \ No newline at end of file diff --git a/src/apps/cifschema/index.ts b/src/apps/cifschema/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..38602d919e917f21b660a37f6171afc65008f6e4 --- /dev/null +++ b/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) + }) + } +} diff --git a/src/apps/cifschema/util/cif-dic.ts b/src/apps/cifschema/util/cif-dic.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f43e75a00009ae4a2ab604c52c1fd401e6112db --- /dev/null +++ b/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 } +} diff --git a/src/apps/cifschema/util/generate.ts b/src/apps/cifschema/util/generate.ts new file mode 100644 index 0000000000000000000000000000000000000000..5024811cd9ab0bfc05f359388a745d886f8f02ee --- /dev/null +++ b/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)}` +} \ No newline at end of file diff --git a/src/apps/cifschema/util/helper.ts b/src/apps/cifschema/util/helper.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1bb36bb2b22b2f89dd032fe9c6872f3648b4d55 --- /dev/null +++ b/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 + } + }) +} \ No newline at end of file diff --git a/src/apps/cifschema/util/schema.ts b/src/apps/cifschema/util/schema.ts new file mode 100644 index 0000000000000000000000000000000000000000..0647c4f82d5d261c53de3846a88950db7b751a4f --- /dev/null +++ b/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 +} diff --git a/src/apps/demos/lighting/index.ts b/src/apps/demos/lighting/index.ts deleted file mode 100644 index 1a50d7f640f5dc2030defa653d21bfe2f3d54097..0000000000000000000000000000000000000000 --- a/src/apps/demos/lighting/index.ts +++ /dev/null @@ -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(); \ No newline at end of file diff --git a/src/apps/viewer/extensions/cellpack/model.ts b/src/apps/viewer/extensions/cellpack/model.ts index 0e9a3d067f999f9b12db02378d5f1224a79eb44d..0c985c078f74bf9d493b8182ac30839aca3ce79e 100644 --- a/src/apps/viewer/extensions/cellpack/model.ts +++ b/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) } -})); +})); \ No newline at end of file diff --git a/src/apps/viewer/extensions/cellpack/state.ts b/src/apps/viewer/extensions/cellpack/state.ts index 1e68b486ec540a8add7b71f84e022f5babe8cbfe..abd983c26808377cda7878eaa28a4c12a889c802 100644 --- a/src/apps/viewer/extensions/cellpack/state.ts +++ b/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 }) }); } diff --git a/src/apps/viewer/extensions/cellpack/util.ts b/src/apps/viewer/extensions/cellpack/util.ts index 6e64ed3dff740caea81d450a2d80286e800470a8..4c36a142b6e00ee79cd384e447660a599ab1da61 100644 --- a/src/apps/viewer/extensions/cellpack/util.ts +++ b/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; -} \ No newline at end of file +} + +export type IngredientFiles = { [name: string]: File } \ No newline at end of file diff --git a/src/apps/basic-wrapper/coloring.ts b/src/examples/basic-wrapper/coloring.ts similarity index 100% rename from src/apps/basic-wrapper/coloring.ts rename to src/examples/basic-wrapper/coloring.ts diff --git a/src/examples/basic-wrapper/controls.tsx b/src/examples/basic-wrapper/controls.tsx new file mode 100644 index 0000000000000000000000000000000000000000..59e1bdb7c995918e046b65c5867e5464ae842736 --- /dev/null +++ b/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. + </>; + } +} \ No newline at end of file diff --git a/src/apps/basic-wrapper/index.html b/src/examples/basic-wrapper/index.html similarity index 96% rename from src/apps/basic-wrapper/index.html rename to src/examples/basic-wrapper/index.html index 7c9a25da6738203aa084b2851e851c649b1441ba..e803d34244d39482e3fc7c96de5e6e2078312e5d 100644 --- a/src/apps/basic-wrapper/index.html +++ b/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()); diff --git a/src/examples/basic-wrapper/index.ts b/src/examples/basic-wrapper/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fed9b455644763a68fd4bb11837dafcb85cb3c9 --- /dev/null +++ b/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(); \ No newline at end of file diff --git a/src/examples/basic-wrapper/superposition.ts b/src/examples/basic-wrapper/superposition.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c45bc0391b5bae28bc76d93db28636114c3f5ba --- /dev/null +++ b/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)); +} \ No newline at end of file diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/examples/domain-annotation-server/mapping.ts similarity index 100% rename from src/apps/domain-annotation-server/mapping.ts rename to src/examples/domain-annotation-server/mapping.ts diff --git a/src/apps/domain-annotation-server/schemas.ts b/src/examples/domain-annotation-server/schemas.ts similarity index 100% rename from src/apps/domain-annotation-server/schemas.ts rename to src/examples/domain-annotation-server/schemas.ts diff --git a/src/apps/domain-annotation-server/server.ts b/src/examples/domain-annotation-server/server.ts similarity index 97% rename from src/apps/domain-annotation-server/server.ts rename to src/examples/domain-annotation-server/server.ts index 1d4f192cad0713c96b559e427535986532c8dc54..4758a4d07e01da4598117900530aff40722334e8 100644 --- a/src/apps/domain-annotation-server/server.ts +++ b/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' diff --git a/src/apps/domain-annotation-server/test.ts b/src/examples/domain-annotation-server/test.ts similarity index 100% rename from src/apps/domain-annotation-server/test.ts rename to src/examples/domain-annotation-server/test.ts diff --git a/src/apps/demos/lighting/index.html b/src/examples/lighting/index.html similarity index 100% rename from src/apps/demos/lighting/index.html rename to src/examples/lighting/index.html diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c52b3e5d855907624990ab0f96a990f73e8179a --- /dev/null +++ b/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(); \ No newline at end of file diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index 1b282a4cd4c8952024ab0b096bc8035d76b37836..5046ebb83f7392949e0a9c6368e1fd53fd0b84c6 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/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 diff --git a/src/mol-model-props/computed/interactions/hydrogen-bonds.ts b/src/mol-model-props/computed/interactions/hydrogen-bonds.ts index 1be7234bc0c5daa92ebc976cdc0d054dfa3b44dc..a47f444e04823e1dfdfd2103cbe7639f9e38a4f3 100644 --- a/src/mol-model-props/computed/interactions/hydrogen-bonds.ts +++ b/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) { diff --git a/src/mol-model-props/computed/interactions/metal.ts b/src/mol-model-props/computed/interactions/metal.ts index 93c9a41222da1f7514c7d315a72c7db64cc20a54..80d7881c716e7582d3cdb1474543748dd1f9af05 100644 --- a/src/mol-model-props/computed/interactions/metal.ts +++ b/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) { diff --git a/src/mol-model-props/computed/representations/interactions.ts b/src/mol-model-props/computed/representations/interactions.ts index 7f8e02df7e11dd35333463d26b5a51590ba55622..a77f08af3024f8f49519110e202569e09dda65ba 100644 --- a/src/mol-model-props/computed/representations/interactions.ts +++ b/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 diff --git a/src/mol-model-props/rcsb/assembly-symmetry.ts b/src/mol-model-props/rcsb/assembly-symmetry.ts index a34b12cc7ee38d9c7af7b320f5d90ac10abcd448..cfcabec150049365c2576535cf1f9d15699b1a67 100644 --- a/src/mol-model-props/rcsb/assembly-symmetry.ts +++ b/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 ( diff --git a/src/mol-model-props/rcsb/graphql/types.ts b/src/mol-model-props/rcsb/graphql/types.ts index 591e83afc715d47a0e70955350c9405f700678d2..d62903be282531a1bdf3561de7fc22f1e8ec9612 100644 --- a/src/mol-model-props/rcsb/graphql/types.ts +++ b/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']; diff --git a/src/mol-model-props/rcsb/representations/validation-report-clashes.ts b/src/mol-model-props/rcsb/representations/validation-report-clashes.ts index f9f05ff4c2977c02b93e301b5dd34c01b7f0ede3..7cc7553b9c07b28cbf014b6346a2f5093dbdd3bc 100644 --- a/src/mol-model-props/rcsb/representations/validation-report-clashes.ts +++ b/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'; // diff --git a/src/mol-plugin-state/builder/structure.ts b/src/mol-plugin-state/builder/structure.ts index 0e7301343b287f31920853e626652d9b345cb6ca..82bb1af27c6d479f883a788893e5d16eeb6f0dad 100644 --- a/src/mol-plugin-state/builder/structure.ts +++ b/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> { diff --git a/src/mol-plugin-state/builder/structure/representation-preset.ts b/src/mol-plugin-state/builder/structure/representation-preset.ts index 9e67d7dc857d22009337e6e2f78bfb3ee6dc351b..a9088281f6afe32730a01f5784ca224b3c539de7 100644 --- a/src/mol-plugin-state/builder/structure/representation-preset.ts +++ b/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[] }) { diff --git a/src/mol-plugin-state/manager/loci-label.ts b/src/mol-plugin-state/manager/loci-label.ts index ddcee6ee9f7b3e87626bd2451a6389baca83aaf4..adabbd1c74657094076593b45169b22d24777336 100644 --- a/src/mol-plugin-state/manager/loci-label.ts +++ b/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() } diff --git a/src/mol-plugin-state/manager/structure/hierarchy.ts b/src/mol-plugin-state/manager/structure/hierarchy.ts index 3c2f4d284b572e974eed63e45dbd33e439a847bd..4eef242c5336979492db34327c679b2f57fff19f 100644 --- a/src/mol-plugin-state/manager/structure/hierarchy.ts +++ b/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) { diff --git a/src/mol-plugin-ui/controls.tsx b/src/mol-plugin-ui/controls.tsx index 9f1b80a33e7f5b44e98ec4233d4b2b5f3e3dc31e..63409ca18b847924a66639e1fadcbf01d42cdff7 100644 --- a/src/mol-plugin-ui/controls.tsx +++ b/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>; } diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts index dfe688f6190a4a2c0744845b9e5f0cd4c9b2804a..3d2cad2628ee2dce12dc17ea9a840b6c5efb7c3b 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts +++ b/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 } }; } diff --git a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/ui/assembly-symmetry.tsx b/src/mol-plugin/behavior/dynamic/custom-props/rcsb/ui/assembly-symmetry.tsx index bab4c360a346efece4eb70623f3074ca0a7f1f40..df69ba8f7e6fe0d301267ae809cc644716a92775 100644 --- a/src/mol-plugin/behavior/dynamic/custom-props/rcsb/ui/assembly-symmetry.tsx +++ b/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 }) } } diff --git a/src/mol-plugin/behavior/dynamic/representation.ts b/src/mol-plugin/behavior/dynamic/representation.ts index a87f63f7396d572d9dfc0c6a360110b9e233288a..41b0ad9abefdb2b2376131fd5a21ed910753f281 100644 --- a/src/mol-plugin/behavior/dynamic/representation.ts +++ b/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); } diff --git a/src/mol-plugin/behavior/static/misc.ts b/src/mol-plugin/behavior/static/misc.ts index b9b09330d6c08416c0d0947fbacba4a6d53aef3c..32f568479d7e4743a7e39ef6ab4e000a25d8017e 100644 --- a/src/mol-plugin/behavior/static/misc.ts +++ b/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(); - }) + }); } diff --git a/src/mol-plugin/commands.ts b/src/mol-plugin/commands.ts index be2cfa91432b791b7fd159c691705686ee524ce4..be1b855480a69be6d808cf14a05209beb3d543d8 100644 --- a/src/mol-plugin/commands.ts +++ b/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<{ }>() } } \ No newline at end of file diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index db6cc468b5dcd9be824bc6819a6cb835ccbad198..1fd0735ec242773a71ea7581e7a7e29e3e4c6375 100644 --- a/src/mol-plugin/context.ts +++ b/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(); diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index c1bbe9e13f71da9856381c54433da0542194b6b9..b6cec476c8e2cc406e0d38014d665ffcc904e68f 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/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 diff --git a/src/mol-repr/structure/complex-visual.ts b/src/mol-repr/structure/complex-visual.ts index ae0ce47ee8d84116a7fbf273767f7876474c9e5a..18c5a10283ddd0064676f1d49a515f22cc50ee1f 100644 --- a/src/mol-repr/structure/complex-visual.ts +++ b/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> { } diff --git a/src/mol-repr/structure/params.ts b/src/mol-repr/structure/params.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f28b6dc5065b642e1a69602cf583cc2d8820a19 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/src/mol-repr/structure/representation.ts b/src/mol-repr/structure/representation.ts index d57f5dfd4bf848bbe56913471ff8f436b3e7ff02..de9c35430415e63e04fbff9ae18c685ba9e7c30e 100644 --- a/src/mol-repr/structure/representation.ts +++ b/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'; + diff --git a/src/mol-repr/structure/representation/ball-and-stick.ts b/src/mol-repr/structure/representation/ball-and-stick.ts index e41229dd240d40b8c9d7b3784bf6cbcd1e1e0051..9a9051d9339dcb0e7358652de583a7626d748f97 100644 --- a/src/mol-repr/structure/representation/ball-and-stick.ts +++ b/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)), diff --git a/src/mol-repr/structure/representation/cartoon.ts b/src/mol-repr/structure/representation/cartoon.ts index 2c7c14369952e5c96f03a16cf71cc0281160e2be..b271675047305b4c2a398b9c8620109fe24b54a3 100644 --- a/src/mol-repr/structure/representation/cartoon.ts +++ b/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) diff --git a/src/mol-repr/structure/representation/ellipsoid.ts b/src/mol-repr/structure/representation/ellipsoid.ts index 2ebf4c911c287d92c2853cfc7a12e0aa1f70c897..2ef2eca8a4cc2c9c4de4f98f90a0ce20d36401b0 100644 --- a/src/mol-repr/structure/representation/ellipsoid.ts +++ b/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), diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index b3ccae350d03b4b93040f9793a8a339fe9859686..ce5c31d12b68b28d9b3970c1ef567036675892f7 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/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> { } diff --git a/src/mol-repr/structure/units-visual.ts b/src/mol-repr/structure/units-visual.ts index 0ac12f20618066790e0997d6d472bc1a3b3aacc9..61ea73795335d4c1fbcb2525caa54039ddf9e211 100644 --- a/src/mol-repr/structure/units-visual.ts +++ b/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 } diff --git a/src/mol-script/runtime/query/base.ts b/src/mol-script/runtime/query/base.ts new file mode 100644 index 0000000000000000000000000000000000000000..a64c3a6af0bd2ab6bd453742eedda3eb028de0f7 --- /dev/null +++ b/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; +} \ No newline at end of file diff --git a/src/mol-script/runtime/query/compiler.ts b/src/mol-script/runtime/query/compiler.ts index e84880e703e7024f3e28ce298b656516349f9b91..2c799c3efd8dad115d33112cc78ed326f012271b 100644 --- a/src/mol-script/runtime/query/compiler.ts +++ b/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' \ No newline at end of file diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts index 2046a6bb472b1066350284515affb13749057b21..dbd42f193e2027b5ebe389c7ca7272f0d3e6a90e 100644 --- a/src/mol-script/runtime/query/table.ts +++ b/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 ============= diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index 42554a7d7dc837ca2bb72609c03e3b3b3eb8c6d1..4383be5045aed5093b9775dc80391210ccf26c56 100644 --- a/src/mol-state/state.ts +++ b/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); diff --git a/src/servers/model/server.ts b/src/servers/model/server.ts index 2a09960b3e6a87c731fd19fc8f11a8330761f49e..1844c8ae2365a25572e4771822a017094fcf7b12 100644 --- a/src/servers/model/server.ts +++ b/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' diff --git a/src/servers/plugin-state/index.ts b/src/servers/plugin-state/index.ts index f8cd83631679504d0e2b8a55626665d53099858d..afe11ac06c4d9f095599a72f4a9b050baf7f6171 100644 --- a/src/servers/plugin-state/index.ts +++ b/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' diff --git a/src/servers/volume/server.ts b/src/servers/volume/server.ts index 43983633b519a8f04da63c6f764afd3b97e60d2f..e9420b2589907073892a14407bc2570a5fe1b58d 100644 --- a/src/servers/volume/server.ts +++ b/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' diff --git a/tsconfig.json b/tsconfig.json index 81c072d12efdca006ad1da8167446e7091a1f36f..1d5ba9d043c61d30f86675d4ebe56fd187078f00 100644 --- a/tsconfig.json +++ b/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", diff --git a/webpack.config.common.js b/webpack.config.common.js index e3c610c72b04cd52fa46ff710c085bdc52579e38..3834422caa0127e29daf99b4ebb98c38dfec0871 100644 --- a/webpack.config.common.js +++ b/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 diff --git a/webpack.config.js b/webpack.config.js index 349c8dd6e3831b434d6f4bf87a3895792df9912e..5412a363045d9c12fdf61d240e1a47832c122ebf 100644 --- a/webpack.config.js +++ b/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) ] \ No newline at end of file