Compare commits

...

84 Commits

Author SHA1 Message Date
Alexander Rose
0e843c20cc 5.4.2 2025-12-07 11:00:41 -08:00
Alexander Rose
ecaf19c5fb changelog 2025-12-07 10:59:26 -08:00
Alexander Rose
f024aeef2c schema updates 2025-12-07 10:58:03 -08:00
Alexander Rose
9d9985f117 package updates 2025-12-07 10:57:55 -08:00
Alexander Rose
a0f7349ef6 reduce automatic quality on standalone HMD devices 2025-12-06 10:50:00 -08:00
Alexander Rose
01407427d2 Merge pull request #1714 from giagitom/postprocessing-improvements
postprocessing improvements
2025-12-06 10:31:39 -08:00
Alexander Rose
3dee03d9b6 cleanup & changelog 2025-12-06 10:26:59 -08:00
Alexander Rose
737f6593be Merge pull request #1712 from molstar/import-tweaks
Import tweaks
2025-12-06 10:15:41 -08:00
giagitom
068e10dd40 fix whitespaces 2025-12-05 11:16:27 +01:00
giagitom
c1ba5248b0 postprocessing improvements 2025-12-04 16:06:30 +01:00
Alexander Rose
4af0f22ac0 remove dependency between mol-util and mol-io 2025-11-23 19:19:17 -08:00
Alexander Rose
25a67e1176 remove dependency between vec4 and sphere3d 2025-11-23 19:09:06 -08:00
Alexander Rose
a8fcd501d6 remove dependency between mol-util and mol-canvas3d 2025-11-23 19:05:48 -08:00
Alexander Rose
573ee92889 remove dependency between mol-util and mol-script 2025-11-23 19:04:58 -08:00
Alexander Rose
2558d6fada remove dependency between mol-math and mol-geo 2025-11-23 19:01:11 -08:00
Alexander Rose
2cf3f8d62b move DensityTextureData type out of shared module
- not reused, depends on mol-gl
2025-11-23 19:00:12 -08:00
Alexander Rose
589d89b0d5 remove import from mol-geo in mol-gl
- too many dependencies
- not usefull, mostly for documentation
2025-11-23 18:58:31 -08:00
Alexander Rose
7cc7b77460 add missing method in gl shim 2025-11-23 18:55:40 -08:00
Alexander Rose
e8a9995bef use more direct imports
- avoid importing from re-exports
- helps to create smaller files with some bundlers
2025-11-23 18:55:11 -08:00
Alexander Rose
74ff283e00 5.4.1 2025-11-16 10:18:09 -08:00
Alexander Rose
1ecb960b82 changelog 2025-11-16 10:17:14 -08:00
Alexander Rose
387d59f97b Merge branch 'master' of https://github.com/molstar/molstar 2025-11-16 10:13:12 -08:00
Alexander Rose
d993082f24 5.4.0 2025-11-16 10:12:28 -08:00
Alexander Rose
5eaa73d56d changelog 2025-11-16 10:11:07 -08:00
Alexander Rose
b9428fd3cd Merge pull request #1708 from molstar/volume-improvements
Volume improvements
2025-11-16 10:10:11 -08:00
Alexander Rose
97d180b79d use Number.isNaN 2025-11-16 10:07:45 -08:00
Alexander Rose
25bd915ea5 optimize wrapped volume 2025-11-16 10:04:10 -08:00
Alexander Rose
f8fdffdc44 Update src/mol-model-formats/volume/ccp4.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2025-11-16 09:45:14 -08:00
David Sehnal
d11aa6ea77 improve guessCifVariant (#1709) 2025-11-16 17:53:16 +01:00
Alexander Rose
fc3c7997ea package updates 2025-11-15 16:54:26 -08:00
Alexander Rose
b3aecf8de4 package updates 2025-11-15 16:46:15 -08:00
Alexander Rose
f3581e62ef schema updates 2025-11-15 16:41:09 -08:00
Alexander Rose
88e7fe508f package updates 2025-11-15 16:32:09 -08:00
Alexander Rose
98049ed02d Merge branch 'master' of https://github.com/molstar/molstar into volume-improvements 2025-11-15 16:25:44 -08:00
Alexander Rose
194092ed67 Merge pull request #1707 from rxht/text-input-chinese-support
Add Chinese language support to the text input box.
2025-11-15 16:23:21 -08:00
Alexander Rose
e96157c890 changelog 2025-11-15 16:19:40 -08:00
Alexander Rose
a028c1ef42 Merge branch 'master' of https://github.com/molstar/molstar into pr/rxht/1707 2025-11-15 16:16:32 -08:00
Alexander Rose
ad2b5e687d Volume improvements
- Add `Volume.periodicity`
- Wrap isosurfaces for periodic volumes
- Fix dimensions for slices
2025-11-15 16:09:47 -08:00
Alexander Rose
8ba19f0be4 Merge pull request #1701 from midlik/bounding-spheres
Bounding spheres include element radius
2025-11-15 09:29:55 -08:00
Alexander Rose
bccc68f6df Merge branch 'master' of https://github.com/molstar/molstar into pr/midlik/1701 2025-11-15 09:06:55 -08:00
Alexander Rose
026a05d03d formating 2025-11-15 09:06:46 -08:00
Alexander Rose
2b4741c8ee Use PluginCommands to set canvas3d props in camera behavior 2025-11-15 08:52:25 -08:00
Alexander Rose
7960ee06d4 Fix default trackball animated spin speed 2025-11-15 08:50:24 -08:00
Alexander Rose
f73f5af131 Fix direct-volume not drawn in illumination mode 2025-11-15 08:48:22 -08:00
rxht
3123110aa4 Add Chinese language support to the text input box. 2025-11-14 10:56:15 +08:00
midlik
154063638d MVS: Allow canvas background interpolation (#1704) 2025-11-13 12:02:10 +01:00
midlik
a720b98365 MVS transformed primitives (#1705)
* MVS: Fix primitives in root not being transformed with reference structure

* MultilayerColorTheme only preferSmoothing when a nested theme prefers

* MVSAnnotationColorTheme do not prefer smoothing

* Update file header
2025-11-11 16:47:35 +01:00
Adam Midlik
d4a2937e0b Merge branch 'master' into bounding-spheres 2025-11-11 15:39:20 +00:00
midlik
b0ca7ffbb7 Fix all-selector color not applying on substructure (#1700)
* Fix all-selector color not applying on substructure

* Fix CHANGELOG

* Size uniform computed from the first included location
2025-11-11 10:32:01 +01:00
David Sehnal
c42b738abe MVS: Fix appendSnapshots when loading MVSX (#1702) 2025-11-11 10:31:06 +01:00
Alexander Rose
ab0d0fec53 Merge pull request #1697 from midlik/label-boundary
Fix bounding sphere computation for 3D text
2025-11-09 17:49:50 -08:00
Alexander Rose
8d96131962 changelog 2025-11-09 17:49:27 -08:00
Alexander Rose
95bbcd8b24 Merge branch 'master' of https://github.com/molstar/molstar into pr/midlik/1697 2025-11-09 17:49:06 -08:00
Alexander Rose
a21f5c2c23 Add viewport button to toggle illumination mode 2025-11-09 16:45:26 -08:00
Adam Midlik
94b7b1281c Merge branch 'master' into bounding-spheres 2025-11-07 10:19:00 +00:00
Adam Midlik
16dba586df Relax camera limits to allow focusing any selection with >1 atom 2025-11-07 10:13:39 +00:00
midlik
72b761f959 Fix ugly camera clipping in snapshot transitions (#1699)
* Fix ugly camera clipping in snapshot transitions

* Update CHANGELOG.md

---------

Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2025-11-06 16:04:20 +01:00
Adam Midlik
943d81cbf9 Fix bounding sphere computation for 3D text 2025-11-06 09:28:09 +00:00
dsehnal
2ecdc0eafa 5.3.0 2025-11-05 14:41:21 +01:00
dsehnal
dccfd35c7a changelog 2025-11-05 14:39:06 +01:00
Dominik Andrew Tichy
9e81a4f7a6 fix: validation and default params for primitives_from_uri (#1690)
* fix: primitives default param values

* chore: changelog

---------

Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2025-11-05 14:37:00 +01:00
midlik
6f6cc73ce9 MVS: tweak param type definitions to make type bundle smaller (#1692)
* MVS: tweak param type definitions to make type bundle smaller

* MVS: add docstring to all parameter definitions

* MVS: tweak param type definitions to make type bundle smaller 2 (palettes)

* add types
2025-11-05 14:21:54 +01:00
David Sehnal
c248ae11bf fix wheel scrolling edge case (#1696) 2025-11-05 13:27:14 +01:00
dsehnal
742be03901 add index file to mvs extension 2025-11-05 09:54:26 +01:00
midlik
00009ef198 MVS: coarse annotations bugfix (#1695) 2025-11-04 23:16:24 +01:00
David Sehnal
1cb617524d MVS: Fix coarse structure selection (#1694) 2025-11-04 23:07:47 +01:00
David Sehnal
e2e348240b MVS: topology format support (#1691)
* MVS: topology format support

* bugfix

* mvs: additional coordinate formats

* fix
2025-11-04 22:16:49 +01:00
Alexander Rose
b54908492c Add Canvas3D.setAttribs 2025-11-03 23:52:26 -08:00
dsehnal
33172862bd mvs stories loading message 2025-11-02 14:07:52 +01:00
dsehnal
c5f2767efc 5.2.0 2025-10-31 17:26:52 +01:00
dsehnal
66f5a81a5d changelog 2025-10-31 17:25:24 +01:00
David Sehnal
9e90e11bfc MVS Improvements (#1684)
* MVS primitives clipping

* camera near distance
2025-10-31 16:43:59 +01:00
midlik
ab372a89d6 MVS: fix persisting tooltips and other fixes (#1688)
* MVS: Fix tooltips persisting across snapshots

* MVS: Fix CIF annotations with no selector columns being ignored

* Vec3.orthogonalize handle special cases (fixes trackpad lock in MVS)

* Update CHANGELOG
2025-10-31 14:35:56 +01:00
Adam Midlik
ea612c3acb Unit and loci bounding sphere includes VDW or coarse sphere radius 2025-10-31 12:50:51 +00:00
Adam Midlik
a1308645e5 Vec3.orthogonalize handle special cases (fixes trackpad lock in MVS) 2025-10-29 14:04:18 +00:00
David Sehnal
c6506d515f Fix CIF Parser edge case (#1687)
* Fix CIF Parser edge case

* header
2025-10-28 15:02:35 +01:00
Adam Midlik
794b705184 MVS: Fix CIF annotations with no selector columns being ignored 2025-10-28 11:56:40 +00:00
Adam Midlik
66264abe50 MVS: Fix tooltips persisting across snapshots 2025-10-28 11:25:29 +00:00
Alexander Rose
7d0f84ff72 Merge pull request #1679 from giagitom/fix-screenshot-helper-change-transparency
Handle transparency mode updates on ImagePass
2025-10-25 14:24:13 -07:00
Alexander Rose
31495ab02a simplify 2025-10-25 14:20:30 -07:00
Alexander Rose
853ad5c916 pass transparency via scene 2025-10-25 14:15:40 -07:00
Alexander Rose
51fc525215 Merge https://github.com/molstar/molstar into pr/giagitom/1679 2025-10-25 13:53:34 -07:00
giagitom
6f9fed180d update headers 2025-10-21 21:56:37 +02:00
giagitom
5ecd176f20 Handle transparency mode updates on ImagePass 2025-10-21 21:50:42 +02:00
162 changed files with 4170 additions and 4298 deletions

View File

@@ -5,7 +5,53 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v5.1.0] - 2025-10-25
## [v5.4.2] - 2025-12-07
- Fix postprocessing issues with SSAO and outlines for large structures (#1387)
- Reduce automatic quality on standalone HMD devices
## [v5.4.1] - 2025-11-16
- Fix ugly camera clipping in snapshot transitions
- Add viewport button to toggle illumination mode
- Fix bounding sphere computation for 3D text
- Structure bounding sphere includes atom VDW radii / coarse sphere radii
- Relax camera limits to allow focusing any selection with >1 atom
- MolViewSpec
- Fix `appendSnapshots` when loading MVSX
- Fix all-selector color not applying on substructure
- Fix primitives in root not being transformed with reference structure
- Color themes do not prefer smoothing (improves performance in animations)
- Allow canvas background interpolation
- Fix `direct-volume` not drawn in illumination mode
- Fix default trackball animated spin speed
- Use `PluginCommands` to set canvas3d props in camera behavior
- Volume improvements
- Add `Volume.periodicity`
- Wrap isosurfaces for periodic volumes
- Fix dimensions for slices
- Add support for Input Method Editor (IME) to text params input
- Update `guessCifVariant` to detect density files not generated by the VolumeServer
## [v5.3.0] - 2025-11-05
- Update loading message in MVS Stories Viewer
- Add `Canvas3D.setAttribs`
- Fix `normalizeWheel` "spin" calculation fallback
- MolViewSpec
- Add support for "topology" formats (TOP, PRMTOP, PSF)
- Add support for additional "coordiates" formats (NCTRAJ, DCD, TRR)
- Fix coarse structure selection
- Fix missing default param values in `primitives_from_uri`
## [v5.2.0] - 2025-10-31
- Handle transparency updates on ImagePass
- Fix CIF parser edge case when the last token is escaped
- MolViewSpec
- Fix tooltips persisting across snapshots
- Fix CIF annotations with no selector columns being ignored
- Fix trackpad lock when camera up parallel to direction
- Add clipping support for primitives
- Support near camera distance
## [v5.1.2] - 2025-10-25
- Fix createColorScaleByType when offsets are available
- Get bond orders from non-standard CONECT records in PDB files
- Remove outdated `gl_FrontFacing` workaround for buggy drivers
@@ -13,7 +59,7 @@ Note that since we don't clearly distinguish between a public and private interf
- Support "magic window" style AR (via WebXR)
- Fix `PluginState.getStateTransitionFrameIndex`
- Update `GlycamSaccharideNames` and `Monosaccharides` in `carbohydrates/constants.ts`
- Support custom ref resolvers in `State`
- Support custom ref resolvers in `State`
- Add full-screen mode support to layout manager
- Add `show-toggle-fullscreen` URL param option to Viewer app
- MolViewSpec

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,264 @@
%VERSION VERSION_STAMP = V0001.000 DATE = 11/04/25 11:55:47
%FLAG TITLE
%FORMAT(20a4)
alanine-dipeptide.solvated.pdb
%FLAG POINTERS
%FORMAT(10I8)
22 7 12 9 25 11 39 19 0 0
99 3 9 11 19 7 11 20 0 0
0 0 0 0 0 0 0 1 10 0
0 1
%FLAG ATOM_NAME
%FORMAT(20a4)
H1 CH3 H2 H3 C O N H CA HA CB HB1 HB2 HB3 C O N H C H1
H2 H3
%FLAG ATOMIC_NUMBER
%FORMAT(10I8)
1 6 1 1 6 8 7 1 6 1
6 1 1 1 6 8 7 1 6 1
1 1
%FLAG RESIDUE_LABEL
%FORMAT(20a4)
ACE ALA NME
%FLAG RESIDUE_POINTER
%FORMAT(10I8)
1 7 17
%FLAG RESIDUE_NUMBER
%FORMAT(20I4)
1 2 3
%FLAG RESIDUE_ICODE
%FORMAT(20a4)
%FLAG RESIDUE_CHAINID
%FORMAT(20a4)
B B B
%FLAG SOLVENT_POINTERS
%FORMAT(3I8)
0 1 0
%FLAG ATOMS_PER_MOLECULE
%FORMAT(10I8)
22
%FLAG MASS
%FORMAT(5E16.8)
3.02400000E+00 5.96200000E+00 3.02400000E+00 3.02400000E+00 1.20100000E+01
1.60000000E+01 1.19940000E+01 3.02400000E+00 9.99400000E+00 3.02400000E+00
5.96200000E+00 3.02400000E+00 3.02400000E+00 3.02400000E+00 1.20100000E+01
1.60000000E+01 1.19940000E+01 3.02400000E+00 5.96200000E+00 3.02400000E+00
3.02400000E+00 3.02400000E+00
%FLAG CHARGE
%FORMAT(5E16.8)
2.04636429E+00 -6.67300626E+00 2.04636429E+00 2.04636429E+00 1.08823576E+01
-1.03484442E+01 -7.57501011E+00 4.95464337E+00 6.14091510E-01 1.49969529E+00
-3.32556975E+00 1.09880469E+00 1.09880469E+00 1.09880469E+00 1.08841798E+01
-1.03484442E+01 -7.57501011E+00 4.95464337E+00 -2.71512270E+00 1.77849648E+00
1.77849648E+00 1.77849648E+00
%FLAG AMBER_ATOM_TYPE
%FORMAT(20a4)
a0 a1 a0 a0 a2 a3 a4 a5 a1 a6 a1 a0 a0 a0 a2 a3 a4 a5 a1 a6
a6 a6
%FLAG ATOM_TYPE_INDEX
%FORMAT(10I8)
1 2 1 1 3 4 5 6 2 7
2 1 1 1 3 4 5 6 2 7
7 7
%FLAG NONBONDED_PARM_INDEX
%FORMAT(10I8)
1 2 4 7 11 16 22 2 3 5
8 12 17 23 4 5 6 9 13 18
24 7 8 9 10 14 19 25 11 12
13 14 15 20 26 16 17 18 19 20
21 27 22 23 24 25 26 27 28
%FLAG LENNARD_JONES_ACOEF
%FORMAT(5E16.8)
7.51607703E+03 9.71708117E+04 1.04308023E+06 8.61541883E+04 9.24822269E+05
8.19971662E+05 5.44261042E+04 6.47841732E+05 5.74393458E+05 3.79876399E+05
8.96776989E+04 9.95480466E+05 8.82619071E+05 6.06829343E+05 9.44293233E+05
1.07193645E+02 2.56678134E+03 2.27577560E+03 1.02595236E+03 2.12601181E+03
1.39982777E-01 4.98586847E+03 6.78771368E+04 6.01816484E+04 3.69471530E+04
6.20665998E+04 5.94667299E+01 3.25969625E+03
%FLAG LENNARD_JONES_BCOEF
%FORMAT(5E16.8)
2.17257828E+01 1.26919150E+02 6.75612247E+02 1.12529845E+02 5.99015525E+02
5.31102864E+02 1.11805549E+02 6.26720080E+02 5.55666449E+02 5.64885984E+02
1.36131731E+02 7.36907417E+02 6.53361429E+02 6.77220874E+02 8.01323529E+02
2.59456373E+00 2.06278363E+01 1.82891803E+01 1.53505284E+01 2.09604198E+01
9.37598976E-02 1.76949863E+01 1.06076943E+02 9.40505981E+01 9.21192137E+01
1.13252062E+02 1.93248820E+00 1.43076527E+01
%FLAG NUMBER_EXCLUDED_ATOMS
%FORMAT(10I8)
6 7 4 3 7 3 10 4 10 7
6 3 2 1 7 3 5 4 3 2
1 1
%FLAG EXCLUDED_ATOMS_LIST
%FORMAT(10I8)
2 3 4 5 6 7 3 4 5 6
7 8 9 4 5 6 7 5 6 7
6 7 8 9 10 11 15 7 8 9
8 9 10 11 12 13 14 15 16 17
9 10 11 15 10 11 12 13 14 15
16 17 18 19 11 12 13 14 15 16
17 12 13 14 15 16 17 13 14 15
14 15 15 16 17 18 19 20 21 22
17 18 19 18 19 20 21 22 19 20
21 22 20 21 22 21 22 22 0
%FLAG BOND_FORCE_CONSTANT
%FORMAT(5E16.8)
3.40000000E+02 4.34000000E+02 3.17000000E+02 5.70000000E+02 4.90000000E+02
3.37000000E+02 3.10000000E+02
%FLAG BOND_EQUIL_VALUE
%FORMAT(5E16.8)
1.09000000E+00 1.01000000E+00 1.52200000E+00 1.22900000E+00 1.33500000E+00
1.44900000E+00 1.52600000E+00
%FLAG BONDS_INC_HYDROGEN
%FORMAT(10I8)
0 3 1 3 6 1 3 9 1 18
21 2 24 27 1 30 33 1 30 36
1 30 39 1 48 51 2 54 57 1
54 60 1 54 63 1
%FLAG BONDS_WITHOUT_HYDROGEN
%FORMAT(10I8)
3 12 3 12 15 4 12 18 5 18
24 6 24 42 3 24 30 7 42 48
5 42 45 4 48 54 6
%FLAG ANGLE_FORCE_CONSTANT
%FORMAT(5E16.8)
3.50000000E+01 5.00000000E+01 5.00000000E+01 5.00000000E+01 8.00000000E+01
7.00000000E+01 5.00000000E+01 8.00000000E+01 8.00000000E+01 6.30000000E+01
6.30000000E+01
%FLAG ANGLE_EQUIL_VALUE
%FORMAT(5E16.8)
1.91113553E+00 1.91113553E+00 2.09439510E+00 2.06018665E+00 2.10137642E+00
2.03505391E+00 2.12755636E+00 2.14500965E+00 1.91462619E+00 1.92160751E+00
1.93906080E+00
%FLAG ANGLES_INC_HYDROGEN
%FORMAT(10I8)
0 3 6 1 0 3 9 1 0 3
12 2 6 3 9 1 6 3 12 2
9 3 12 2 12 18 21 3 18 24
27 2 21 18 24 4 24 30 33 2
24 30 36 2 24 30 39 2 27 24
30 2 27 24 42 2 33 30 36 1
33 30 39 1 36 30 39 1 42 48
51 3 48 54 57 2 48 54 60 2
48 54 63 2 51 48 54 4 57 54
60 1 57 54 63 1 60 54 63 1
%FLAG ANGLES_WITHOUT_HYDROGEN
%FORMAT(10I8)
3 12 15 5 3 12 18 6 12 18
24 7 15 12 18 8 18 24 30 9
18 24 42 10 24 42 45 5 24 42
48 6 30 24 42 11 42 48 54 7
45 42 48 8
%FLAG DIHEDRAL_FORCE_CONSTANT
%FORMAT(5E16.8)
8.00000000E-01 8.00000000E-02 2.50000000E+00 2.50000000E+00 2.00000000E+00
1.55555556E-01 1.10000000E+00 0.00000000E+00 0.00000000E+00 8.00000000E-01
1.80000000E+00 4.20000000E-01 2.70000000E-01 5.50000000E-01 1.58000000E+00
4.50000000E-01 4.00000000E-01 2.00000000E-01 2.00000000E-01 1.05000000E+01
%FLAG DIHEDRAL_PERIODICITY
%FORMAT(5E16.8)
1.00000000E+00 3.00000000E+00 2.00000000E+00 2.00000000E+00 1.00000000E+00
3.00000000E+00 2.00000000E+00 1.00000000E+00 1.00000000E+00 3.00000000E+00
2.00000000E+00 3.00000000E+00 2.00000000E+00 3.00000000E+00 2.00000000E+00
1.00000000E+00 3.00000000E+00 2.00000000E+00 1.00000000E+00 2.00000000E+00
%FLAG DIHEDRAL_PHASE
%FORMAT(5E16.8)
0.00000000E+00 3.14159265E+00 3.14159265E+00 3.14159265E+00 0.00000000E+00
0.00000000E+00 3.14159265E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 3.14159265E+00 3.14159265E+00
3.14159265E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 3.14159265E+00
%FLAG SCEE_SCALE_FACTOR
%FORMAT(5E16.8)
1.20000000E+00 0.00000000E+00 1.20000000E+00 1.20000000E+00 0.00000000E+00
1.20000000E+00 0.00000000E+00 1.20000000E+00 1.20000000E+00 1.20000000E+00
0.00000000E+00 1.20000000E+00 0.00000000E+00 1.20000000E+00 0.00000000E+00
0.00000000E+00 1.20000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
%FLAG SCNB_SCALE_FACTOR
%FORMAT(5E16.8)
2.00000000E+00 0.00000000E+00 2.00000000E+00 2.00000000E+00 0.00000000E+00
2.00000000E+00 0.00000000E+00 2.00000000E+00 2.00000000E+00 2.00000000E+00
0.00000000E+00 2.00000000E+00 0.00000000E+00 2.00000000E+00 0.00000000E+00
0.00000000E+00 2.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
%FLAG DIHEDRALS_INC_HYDROGEN
%FORMAT(10I8)
0 3 12 15 1 0 3 -12 15 2
3 12 18 21 3 6 3 12 15 1
6 3 -12 15 2 9 3 12 15 1
9 3 -12 15 2 15 12 18 21 4
15 12 -18 21 5 18 24 30 33 6
18 24 30 36 6 18 24 30 39 6
24 42 48 51 3 27 24 30 33 6
27 24 30 36 6 27 24 30 39 6
27 24 42 45 1 27 24 -42 45 2
42 24 30 33 6 42 24 30 36 6
42 24 30 39 6 45 42 48 51 4
45 42 -48 51 5 21 18 -24 -12 7
51 48 -54 -42 7 51 48 54 60 8
21 18 24 30 8 42 48 54 57 8
6 3 12 18 9 42 48 54 63 8
51 48 54 57 8 21 18 24 42 8
0 3 12 18 9 42 48 54 60 8
27 24 42 48 8 21 18 24 27 8
51 48 54 63 8 9 3 12 18 9
12 18 24 27 8
%FLAG DIHEDRALS_WITHOUT_HYDROGEN
%FORMAT(10I8)
3 12 18 24 3 12 18 24 30 10
12 18 -24 30 11 12 18 -24 30 5
12 18 24 42 12 12 18 -24 42 13
15 12 18 24 3 18 24 42 48 14
18 24 -42 48 15 18 24 -42 48 16
24 42 48 54 3 30 24 42 48 17
30 24 -42 48 18 30 24 -42 48 19
45 42 48 54 3 15 12 -18 -3 20
45 42 -48 -24 20 18 24 42 45 8
30 24 42 45 8
%FLAG SOLTY
%FORMAT(5E16.8)
%FLAG HBOND_ACOEF
%FORMAT(5E16.8)
%FLAG HBOND_BCOEF
%FORMAT(5E16.8)
%FLAG HBCUT
%FORMAT(5E16.8)
%FLAG TREE_CHAIN_CLASSIFICATION
%FORMAT(20a4)
BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA BLA
BLA BLA
%FLAG JOIN_ARRAY
%FORMAT(10I8)
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0
%FLAG IROTAT
%FORMAT(10I8)
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0
%FLAG BOX_DIMENSIONS
%FORMAT(5E16.8)
9.00000000E+01 3.00000000E+01 3.00000000E+01 3.00000000E+01
%FLAG RADIUS_SET
%FORMAT(1a80)
0
%FLAG RADII
%FORMAT(5E16.8)
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00
%FLAG SCREEN
%FORMAT(5E16.8)
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00
%FLAG IPOL
%FORMAT(1I8)
0

View File

@@ -0,0 +1,26 @@
CRYST1 30.000 30.000 30.000 90.00 90.00 90.00 P 1 1
ATOM 1 H1 ACE A 1 2.000 1.000 -0.000 0.00 0.00 H
ATOM 2 CH3 ACE A 1 2.000 2.090 0.000 0.00 0.00 C
ATOM 3 H2 ACE A 1 1.486 2.454 0.890 0.00 0.00 H
ATOM 4 H3 ACE A 1 1.486 2.454 -0.890 0.00 0.00 H
ATOM 5 C ACE A 1 3.427 2.641 -0.000 0.00 0.00 C
ATOM 6 O ACE A 1 4.391 1.877 -0.000 0.00 0.00 O
ATOM 7 N ALA A 2 3.555 3.970 -0.000 0.00 0.00 N
ATOM 8 H ALA A 2 2.733 4.556 -0.000 0.00 0.00 H
ATOM 9 CA ALA A 2 4.853 4.614 -0.000 0.00 0.00 C
ATOM 10 HA ALA A 2 5.408 4.316 0.890 0.00 0.00 H
ATOM 11 CB ALA A 2 5.661 4.221 -1.232 0.00 0.00 C
ATOM 12 HB1 ALA A 2 5.123 4.521 -2.131 0.00 0.00 H
ATOM 13 HB2 ALA A 2 6.630 4.719 -1.206 0.00 0.00 H
ATOM 14 HB3 ALA A 2 5.809 3.141 -1.241 0.00 0.00 H
ATOM 15 C ALA A 2 4.713 6.129 0.000 0.00 0.00 C
ATOM 16 O ALA A 2 3.601 6.653 0.000 0.00 0.00 O
ATOM 17 N NME A 3 5.846 6.835 0.000 0.00 0.00 N
ATOM 18 H NME A 3 6.737 6.359 -0.000 0.00 0.00 H
ATOM 19 C NME A 3 5.846 8.284 0.000 0.00 0.00 C
ATOM 20 H1 NME A 3 4.819 8.648 0.000 0.00 0.00 H
ATOM 21 H2 NME A 3 6.360 8.648 0.890 0.00 0.00 H
ATOM 22 H3 NME A 3 6.360 8.648 -0.890 0.00 0.00 H
TER 23 NME A 3
CONECT 5 7
CONECT 15 17

View File

@@ -0,0 +1,14 @@
alanine-dipeptide.solvated.pdb
22
0.7494821 1.2436848 0.8743532 1.0856344 2.2423820 0.5955986
0.4304414 2.9747953 1.0671825 1.0497815 2.3544810 -0.4880289
2.5015950 2.4471725 1.0820421 3.1003812 1.5343071 1.6479120
3.0220696 3.6519467 0.8741013 2.4411554 4.3533213 0.4373955
4.3920715 4.0500473 1.2160543 4.7674596 3.4172266 2.0202454
5.2805058 3.8202998 -0.0180103 4.9565949 4.4537317 -0.8438106
6.3180425 4.0583459 0.2164072 5.2327259 2.7740601 -0.3200050
4.4431625 5.5106563 1.7135265 3.4307644 6.2198007 1.6891606
5.6170320 5.9613562 2.1744082 6.3997462 5.3231585 2.1616313
5.8784762 7.3296314 2.6320299 5.1056278 8.0184146 2.2908769
5.9253575 7.3544224 3.7207393 6.8360338 7.6745804 2.2419090
30.0000000 30.0000000 30.0000000 90.0000000 90.0000000 90.0000000

6393
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "5.1.2",
"version": "5.4.2",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -131,51 +131,51 @@
"@types/gl": "^6.0.5",
"@types/jest": "^30.0.0",
"@types/pngjs": "^6.0.5",
"@types/react": "^18.3.24",
"@types/react": "^18.3.26",
"@types/react-dom": "^18.3.7",
"@types/webxr": "^0.5.23",
"@typescript-eslint/eslint-plugin": "^8.44.1",
"@typescript-eslint/parser": "^8.44.1",
"@types/webxr": "^0.5.24",
"@typescript-eslint/eslint-plugin": "^8.48.1",
"@typescript-eslint/parser": "^8.48.1",
"benchmark": "^2.1.4",
"concurrently": "^9.2.1",
"cpx2": "^8.0.0",
"css-loader": "^7.1.2",
"esbuild": "^0.25.10",
"esbuild": "^0.27.1",
"esbuild-jest-transform": "^2.0.1",
"esbuild-sass-plugin": "^3.3.1",
"eslint": "^9.36.0",
"eslint": "^9.39.1",
"fs-extra": "^11.3.2",
"http-server": "^14.1.1",
"jest": "^30.2.0",
"jpeg-js": "^0.4.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sass": "^1.93.2",
"simple-git": "^3.28.0",
"sass": "^1.94.2",
"simple-git": "^3.30.0",
"tsc-alias": "^1.8.16",
"typescript": "^5.9.2"
"typescript": "^5.9.3"
},
"dependencies": {
"@types/argparse": "^2.0.17",
"@types/benchmark": "^2.1.5",
"@types/compression": "1.8.1",
"@types/express": "^5.0.3",
"@types/node": "^20.19.17",
"@types/express": "^5.0.6",
"@types/node": "^20.19.25",
"@types/node-fetch": "^2.6.13",
"@types/swagger-ui-dist": "3.30.6",
"argparse": "^2.0.1",
"compression": "^1.8.1",
"cors": "^2.8.5",
"express": "^5.1.0",
"express": "^5.2.1",
"h264-mp4-encoder": "^1.0.12",
"immutable": "^5.1.3",
"immutable": "^5.1.4",
"io-ts": "^2.2.22",
"mutative": "^1.3.0",
"node-fetch": "^2.7.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"rxjs": "^7.8.2",
"swagger-ui-dist": "^5.29.0",
"swagger-ui-dist": "^5.30.3",
"tslib": "^2.8.1",
"util.promisify": "^1.1.3"
},

View File

@@ -12,7 +12,7 @@ import { useBehavior } from '../../../mol-plugin-ui/hooks/use-behavior';
import { createRoot } from 'react-dom/client';
import { PluginStateSnapshotManager } from '../../../mol-plugin-state/manager/snapshots';
import { PluginReactContext } from '../../../mol-plugin-ui/base';
import { CSSProperties } from 'react';
import { CSSProperties, useEffect, useState } from 'react';
import { Markdown } from '../../../mol-plugin-ui/controls/markdown';
export class MVSStoriesSnapshotMarkdownModel extends PluginComponent {
@@ -70,6 +70,28 @@ export class MVSStoriesSnapshotMarkdownModel extends PluginComponent {
}
}
function Loading() {
return <div>
<div style={{ marginBottom: 16 }}><i>Loading times may vary depending on the story size, your internet connection, and device performance</i></div>
<div>Fetching data<Dots /></div>
<div>Generating animations<Dots /></div>
<div>Preparing visuals<Dots /></div>
</div>;
}
function Dots() {
const [dots, setDots] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setDots(d => (d + 1) % 4);
}, Math.random() * 500 + 300);
return () => clearInterval(interval);
}, []);
return <span>{'.'.repeat(dots)}</span>;
}
export function MVSStoriesSnapshotMarkdownUI({ model }: { model: MVSStoriesSnapshotMarkdownModel }) {
const state = useBehavior(model.state);
const isLoading = useBehavior(model.context.state.isLoading);
@@ -79,7 +101,8 @@ export function MVSStoriesSnapshotMarkdownUI({ model }: { model: MVSStoriesSnaps
if (isLoading) {
return <div style={style} className={className}>
<i>Loading...</i>
<h3>The story will be ready momentarily</h3>
<Loading />
</div>;
}

View File

@@ -94,8 +94,8 @@
</div>
<div id="links">
<span id="open-in-stories"><a href="#" id="open-in-stories-link" target="_blank" rel="noopener noreferrer" title="Open and edit the story in the MolViewStories app">Edit in MolViewStories</a>&nbsp;<span class="sep"></span></span>
<span id="open-in-molstar"><a href="#" id="open-in-molstar-link" target="_blank" rel="noopener noreferrer" title="Open the story in the Mol* Viewer app. Enables exporting an animation.">Open in Mol* Viewer</a>&nbsp;<span class="sep"></span></span>
<span id="open-in-stories" style="display: none;"><a href="#" id="open-in-stories-link" target="_blank" rel="noopener noreferrer" title="Open and edit the story in the MolViewStories app">Edit in MolViewStories</a>&nbsp;<span class="sep"></span></span>
<span id="open-in-molstar" style="display: none;"><a href="#" id="open-in-molstar-link" target="_blank" rel="noopener noreferrer" title="Open the story in the Mol* Viewer app. Enables exporting an animation.">Open in Mol* Viewer</a>&nbsp;<span class="sep"></span></span>
<a href="#" id="mvs-data" title="MolViewSpec State for this story. Can be opened in the Mol* app.">Download MVS</a> <span class="sep"></span> <a href="https://github.com/molstar/molstar/tree/master/src/apps/mvs-stories" id="mvs-data" target="_blank" rel="noopener noreferrer">Source Code</a>
</div>

View File

@@ -531,7 +531,7 @@ export class Viewer {
} else if (format === 'mvsx') {
const data = await this.plugin.runTask(this.plugin.fetch({ url, type: 'binary' }));
await this.plugin.runTask(Task.create('Load MVSX file', async ctx => {
const parsed = await loadMVSX(this.plugin, ctx, data);
const parsed = await loadMVSX(this.plugin, ctx, data, { doNotClearAssets: options?.appendSnapshots });
await loadMVS(this.plugin, parsed.mvsData, { sanityChecks: true, sourceUrl: parsed.sourceUrl, ...options });
}));
} else {

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env node
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2025 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 fs from 'fs';
@@ -38,7 +39,7 @@ function print(volume: Volume) {
}
async function doMesh(volume: Volume, filename: string) {
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, -1, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) })).run();
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, -1, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5), wrap: 'auto' })).run();
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
// Export the mesh in OBJ format.

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Adam Midlik <midlik@gmail.com>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -33,7 +33,7 @@ import { MVSTreeSchema } from './tree/mvs/mvs-tree';
const DefaultFocusOptions = {
minRadius: 5,
minRadius: 1,
extraRadius: 0,
};
const DefaultCanvasBackgroundColor = ColorNames.white;
@@ -62,7 +62,15 @@ export function cameraParamsToCameraSnapshot(plugin: PluginContext, params: Mols
if (plugin.canvas3d) position = fovAdjustedPosition(target, position, plugin.canvas3d.camera.state.mode, plugin.canvas3d.camera.state.fov);
const up = Vec3.create(...params.up);
Vec3.orthogonalize(up, Vec3.sub(_tmpVec, target, position), up);
const snapshot: Partial<Camera.Snapshot> = { target, position, up, radius, radiusMax: radius };
const snapshot: Partial<Camera.Snapshot> = {
target,
position,
up,
radius,
radiusMax: radius,
minNear: params.near ?? undefined,
};
return snapshot;
}

View File

@@ -117,7 +117,7 @@ export function MVSAnnotationColorTheme(ctx: ThemeDataContext, props: MVSAnnotat
return {
factory: MVSAnnotationColorTheme,
granularity: 'groupInstance',
preferSmoothing: true,
preferSmoothing: false,
color: color,
props: props,
description: 'Assigns colors based on custom MolViewSpec annotation data.',

View File

@@ -377,6 +377,7 @@ function getRowsFromCif(data: CifCategory, schema: MVSAnnotationSchema, fieldRem
const columnArray = getArrayFromCifCategory(data, srcKey, cifSchema[key]); // Avoiding `column.toArray` as it replaces . and ? fields by 0 or ''
if (columnArray) columns[key] = columnArray;
}
if (Object.keys(columns).length === 0) return new Array(data.rowCount).fill({});
return objectOfArraysToArrayOfObjects(columns);
}

View File

@@ -112,11 +112,18 @@ export const MVSXFormatProvider: DataFormatProvider<{}, StateObjectRef<Mvs>, any
* add all contained files to `plugin`'s asset manager,
* and parse the main file in the archive as MVSJ.
* Return parsed MVS data and `sourceUrl` for resolution of relative URIs. */
export async function loadMVSX(plugin: PluginContext, runtimeCtx: RuntimeContext, data: Uint8Array<ArrayBuffer>, mainFilePath: string = 'index.mvsj'): Promise<{ mvsData: MVSData, sourceUrl: string }> {
export async function loadMVSX(plugin: PluginContext, runtimeCtx: RuntimeContext, data: Uint8Array<ArrayBuffer>, mainFilePathOrOptions?: string | { mainFilePath?: string, doNotClearAssets?: boolean }): Promise<{ mvsData: MVSData, sourceUrl: string }> {
// TODO: on next major version, streamline mainFilePathOrOptions
if (typeof mainFilePathOrOptions === 'string') {
mainFilePathOrOptions = { mainFilePath: mainFilePathOrOptions };
}
const mainFilePath = mainFilePathOrOptions?.mainFilePath ?? 'index.mvsj';
const doNotClearAssets = mainFilePathOrOptions?.doNotClearAssets ?? false;
// Ensure at most one generation of MVSX file assets exists in the asset manager.
// Hopefully, this is a reasonable compromise to ensure MVSX files work in multi-snapshot
// states.
clearMVSXFileAssets(plugin);
if (!doNotClearAssets) clearMVSXFileAssets(plugin);
const archiveId = `ni,MurmurHash3_128;${murmurHash3_128_fromBytes(data, 42)}`;
let files: { [path: string]: Uint8Array<ArrayBuffer> };
@@ -160,7 +167,7 @@ export async function loadMVSData(plugin: PluginContext, data: MVSData | StringL
throw new Error("loadMvsData: if `format` is 'mvsx', then `data` must be a Uint8Array or a base64-encoded string prefixed with 'base64,'.");
}
await plugin.runTask(Task.create('Load MVSX file', async ctx => {
const parsed = await loadMVSX(plugin, ctx, data as Uint8Array<ArrayBuffer>);
const parsed = await loadMVSX(plugin, ctx, data as Uint8Array<ArrayBuffer>, { doNotClearAssets: options?.appendSnapshots });
await loadMVS(plugin, parsed.mvsData, { sanityChecks: true, ...options, sourceUrl: parsed.sourceUrl });
}));
} else {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Adam Midlik <midlik@gmail.com>
*/
@@ -72,7 +72,7 @@ export const DefaultMultilayerColorThemeProps: MultilayerColorThemeProps = { lay
* If a nested theme provider has `ensureCustomProperties` methods, these will not be called automatically
* (the caller must ensure that any required custom properties be attached). */
function makeMultilayerColorTheme(ctx: ThemeDataContext, props: MultilayerColorThemeProps, colorThemeRegistry: ColorTheme.Registry): ColorTheme<MultilayerColorThemeParams> {
const { colorLayers, granularity } = makeLayers(ctx, props, colorThemeRegistry);
const { colorLayers, granularity, preferSmoothing } = makeLayers(ctx, props, colorThemeRegistry);
function structureElementColor(loc: StructureElement.Location, isSecondary: boolean): Color {
for (const layer of colorLayers) {
@@ -101,7 +101,7 @@ function makeMultilayerColorTheme(ctx: ThemeDataContext, props: MultilayerColorT
return {
factory: (ctx_, props_) => makeMultilayerColorTheme(ctx_, props_, colorThemeRegistry),
granularity,
preferSmoothing: true,
preferSmoothing,
color: color,
props: props,
description: 'Combines colors from multiple color themes.',
@@ -136,6 +136,7 @@ interface ColorLayer {
function makeLayers(ctx: ThemeDataContext, props: MultilayerColorThemeProps, colorThemeRegistry: ColorTheme.Registry) {
const colorLayers: ColorLayer[] = [];
let granularityFlags = 0;
let preferSmoothing = false;
for (let i = props.layers.length - 1; i >= 0; i--) { // iterate from the end to get top layer first, bottom layer last
const layer = props.layers[i];
const themeProvider = colorThemeRegistry.get(layer.theme.name);
@@ -175,8 +176,9 @@ function makeLayers(ctx: ThemeDataContext, props: MultilayerColorThemeProps, col
default:
console.warn(`Skipping color theme '${layer.theme.name}', cannot process granularity '${theme.granularity}'`);
}
if (theme.preferSmoothing) preferSmoothing = true;
}
return { colorLayers, granularity: granularityNameFromFlags(granularityFlags) };
return { colorLayers, granularity: granularityNameFromFlags(granularityFlags), preferSmoothing };
}

View File

@@ -33,6 +33,7 @@ import { Task } from '../../../mol-task';
import { round } from '../../../mol-util';
import { range } from '../../../mol-util/array';
import { Asset } from '../../../mol-util/assets';
import { Clip } from '../../../mol-util/clip';
import { Color } from '../../../mol-util/color';
import { MarkerActions } from '../../../mol-util/marker-action';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
@@ -40,6 +41,7 @@ import { capitalize } from '../../../mol-util/string';
import { rowsToExpression, rowToExpression } from '../helpers/selections';
import { collectMVSReferences, decodeColor, isDefined } from '../helpers/utils';
import { addParamDefaults } from '../tree/generic/params-schema';
import { treeValidationIssues } from '../tree/generic/tree-validation';
import { MolstarNode, MolstarNodeParams, MolstarSubtree } from '../tree/molstar/molstar-tree';
import { MVSNode, MVSTreeSchema } from '../tree/mvs/mvs-tree';
import { isComponentExpression, isPrimitiveComponentExpressions, isVector3, PrimitivePositionT } from '../tree/mvs/param-types';
@@ -81,15 +83,35 @@ export const MVSDownloadPrimitiveData = MVSTransform({
const url = Asset.getUrlAsset(plugin.managers.asset, params.uri);
const asset = await plugin.managers.asset.resolve(url, 'string').runInContext(ctx);
const node = JSON.parse(StringLike.toString(asset.data)) as MolstarSubtree<'primitives'>;
const validationIssues = treeValidationIssues(MVSTreeSchema, node, { anyRoot: true });
if (validationIssues) {
throw new Error(`Invalid primitive data from ${params.uri}:\n${validationIssues.join('\n')}`);
}
if (node.kind !== 'primitives') {
throw new Error(`Expected primitives node from ${params.uri}, got ${node.kind}`);
}
const nodeWithDefaults: MolstarSubtree<'primitives'> = {
...node,
params: addParamDefaults(MVSTreeSchema.nodes.primitives.params, node.params || {}),
children: node.children?.map((child: any) => {
if (child.kind === 'primitive') {
return {
...child,
params: addParamDefaults(MVSTreeSchema.nodes.primitive.params, child.params || {})
};
}
return child;
})
};
(cache as any).asset = asset;
return new MVSPrimitivesData({
node,
node: nodeWithDefaults,
defaultStructure: SO.Molecule.Structure.is(a) ? a.data : undefined,
structureRefs: {},
primitives: getPrimitives(node),
options: { ...node.params },
primitives: getPrimitives(nodeWithDefaults),
options: { ...nodeWithDefaults.params },
positionCache: new Map(),
instances: getInstances(node.params),
instances: getInstances(nodeWithDefaults.params),
}, { label: 'Primitive Data' });
});
},
@@ -141,7 +163,8 @@ export const MVSBuildPrimitiveShape = MVSTransform({
from: MVSPrimitivesData,
to: SO.Shape.Provider,
params: {
kind: PD.Text<'mesh' | 'labels' | 'lines'>('mesh')
kind: PD.Text<'mesh' | 'labels' | 'lines'>('mesh'),
clip: PD.Value<Clip.Props | undefined>(undefined, { isHidden: true })
}
})({
apply({ a, params, dependencies }) {
@@ -160,7 +183,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
label,
data: context,
params: {
...PD.withDefaults(Mesh.Params, { alpha: a.data.options?.opacity ?? 1, ...customMeshParams }),
...PD.withDefaults(Mesh.Params, { alpha: a.data.options?.opacity ?? 1, clip: params.clip, ...customMeshParams }),
...snapshotKey,
...markdownCommands,
},
@@ -184,6 +207,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
tetherLength: options?.label_tether_length ?? 1,
background: isDefined(bgColor),
backgroundColor: isDefined(bgColor) ? decodeColor(bgColor) : undefined,
clip: params.clip,
...customLabelParams,
}),
...snapshotKey,
@@ -200,7 +224,7 @@ export const MVSBuildPrimitiveShape = MVSTransform({
label,
data: context,
params: {
...PD.withDefaults(Lines.Params, { alpha: a.data.options?.opacity ?? 1, ...customLineParams }),
...PD.withDefaults(Lines.Params, { alpha: a.data.options?.opacity ?? 1, clip: params.clip, ...customLineParams }),
...snapshotKey,
...markdownCommands,
},

View File

@@ -14,7 +14,7 @@ import { MVSTransform } from './annotation-structure-component';
export const MVSTrajectoryWithCoordinates = MVSTransform({
name: 'trajectory-with-coordinates',
display: { name: 'Trajectory with Coordinates', description: 'Create a trajectory from existing model and the provided coordinates.' },
from: PluginStateObject.Molecule.Model,
from: [PluginStateObject.Molecule.Model, PluginStateObject.Molecule.Topology],
to: PluginStateObject.Molecule.Trajectory,
params: {
coordinatesRef: ParamDefinition.Text('', { isHidden: true }),

View File

@@ -208,6 +208,7 @@ function getQualifyingResidues(model: Model, row: MVSAnnotationRow, indices: Ato
}
arrayExtend(result, residuesHere);
}
sortIfNeeded(result, (a, b) => a - b);
return result;
}
@@ -419,7 +420,7 @@ function getQualifyingCoarseElements(coarseElements: CoarseElements, row: MVSAnn
// This implementation can yield some elements even when queryStart>queryEnd (e.g. { beg_label_seq_id: 70, end_label_seq_id: 58, label_seq_id: 60 } -> sphere 51-100 qualifies ).
// This is on purpose, to have the same behavior as MolScript.
}
sortIfNeeded(result, iElem => iElem);
sortIfNeeded(result, (a, b) => a - b);
return result;
}

View File

@@ -8,7 +8,6 @@
import { hashString } from '../../../mol-data/util';
import { StateObject } from '../../../mol-state';
import { Color } from '../../../mol-util/color';
import { ColorNames } from '../../../mol-util/color/names';
import { decodeColor as _decodeColor } from '../../../mol-util/color/utils';
@@ -107,29 +106,6 @@ export function decodeColor(colorString: string | number | undefined | null): Co
return _decodeColor(colorString);
}
/** Regular expression matching a hexadecimal color string, e.g. '#FF1100' or '#f10' */
const hexColorRegex = /^#([0-9A-F]{3}){1,2}$/i;
/** Hexadecimal color string, e.g. '#FF1100' (the type matches more than just valid HexColor strings) */
export type HexColor = `#${string}`
export const HexColor = {
/** Decide if a string is a valid hexadecimal color string (6-digit or 3-digit, e.g. '#FF1100' or '#f10') */
is(str: any): str is HexColor {
return typeof str === 'string' && hexColorRegex.test(str);
},
};
/** Named color string, e.g. 'red' */
export type ColorName = keyof ColorNames
export const ColorName = {
/** Decide if a string is a valid named color string */
is(str: any): str is ColorName {
return str in ColorNames;
},
};
export function collectMVSReferences<T extends StateObject.Ctor>(type: T[], dependencies: Record<string, StateObject>): Record<string, StateObject.From<T>['data']> {
const ret: any = {};

View File

@@ -0,0 +1,8 @@
/**
* Copyright (c) 2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
export * from './mvs-data';
export * from './load';

View File

@@ -479,7 +479,7 @@ function getClipObject(node: MolstarNode<'clip'>): Clip.Props['objects'][number]
}
}
export function clippingForNode(node: MolstarSubtree<'representation' | 'volume_representation'>): Clip.Props | undefined {
export function clippingForNode(node: MolstarSubtree<'representation' | 'volume_representation' | 'primitives' | 'primitives_from_uri'>): Clip.Props | undefined {
const children = getChildren(node).filter(c => c.kind === 'clip');
if (!children.length) return;

View File

@@ -8,8 +8,8 @@
import { PluginStateSnapshotManager } from '../../mol-plugin-state/manager/snapshots';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { Download, ParseCif, ParseCcp4, ParseDx } from '../../mol-plugin-state/transforms/data';
import { CoordinatesFromLammpstraj, CoordinatesFromXtc, CustomModelProperties, CustomStructureProperties, ModelFromTrajectory, StructureComponent, StructureFromModel, TrajectoryFromGRO, TrajectoryFromLammpsTrajData, TrajectoryFromMmCif, TrajectoryFromMOL, TrajectoryFromMOL2, TrajectoryFromPDB, TrajectoryFromSDF, TrajectoryFromXYZ } from '../../mol-plugin-state/transforms/model';
import { Download, ParseCcp4, ParseCif, ParseDx, ParsePrmtop, ParsePsf, ParseTop } from '../../mol-plugin-state/transforms/data';
import { CoordinatesFromDcd, CoordinatesFromLammpstraj, CoordinatesFromNctraj, CoordinatesFromTrr, CoordinatesFromXtc, CustomModelProperties, CustomStructureProperties, ModelFromTrajectory, StructureComponent, StructureFromModel, TopologyFromPrmtop, TopologyFromPsf, TopologyFromTop, TrajectoryFromGRO, TrajectoryFromLammpsTrajData, TrajectoryFromMmCif, TrajectoryFromMOL, TrajectoryFromMOL2, TrajectoryFromPDB, TrajectoryFromSDF, TrajectoryFromXYZ } from '../../mol-plugin-state/transforms/model';
import { StructureRepresentation3D, VolumeRepresentation3D } from '../../mol-plugin-state/transforms/representation';
import { VolumeFromCcp4, VolumeFromDensityServerCif, VolumeFromDx } from '../../mol-plugin-state/transforms/volume';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -17,6 +17,7 @@ import { PluginContext } from '../../mol-plugin/context';
import { PluginState } from '../../mol-plugin/state';
import { StateObjectSelector, StateTree } from '../../mol-state';
import { RuntimeContext, Task } from '../../mol-task';
import { Clip } from '../../mol-util/clip';
import { MolViewSpec } from './behavior';
import { createPluginStateSnapshotCamera, modifyCanvasProps, resetCanvasProps } from './camera';
import { MVSAnnotationsProvider } from './components/annotation-prop';
@@ -31,7 +32,7 @@ import { generateStateTransition } from './helpers/animation';
import { IsHiddenCustomStateExtension } from './load-extensions/is-hidden-custom-state';
import { NonCovalentInteractionsExtension } from './load-extensions/non-covalent-interactions';
import { LoadingActions, LoadingExtension, loadTreeVirtual, UpdateTarget } from './load-generic';
import { AnnotationFromSourceKind, AnnotationFromUriKind, collectAnnotationReferences, collectAnnotationTooltips, collectInlineLabels, collectInlineTooltips, colorThemeForNode, componentFromXProps, componentPropsFromSelector, isPhantomComponent, labelFromXProps, makeNearestReprMap, prettyNameFromSelector, representationProps, structureProps, transformAndInstantiateStructure, transformAndInstantiateVolume, volumeColorThemeForNode, volumeRepresentationProps } from './load-helpers';
import { AnnotationFromSourceKind, AnnotationFromUriKind, clippingForNode, collectAnnotationReferences, collectAnnotationTooltips, collectInlineLabels, collectInlineTooltips, colorThemeForNode, componentFromXProps, componentPropsFromSelector, isPhantomComponent, labelFromXProps, makeNearestReprMap, prettyNameFromSelector, representationProps, structureProps, transformAndInstantiateStructure, transformAndInstantiateVolume, volumeColorThemeForNode, volumeRepresentationProps } from './load-helpers';
import { MVSData, MVSData_States, Snapshot, SnapshotMetadata } from './mvs-data';
import { MVSAnimationNode, MVSAnimationSchema } from './tree/animation/animation-tree';
import { validateTree } from './tree/generic/tree-validation';
@@ -245,7 +246,16 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
case 'mol2':
case 'xtc':
case 'lammpstrj':
case 'dcd':
case 'nctraj':
case 'trr':
return updateParent;
case 'psf':
return UpdateTarget.apply(updateParent, ParsePsf, {});
case 'prmtop':
return UpdateTarget.apply(updateParent, ParsePrmtop, {});
case 'top':
return UpdateTarget.apply(updateParent, ParseTop, {});
case 'map':
return UpdateTarget.apply(updateParent, ParseCcp4, {});
case 'dx':
@@ -259,6 +269,12 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
coordinates(updateParent: UpdateTarget, node: MolstarNode<'coordinates'>): UpdateTarget | undefined {
const format = node.params.format;
switch (format) {
case 'nctraj':
return UpdateTarget.apply(updateParent, CoordinatesFromNctraj);
case 'dcd':
return UpdateTarget.apply(updateParent, CoordinatesFromDcd);
case 'trr':
return UpdateTarget.apply(updateParent, CoordinatesFromTrr);
case 'xtc':
return UpdateTarget.apply(updateParent, CoordinatesFromXtc);
case 'lammpstrj':
@@ -302,6 +318,28 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
});
return UpdateTarget.setMvsDependencies(result, [node.params.coordinates_ref]);
},
topology_with_coordinates(updateParent: UpdateTarget, node: MolstarNode<'topology_with_coordinates'>): UpdateTarget | undefined {
let parsed: UpdateTarget;
const format = node.params.format;
switch (format) {
case 'psf':
parsed = UpdateTarget.apply(updateParent, TopologyFromPsf, {});
break;
case 'prmtop':
parsed = UpdateTarget.apply(updateParent, TopologyFromPrmtop, {});
break;
case 'top':
parsed = UpdateTarget.apply(updateParent, TopologyFromTop, {});
break;
default:
console.error(`Unknown format in "topology_with_coordinates" node: "${format}"`);
return undefined;
}
const result = UpdateTarget.apply(parsed, MVSTrajectoryWithCoordinates, {
coordinatesRef: node.params.coordinates_ref,
});
return UpdateTarget.setMvsDependencies(result, [node.params.coordinates_ref]);
},
model(updateParent: UpdateTarget, node: MolstarSubtree<'model'>, context: MolstarLoadingContext): UpdateTarget {
const annotations = collectAnnotationReferences(node, context);
const model = UpdateTarget.apply(updateParent, ModelFromTrajectory, {
@@ -325,18 +363,16 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
const transformed = transformAndInstantiateStructure(struct, node);
const annotationTooltips = collectAnnotationTooltips(node, context);
const inlineTooltips = collectInlineTooltips(node, context);
if (annotationTooltips.length + inlineTooltips.length > 0) {
UpdateTarget.apply(struct, CustomStructureProperties, {
properties: {
[MVSAnnotationTooltipsProvider.descriptor.name]: { tooltips: annotationTooltips },
[CustomTooltipsProvider.descriptor.name]: { tooltips: inlineTooltips },
},
autoAttach: [
MVSAnnotationTooltipsProvider.descriptor.name,
CustomTooltipsProvider.descriptor.name,
],
});
}
UpdateTarget.apply(struct, CustomStructureProperties, {
properties: {
[MVSAnnotationTooltipsProvider.descriptor.name]: { tooltips: annotationTooltips },
[CustomTooltipsProvider.descriptor.name]: { tooltips: inlineTooltips },
},
autoAttach: [
MVSAnnotationTooltipsProvider.descriptor.name,
CustomTooltipsProvider.descriptor.name,
],
}); // CustomStructureProperties must be applied even when `annotationTooltips` and `inlineTooltips` are empty, otherwise tooltips would persists across MVS snapshots
const inlineLabels = collectInlineLabels(node, context);
if (inlineLabels.length > 0) {
const nearestReprNode = context.nearestReprMap?.get(node);
@@ -426,21 +462,26 @@ const MolstarLoadingActions: LoadingActions<MolstarTree, MolstarLoadingContext>
},
primitives(updateParent: UpdateTarget, tree: MolstarSubtree<'primitives'>, context: MolstarLoadingContext): UpdateTarget {
const refs = getPrimitiveStructureRefs(tree);
const clip = clippingForNode(tree);
const data = UpdateTarget.apply(updateParent, MVSInlinePrimitiveData, { node: tree as any });
return applyPrimitiveVisuals(data, refs);
UpdateTarget.setMvsDependencies(data, refs); // MVSInlinePrimitiveData must depend on `refs` because it caches positions
return applyPrimitiveVisuals(data, refs, clip);
},
primitives_from_uri(updateParent: UpdateTarget, tree: MolstarNode<'primitives_from_uri'>, context: MolstarLoadingContext): UpdateTarget {
const refs = new Set(tree.params.references);
const clip = clippingForNode(tree);
const data = UpdateTarget.apply(updateParent, MVSDownloadPrimitiveData, { uri: tree.params.uri, format: tree.params.format });
return applyPrimitiveVisuals(data, new Set(tree.params.references));
UpdateTarget.setMvsDependencies(data, refs); // MVSInlinePrimitiveData must depend on `refs` because it caches positions
return applyPrimitiveVisuals(data, refs, clip);
},
};
function applyPrimitiveVisuals(data: UpdateTarget, refs: Set<string>) {
const mesh = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'mesh' }, { state: { isGhost: true } }), refs);
function applyPrimitiveVisuals(data: UpdateTarget, refs: Set<string>, clip: Clip.Props | undefined) {
const mesh = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'mesh', clip }, { state: { isGhost: true } }), refs);
UpdateTarget.apply(mesh, MVSShapeRepresentation3D);
const labels = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'labels' }, { state: { isGhost: true } }), refs);
const labels = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'labels', clip }, { state: { isGhost: true } }), refs);
UpdateTarget.apply(labels, MVSShapeRepresentation3D);
const lines = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'lines' }, { state: { isGhost: true } }), refs);
const lines = UpdateTarget.setMvsDependencies(UpdateTarget.apply(data, MVSBuildPrimitiveShape, { kind: 'lines', clip }, { state: { isGhost: true } }), refs);
UpdateTarget.apply(lines, MVSShapeRepresentation3D);
return data;
}

View File

@@ -9,7 +9,15 @@ import { SimpleParamsSchema, UnionParamsSchema } from '../generic/params-schema'
import { NodeFor, ParamsOfKind, SubtreeOfKind, TreeFor, TreeSchema } from '../generic/tree-schema';
import { ColorT, ContinuousPalette, DiscretePalette, Matrix, Vector3 } from '../mvs/param-types';
const Easing = literal(
type Easing =
| 'linear'
| 'bounce-in' | 'bounce-out' | 'bounce-in-out'
| 'circle-in' | 'circle-out' | 'circle-in-out'
| 'cubic-in' | 'cubic-out' | 'cubic-in-out'
| 'exp-in' | 'exp-out' | 'exp-in-out'
| 'quad-in' | 'quad-out' | 'quad-in-out'
| 'sin-in' | 'sin-out' | 'sin-in-out'
const Easing = literal<Easing>(
'linear',
'bounce-in', 'bounce-out', 'bounce-in-out',
'circle-in', 'circle-out', 'circle-in-out',
@@ -22,23 +30,31 @@ const Easing = literal(
export type MVSAnimationEasing = ValueFor<typeof Easing>;
const _Noise = {
/** Magnitude of the noise to apply to the interpolated value. */
noise_magnitude: OptionalField(float, 0, 'Magnitude of the noise to apply to the interpolated value.')
// support cummulative noise?
};
const _Common = {
/** Reference to the node. */
target_ref: RequiredField(str, 'Reference to the node.'),
/** Value accessor. */
property: RequiredField(union(str, list(union(str, int))), 'Value accessor.'),
/** Start time of the transition in milliseconds. */
start_ms: OptionalField(float, 0, 'Start time of the transition in milliseconds.'),
/** Duration of the transition in milliseconds. */
duration_ms: RequiredField(float, 'Duration of the transition in milliseconds.'),
};
const _Frequency = {
/** Determines how many times the interpolation loops. Current T = frequency * t mod 1. */
frequency: OptionalField(int, 1, 'Determines how many times the interpolation loops. Current T = frequency * t mod 1.'),
/** Whether to alternate the direction of the interpolation for frequency > 1. */
alternate_direction: OptionalField(bool, false, 'Whether to alternate the direction of the interpolation for frequency > 1.'),
};
const _Easing = {
/** Easing function to use for the transition. */
easing: OptionalField(Easing, 'linear', 'Easing function to use for the transition.'),
};
@@ -46,8 +62,11 @@ const ScalarInterpolation = {
..._Common,
..._Frequency,
..._Easing,
/** Start value. If a list of values is provided, each element will be interpolated separately. If unset, parent state value is used. */
start: OptionalField(nullable(union(float, list(float))), null, 'Start value. If a list of values is provided, each element will be interpolated separately. If unset, parent state value is used.'),
/** End value. If a list of values is provided, each element will be interpolated separately. If unset, only noise is applied. */
end: OptionalField(nullable(union(float, list(float))), null, 'End value. If a list of values is provided, each element will be interpolated separately. If unset, only noise is applied.'),
/** Whether to round the values to the closest integer. Useful for example for trajectory animation. */
discrete: OptionalField(bool, false, 'Whether to round the values to the closest integer. Useful for example for trajectory animation.'),
..._Noise,
};
@@ -56,8 +75,11 @@ const Vec3Interpolation = {
..._Common,
..._Frequency,
..._Easing,
/** Start value. If unset, parent state value is used. Must be array of length 3N (x1, y1, z1, x2, y2, z2, ...). */
start: OptionalField(nullable(list(float)), null, 'Start value. If unset, parent state value is used. Must be array of length 3N (x1, y1, z1, x2, y2, z2, ...).'),
/** End value. Must be array of length 3N (x1, y1, z1, x2, y2, z2, ...). If unset, only noise is applied. */
end: OptionalField(nullable(list(float)), null, 'End value. Must be array of length 3N (x1, y1, z1, x2, y2, z2, ...). If unset, only noise is applied.'),
/** Whether to use spherical interpolation. */
spherical: OptionalField(bool, false, 'Whether to use spherical interpolation.'),
..._Noise,
};
@@ -66,7 +88,9 @@ const RotationMatrixInterpolation = {
..._Common,
..._Frequency,
..._Easing,
/** Start value. If unset, parent state value is used. */
start: OptionalField(nullable(Matrix), null, 'Start value. If unset, parent state value is used.'),
/** End value. If unset, only noise is applied. */
end: OptionalField(nullable(Matrix), null, 'End value. If unset, only noise is applied.'),
..._Noise,
};
@@ -75,31 +99,53 @@ const ColorInterpolation = {
..._Common,
..._Frequency,
..._Easing,
/** Start value. If unset, parent state value is used. */
start: OptionalField(union(nullable(ColorT), dict(union(int, str), ColorT)), null, 'Start value. If unset, parent state value is used.'),
/** End value. */
end: OptionalField(union(nullable(ColorT), dict(union(int, str), ColorT)), null, 'End value.'),
/** Palette to sample colors from. Overrides start and end values. */
palette: OptionalField(nullable(union(DiscretePalette, ContinuousPalette)), null, 'Palette to sample colors from. Overrides start and end values.'),
};
const TransformationMatrixInterpolation = {
..._Common,
/** Pivot point for rotation and scale. */
pivot: OptionalField(nullable(Vector3), null, 'Pivot point for rotation and scale.'),
/** Start rotation value. If unset, parent state value is used. */
rotation_start: OptionalField(nullable(Matrix), null, 'Start rotation value. If unset, parent state value is used.'),
/** End rotation value. If unset, only noise is applied */
rotation_end: OptionalField(nullable(Matrix), null, 'End rotation value. If unset, only noise is applied.'),
/** Magnitude of the noise to apply to the rotation. */
rotation_noise_magnitude: OptionalField(float, 0, 'Magnitude of the noise to apply to the rotation.'),
/** Easing function to use for the rotation. */
rotation_easing: OptionalField(Easing, 'linear', 'Easing function to use for the rotation.'),
/** Determines how many times the rotation interpolation loops. Current T = frequency * t mod 1. */
rotation_frequency: OptionalField(int, 1, 'Determines how many times the rotation interpolation loops. Current T = frequency * t mod 1.'),
/** Whether to alternate the direction of the interpolation for frequency > 1. */
rotation_alternate_direction: OptionalField(bool, false, 'Whether to alternate the direction of the interpolation for frequency > 1.'),
/** Start translation value. If unset, parent state value is used. */
translation_start: OptionalField(nullable(Vector3), null, 'Start translation value. If unset, parent state value is used.'),
/** End translation value. If unset, only noise is applied. */
translation_end: OptionalField(nullable(Vector3), null, 'End translation value. If unset, only noise is applied.'),
/** Magnitude of the noise to apply to the translation. */
translation_noise_magnitude: OptionalField(float, 0, 'Magnitude of the noise to apply to the translation.'),
/** Easing function to use for the translation. */
translation_easing: OptionalField(Easing, 'linear', 'Easing function to use for the translation.'),
/** Determines how many times the translation interpolation loops. Current T = frequency * t mod 1. */
translation_frequency: OptionalField(int, 1, 'Determines how many times the translation interpolation loops. Current T = frequency * t mod 1.'),
/** Whether to alternate the direction of the interpolation for frequency > 1. */
translation_alternate_direction: OptionalField(bool, false, 'Whether to alternate the direction of the interpolation for frequency > 1.'),
/** Start scale value. If unset, parent state value is used. */
scale_start: OptionalField(nullable(Vector3), null, 'Start scale value. If unset, parent state value is used.'),
/** End scale value. If unset, only noise is applied. */
scale_end: OptionalField(nullable(Vector3), null, 'End scale value. If unset, only noise is applied.'),
/** Magnitude of the noise to apply to the scale. */
scale_noise_magnitude: OptionalField(float, 0, 'Magnitude of the noise to apply to the scale.'),
/** Easing function to use for the scale. */
scale_easing: OptionalField(Easing, 'linear', 'Easing function to use for the scale.'),
/** Determines how many times the scale interpolation loops. Current T = frequency * t mod 1. */
scale_frequency: OptionalField(int, 1, 'Determines how many times the scale interpolation loops. Current T = frequency * t mod 1.'),
/** Whether to alternate the direction of the interpolation for frequency > 1. */
scale_alternate_direction: OptionalField(bool, false, 'Whether to alternate the direction of the interpolation for frequency > 1.'),
};
@@ -110,12 +156,18 @@ export const MVSAnimationSchema = TreeSchema({
description: 'Animation root node',
parent: [],
params: SimpleParamsSchema({
frame_time_ms: OptionalField(float, 1000 / 60, 'Frame time in milliseconds'),
/** Frame time in milliseconds. */
frame_time_ms: OptionalField(float, 1000 / 60, 'Frame time in milliseconds.'),
/** Total duration of the animation. If not specified, computed as maximum of all transitions. */
duration_ms: OptionalField(nullable(float), null, 'Total duration of the animation. If not specified, computed as maximum of all transitions.'),
autoplay: OptionalField(bool, true, 'Determines whether the animation should autoplay when a snapshot is loaded'),
loop: OptionalField(bool, false, 'Determines whether the animation should loop when it reaches the end'),
include_camera: OptionalField(bool, false, 'Determines whether the camera state should be included in the animation'),
include_canvas: OptionalField(bool, false, 'Determines whether the canvas state should be included in the animation'),
/** Determines whether the animation should autoplay when a snapshot is loaded */
autoplay: OptionalField(bool, true, 'Determines whether the animation should autoplay when a snapshot is loaded.'),
/** Determines whether the animation should loop when it reaches the end. */
loop: OptionalField(bool, false, 'Determines whether the animation should loop when it reaches the end.'),
/** Determines whether the camera state should be included in the animation. */
include_camera: OptionalField(bool, false, 'Determines whether the camera state should be included in the animation.'),
/** Determines whether the canvas state should be included in the animation. */
include_canvas: OptionalField(bool, false, 'Determines whether the canvas state should be included in the animation.'),
}),
},
interpolate: {

View File

@@ -78,7 +78,16 @@ export function nullable<V>(type: iots.Type<V>): iots.Type<V | null> {
return union(type, iots.null);
}
/** Type definition for literal types, e.g. `literal('red', 'green', 'blue')` means 'red' or 'green' or 'blue' */
/** Type definition for literal types, e.g. `literal('red', 'green', 'blue')` means 'red' or 'green' or 'blue'.
*
* Example usage:
* ```
* export type MyColor = 'red' | 'green' | 'blue';
* export const MyColor = literal<MyColor>('red', 'green', 'blue');
* ```
*
* (it looks stupid to repeat the list of values but it will result in nicer type bundle (for MolViewStories))
*/
export function literal<V extends string | number | boolean>(...values: V[]) {
if (values.length === 0) {
throw new Error(`literal type must have at least one value`);

View File

@@ -28,12 +28,22 @@ export const ParseFormatMvsToMolstar = {
lammpstrj: { format: 'lammpstrj', is_binary: false },
// coordinates
xtc: { format: 'xtc', is_binary: true },
nctraj: { format: 'nctraj', is_binary: true },
dcd: { format: 'dcd', is_binary: true },
trr: { format: 'trr', is_binary: true },
// topology
psf: { format: 'psf', is_binary: false },
prmtop: { format: 'prmtop', is_binary: false },
top: { format: 'top', is_binary: false },
// maps
map: { format: 'map', is_binary: true },
dx: { format: 'dx', is_binary: false },
dxbin: { format: 'dxbin', is_binary: true },
} satisfies { [p in ParseFormatT]: { format: MolstarParseFormatT, is_binary: boolean } };
const TopologyFormats = new Set<ParseFormatT>(['psf', 'prmtop', 'top']);
/** Conversion rules for conversion from `MVSTree` (with all parameter values) to `MolstarTree` */
const mvsToMolstarConversionRules: ConversionRules<FullMVSTree, MolstarTree> = {
'download': node => ({ subtree: [] }),
@@ -68,7 +78,18 @@ const mvsToMolstarConversionRules: ConversionRules<FullMVSTree, MolstarTree> = {
if (parent?.kind !== 'parse') throw new Error(`Parent of "structure" must be "parse", not "${parent?.kind}".`);
const { format } = ParseFormatMvsToMolstar[parent.params.format];
if (node.params.coordinates_ref) {
if (TopologyFormats.has(parent.params.format)) {
if (!node.params.coordinates_ref) {
throw new Error(`"structure" node with topology format "${parent.params.format}" must have "coordinates_ref" parameter.`);
}
return {
subtree: [
{ kind: 'topology_with_coordinates', params: { format, coordinates_ref: node.params.coordinates_ref } },
{ kind: 'model', params: pickObjectKeys(node.params, ['model_index']) },
{ kind: 'structure', params: omitObjectKeys(node.params, ['block_header', 'block_index', 'model_index', 'coordinates_ref']), custom: node.custom, ref: node.ref },
] satisfies MolstarNode[]
};
} else if (node.params.coordinates_ref) {
return {
subtree: [
{ kind: 'trajectory', params: { format, ...pickObjectKeys(node.params, ['block_header', 'block_index']) } },
@@ -124,6 +145,13 @@ const StructureFormatExtensions: Record<ParseFormatT, (FileExtension | '*')[]> =
lammpstrj: ['.lammpstrj'],
// coordinates
xtc: ['.xtc'],
nctraj: ['.nc', '.nctraj'],
dcd: ['.dcd'],
trr: ['.trr'],
// topology
psf: ['.psf'],
prmtop: ['.prmtop', '.parm7'],
top: ['.top'],
// volumes
map: ['.map', '.ccp4', '.mrc', '.mrcs'],
dx: ['.dx'],

View File

@@ -21,12 +21,14 @@ export const MolstarTreeSchema = TreeSchema({
...FullMVSTreeSchema.nodes.download,
params: SimpleParamsSchema({
...FullMVSTreeSchema.nodes.download.params.fields,
/** Specifies whether file is downloaded as bytes array or string */
is_binary: RequiredField(bool, 'Specifies whether file is downloaded as bytes array or string'),
}),
},
parse: {
...FullMVSTreeSchema.nodes.parse,
params: SimpleParamsSchema({
/** File format */
format: RequiredField(MolstarParseFormatT, 'File format'),
}),
},
@@ -35,6 +37,7 @@ export const MolstarTreeSchema = TreeSchema({
description: "Auxiliary node corresponding to Molstar's CoordinatesFrom*.",
parent: ['parse'],
params: SimpleParamsSchema({
/** File format */
format: RequiredField(MolstarParseFormatT, 'File format'),
}),
},
@@ -43,6 +46,7 @@ export const MolstarTreeSchema = TreeSchema({
description: "Auxiliary node corresponding to Molstar's TrajectoryFrom*.",
parent: ['parse'],
params: SimpleParamsSchema({
/** File format */
format: RequiredField(MolstarParseFormatT, 'File format'),
...pickObjectKeys(FullMVSTreeSchema.nodes.structure.params.fields, ['block_header', 'block_index'] as const),
}),
@@ -52,13 +56,22 @@ export const MolstarTreeSchema = TreeSchema({
description: 'Auxiliary node corresponding to assigning a separate coordinates to a trajectory.',
parent: ['model'],
params: SimpleParamsSchema({
/** Coordinates reference */
coordinates_ref: RequiredField(str, 'Coordinates reference'),
}),
},
topology_with_coordinates: {
description: 'Auxiliary node corresponding to assigning a separate coordinates to a topology.',
parent: ['parse'],
params: SimpleParamsSchema({
format: RequiredField(MolstarParseFormatT, 'File format'),
coordinates_ref: RequiredField(str, 'Coordinates reference'),
}),
},
/** Auxiliary node corresponding to Molstar's ModelFromTrajectory. */
model: {
description: "Auxiliary node corresponding to Molstar's ModelFromTrajectory.",
parent: ['trajectory', 'trajectory_with_coordinates'],
parent: ['trajectory', 'trajectory_with_coordinates', 'topology_with_coordinates'],
params: SimpleParamsSchema(
pickObjectKeys(FullMVSTreeSchema.nodes.structure.params.fields, ['model_index'] as const)
),

View File

@@ -403,12 +403,24 @@ export class Primitives extends _Base<'primitives'> implements FocusMixin {
return this;
}
focus = bindMethod(this, FocusMixinImpl, 'focus');
/** Add a 'clip' node and return builder pointing back to the representation node. 'clip' node instructs to apply clipping to a visual representation. */
clip(params: MVSNodeParams<'clip'> & CustomAndRef): Primitives {
this.addChild('clip', params);
return this;
}
}
/** MVS builder pointing to a 'primitives_from_uri' node */
class PrimitivesFromUri extends _Base<'primitives_from_uri'> implements FocusMixin {
focus = bindMethod(this, FocusMixinImpl, 'focus');
/** Add a 'clip' node and return builder pointing back to the representation node. 'clip' node instructs to apply clipping to a visual representation. */
clip(params: MVSNodeParams<'clip'> & CustomAndRef): PrimitivesFromUri {
this.addChild('clip', params);
return this;
}
}

View File

@@ -8,9 +8,9 @@
import { bool, dict, float, int, list, literal, nullable, OptionalField, RequiredField, str, tuple, union } from '../generic/field-schema';
import { SimpleParamsSchema } from '../generic/params-schema';
import { NodeFor, ParamsOfKind, SubtreeOfKind, TreeFor, TreeSchema, TreeSchemaWithAllRequired } from '../generic/tree-schema';
import { MVSClipParams, MVSRepresentationParams, MVSVolumeRepresentationParams } from './mvs-tree-representations';
import { MVSPrimitiveParams } from './mvs-tree-primitives';
import { ColorT, ComponentExpressionT, ComponentSelectorT, Matrix, Palette, ParseFormatT, SchemaFormatT, SchemaT, StrList, StructureTypeT, Vector3 } from './param-types';
import { MVSClipParams, MVSRepresentationParams, MVSVolumeRepresentationParams } from './mvs-tree-representations';
import { ColorT, ComponentExpressionT, ComponentSelectorT, LabelAttachments, Matrix, Palette, ParseFormatT, SchemaFormatT, SchemaT, StrList, StructureTypeT, Vector3 } from './param-types';
const _DataFromUriParams = {
@@ -50,8 +50,6 @@ const _DataFromSourceParams = {
/** Color to be used e.g. for representations without 'color' node */
export const DefaultColor = 'white';
const LabelAttachments = literal('bottom-left', 'bottom-center', 'bottom-right', 'middle-left', 'middle-center', 'middle-right', 'top-left', 'top-center', 'top-right');
const TransformParams = SimpleParamsSchema({
/** Rotation matrix (3x3 matrix flattened in column major format (j*3+i indexing), this is equivalent to Fortran-order in numpy). This matrix will multiply the structure coordinates from the left. The default value is the identity matrix (corresponds to no rotation). */
rotation: OptionalField(Matrix, [1, 0, 0, 0, 1, 0, 0, 0, 1], 'Rotation matrix (3x3 matrix flattened in column major format (j*3+i indexing), this is equivalent to Fortran-order in numpy). This matrix will multiply the structure coordinates from the left. The default value is the identity matrix (corresponds to no rotation).'),
@@ -179,6 +177,7 @@ export const MVSTreeSchema = TreeSchema({
description: 'This node instructs to create a volume from a parsed data resource. "Volume" refers to an internal representation of volumetric data without any visual representation.',
parent: ['parse'],
params: SimpleParamsSchema({
/** Channel identifier (only applies when the input data contain multiple channels). */
channel_id: OptionalField(nullable(str), null, 'Channel identifier (only applies when the input data contain multiple channels).'),
}),
},
@@ -226,7 +225,7 @@ export const MVSTreeSchema = TreeSchema({
/** This node instructs to apply clipping to a visual representation. */
clip: {
description: 'This node instructs to apply clipping to a visual representation.',
parent: ['representation', 'volume_representation'],
parent: ['representation', 'volume_representation', 'primitives', 'primitives_from_uri'],
params: MVSClipParams,
},
/** This node instructs to apply opacity/transparency to a visual representation. */
@@ -324,6 +323,8 @@ export const MVSTreeSchema = TreeSchema({
position: RequiredField(Vector3, 'Coordinates of the camera.'),
/** Vector which will be aligned with the screen Y axis. */
up: OptionalField(Vector3, [0, 1, 0], 'Vector which will be aligned with the screen Y axis.'),
/** Near clipping plane distance from the position. */
near: OptionalField(nullable(float), null, 'Near clipping plane distance from the position.'),
}),
},
/** This node sets canvas properties. */

View File

@@ -6,12 +6,20 @@
*/
import * as iots from 'io-ts';
import { ColorName, HexColor } from '../../helpers/utils';
import { ColorNames } from '../../../../mol-util/color/names';
import { ValueFor, bool, dict, float, int, list, literal, nullable, object, partial, str, tuple, union } from '../generic/field-schema';
/** `format` parameter values for `parse` node in MVS tree */
export const ParseFormatT = literal(
export type ParseFormatT =
// trajectory
| 'mmcif' | 'bcif' | 'pdb' | 'pdbqt' | 'gro' | 'xyz' | 'mol' | 'sdf' | 'mol2' | 'lammpstrj'
// coordinates
| 'xtc' | 'nctraj' | 'dcd' | 'trr'
// topology
| 'psf' | 'prmtop' | 'top'
// volumes
| 'map' | 'dx' | 'dxbin'
export const ParseFormatT = literal<ParseFormatT>(
// trajectory
'mmcif',
'bcif', // +volumes
@@ -25,15 +33,30 @@ export const ParseFormatT = literal(
'lammpstrj', // + coordinates
// coordinates
'xtc',
'nctraj',
'dcd',
'trr',
// topology
'psf',
'prmtop',
'top',
// volumes
'map',
'dx',
'dxbin',
);
export type ParseFormatT = ValueFor<typeof ParseFormatT>
/** `format` parameter values for `parse` node in Molstar tree */
export const MolstarParseFormatT = literal(
export type MolstarParseFormatT =
// trajectory
| 'cif' | 'pdb' | 'pdbqt' | 'gro' | 'xyz' | 'mol' | 'sdf' | 'mol2' | 'lammpstrj'
// coordinates
| 'xtc' | 'nctraj' | 'dcd' | 'trr'
// topology
| 'psf' | 'prmtop' | 'top'
// volumes
| 'map' | 'dx' | 'dxbin'
export const MolstarParseFormatT = literal<MolstarParseFormatT>(
// trajectory
'cif', // +volumes
'pdb',
@@ -46,21 +69,29 @@ export const MolstarParseFormatT = literal(
'lammpstrj',
// coordinates
'xtc',
'nctraj',
'dcd',
'trr',
// topology
'psf',
'prmtop',
'top',
// volumes
'map',
'dx',
'dxbin',
);
export type MolstarParseFormatT = ValueFor<typeof MolstarParseFormatT>
/** `kind` parameter values for `structure` node in MVS tree */
export const StructureTypeT = literal('model', 'assembly', 'symmetry', 'symmetry_mates');
export type StructureTypeT = 'model' | 'assembly' | 'symmetry' | 'symmetry_mates';
export const StructureTypeT = literal<StructureTypeT>('model', 'assembly', 'symmetry', 'symmetry_mates');
/** `selector` parameter values for `component` node in MVS tree */
export const ComponentSelectorT = literal('all', 'polymer', 'protein', 'nucleic', 'branched', 'ligand', 'ion', 'water', 'coarse');
export type ComponentSelectorT = 'all' | 'polymer' | 'protein' | 'nucleic' | 'branched' | 'ligand' | 'ion' | 'water' | 'coarse';
export const ComponentSelectorT = literal<ComponentSelectorT>('all', 'polymer', 'protein', 'nucleic', 'branched', 'ligand', 'ion', 'water', 'coarse');
/** `selector` parameter values for `component` node in MVS tree */
export const ComponentExpressionT = partial({
const _ComponentExpressionT = partial({
label_entity_id: str,
label_asym_id: str,
auth_asym_id: str,
@@ -84,24 +115,39 @@ export const ComponentExpressionT = partial({
* like 'ASM-X0-1' for assemblies or '1_555' for crystals */
instance_id: str,
});
export type ComponentExpressionT = ValueFor<typeof ComponentExpressionT>
/** `selector` parameter values for `component` node in MVS tree */
export interface ComponentExpressionT extends ValueFor<typeof _ComponentExpressionT> { }
export const ComponentExpressionT: iots.Type<ComponentExpressionT> = _ComponentExpressionT;
/** `schema` parameter values for `*_from_uri` and `*_from_source` nodes in MVS tree */
export const SchemaT = literal('whole_structure', 'entity', 'chain', 'auth_chain', 'residue', 'auth_residue', 'residue_range', 'auth_residue_range', 'atom', 'auth_atom', 'all_atomic');
export type SchemaT = 'whole_structure' | 'entity' | 'chain' | 'auth_chain' | 'residue' | 'auth_residue' | 'residue_range' | 'auth_residue_range' | 'atom' | 'auth_atom' | 'all_atomic';
export const SchemaT = literal<SchemaT>('whole_structure', 'entity', 'chain', 'auth_chain', 'residue', 'auth_residue', 'residue_range', 'auth_residue_range', 'atom', 'auth_atom', 'all_atomic');
/** `format` parameter values for `*_from_uri` nodes in MVS tree */
export const SchemaFormatT = literal('cif', 'bcif', 'json');
export type SchemaFormatT = 'cif' | 'bcif' | 'json';
export const SchemaFormatT = literal<SchemaFormatT>('cif', 'bcif', 'json');
/** Parameter values for vector params, e.g. `position` */
export const Vector3 = tuple([float, float, float]);
export type Vector3 = ValueFor<typeof Vector3>
export type Vector3 = [number, number, number];
export const Vector3: iots.Type<Vector3> = tuple([float, float, float]);
/** Parameter values for matrix params, e.g. `rotation` */
export const Matrix = list(float);
export const Matrix = list(float); // TODO impl custom types Matrix3x3 and Matrix4x4
export type LabelAttachments = 'bottom-left' | 'bottom-center' | 'bottom-right' | 'middle-left' | 'middle-center' | 'middle-right' | 'top-left' | 'top-center' | 'top-right';
export const LabelAttachments = literal<LabelAttachments>('bottom-left', 'bottom-center', 'bottom-right', 'middle-left', 'middle-center', 'middle-right', 'top-left', 'top-center', 'top-right');
/** Primitives-related types */
export const PrimitiveComponentExpressionT = partial({ structure_ref: str, expression_schema: SchemaT, expressions: list(ComponentExpressionT) });
export type PrimitiveComponentExpressionT = ValueFor<typeof PrimitiveComponentExpressionT>
const _PrimitiveComponentExpressionT = partial({
structure_ref: str,
expression_schema: SchemaT,
expressions: list(ComponentExpressionT),
});
/** Primitives-related types */
export interface PrimitiveComponentExpressionT extends ValueFor<typeof _PrimitiveComponentExpressionT> { }
export const PrimitiveComponentExpressionT: iots.Type<PrimitiveComponentExpressionT> = _PrimitiveComponentExpressionT;
export const PrimitivePositionT = union(Vector3, ComponentExpressionT, PrimitiveComponentExpressionT);
export type PrimitivePositionT = ValueFor<typeof PrimitivePositionT>
@@ -110,27 +156,61 @@ export const IntList = list(int);
export const StrList = list(str);
/** `color` parameter values for `color` node in MVS tree */
export const HexColorT = new iots.Type<HexColor>(
/** Hexadecimal color string, e.g. '#FF1100' (the type matches more than just valid HexColor strings) */
export type HexColorT = `#${string}`;
export const HexColorT = new iots.Type<HexColorT>(
'HexColor',
((value: any) => typeof value === 'string') as any,
(value, ctx) => HexColor.is(value) ? { _tag: 'Right', right: value } : { _tag: 'Left', left: [{ value: value, context: ctx, message: `"${value}" is not a valid hex color string` }] },
(value, ctx) => isHexColorT(value) ? { _tag: 'Right', right: value } : { _tag: 'Left', left: [{ value: value, context: ctx, message: `"${value}" is not a valid hex color string` }] },
value => value
);
/** Regular expression matching a hexadecimal color string, e.g. '#FF1100' or '#f10' */
const hexColorRegex = /^#([0-9A-F]{3}){1,2}$/i;
/** Decide if a string is a valid hexadecimal color string (6-digit or 3-digit, e.g. '#FF1100' or '#f10') */
function isHexColorT(str: any): str is HexColorT {
return typeof str === 'string' && hexColorRegex.test(str);
}
/** `color` parameter values for `color` node in MVS tree */
export const ColorNameT = new iots.Type<ColorName>(
/** Named color string (e.g. 'red') for `color` parameter values for `color` node in MVS tree */
export const ColorNameT = new iots.Type<ColorNameT>(
'ColorName',
((value: any) => typeof value === 'string') as any,
(value, ctx) => ColorName.is(value) ? { _tag: 'Right', right: value } : { _tag: 'Left', left: [{ value: value, context: ctx, message: `"${value}" is not a valid color name` }] },
(value, ctx) => isColorNameT(value) ? { _tag: 'Right', right: value } : { _tag: 'Left', left: [{ value: value, context: ctx, message: `"${value}" is not a valid color name` }] },
value => value
);
export type ColorNameT =
| 'aliceblue' | 'antiquewhite' | 'aqua' | 'aquamarine' | 'azure' | 'beige' | 'bisque' | 'black'
| 'blanchedalmond' | 'blue' | 'blueviolet' | 'brown' | 'burlywood' | 'cadetblue' | 'chartreuse'
| 'chocolate' | 'coral' | 'cornflower' | 'cornflowerblue' | 'cornsilk' | 'crimson' | 'cyan' | 'darkblue'
| 'darkcyan' | 'darkgoldenrod' | 'darkgray' | 'darkgreen' | 'darkgrey' | 'darkkhaki' | 'darkmagenta'
| 'darkolivegreen' | 'darkorange' | 'darkorchid' | 'darkred' | 'darksalmon' | 'darkseagreen' | 'darkslateblue'
| 'darkslategray' | 'darkslategrey' | 'darkturquoise' | 'darkviolet' | 'deeppink' | 'deepskyblue' | 'dimgray'
| 'dimgrey' | 'dodgerblue' | 'firebrick' | 'floralwhite' | 'forestgreen' | 'fuchsia' | 'gainsboro'
| 'ghostwhite' | 'gold' | 'goldenrod' | 'gray' | 'green' | 'greenyellow' | 'grey' | 'honeydew' | 'hotpink'
| 'indianred' | 'indigo' | 'ivory' | 'khaki' | 'laserlemon' | 'lavender' | 'lavenderblush' | 'lawngreen'
| 'lemonchiffon' | 'lightblue' | 'lightcoral' | 'lightcyan' | 'lightgoldenrod' | 'lightgoldenrodyellow'
| 'lightgray' | 'lightgreen' | 'lightgrey' | 'lightpink' | 'lightsalmon' | 'lightseagreen' | 'lightskyblue'
| 'lightslategray' | 'lightslategrey' | 'lightsteelblue' | 'lightyellow' | 'lime' | 'limegreen' | 'linen'
| 'magenta' | 'maroon' | 'maroon2' | 'maroon3' | 'mediumaquamarine' | 'mediumblue' | 'mediumorchid' | 'mediumpurple'
| 'mediumseagreen' | 'mediumslateblue' | 'mediumspringgreen' | 'mediumturquoise' | 'mediumvioletred' | 'midnightblue'
| 'mintcream' | 'mistyrose' | 'moccasin' | 'navajowhite' | 'navy' | 'oldlace' | 'olive' | 'olivedrab' | 'orange'
| 'orangered' | 'orchid' | 'palegoldenrod' | 'palegreen' | 'paleturquoise' | 'palevioletred' | 'papayawhip'
| 'peachpuff' | 'peru' | 'pink' | 'plum' | 'powderblue' | 'purple' | 'purple2' | 'purple3' | 'rebeccapurple'
| 'red' | 'rosybrown' | 'royalblue' | 'saddlebrown' | 'salmon' | 'sandybrown' | 'seagreen' | 'seashell' | 'sienna'
| 'silver' | 'skyblue' | 'slateblue' | 'slategray' | 'slategrey' | 'snow' | 'springgreen' | 'steelblue' | 'tan'
| 'teal' | 'thistle' | 'tomato' | 'turquoise' | 'violet' | 'wheat' | 'white' | 'whitesmoke' | 'yellow' | 'yellowgreen'
/** Decide if a string is a valid named color string */
function isColorNameT(str: any): str is ColorNameT {
return str in ColorNames;
}
/** `color` parameter values for `color` node in MVS tree */
export const ColorT = union(ColorNameT, HexColorT);
export type ColorT = ValueFor<typeof ColorT>
export type ColorT = ColorNameT | HexColorT;
export const ColorT: iots.Type<ColorT> = union(ColorNameT, HexColorT);
// Type helpers
/** Type helpers */
export function isVector3(x: any): x is Vector3 {
return !!x && Array.isArray(x) && x.length === 3 && typeof x[0] === 'number';
}
@@ -144,7 +224,23 @@ export function isComponentExpression(x: any): x is ComponentExpressionT {
}
export const ColorListNameT = literal(
export type ColorListNameT =
// Color lists from https://observablehq.com/@d3/color-schemes (definitions: https://colorbrewer2.org/export/colorbrewer.js)
// Sequential single-hue
| 'Reds' | 'Oranges' | 'Greens' | 'Blues' | 'Purples' | 'Greys'
// Sequential multi-hue
| 'OrRd' | 'BuGn' | 'PuBuGn' | 'GnBu' | 'PuBu' | 'BuPu' | 'RdPu' | 'PuRd' | 'YlOrRd' | 'YlOrBr' | 'YlGn' | 'YlGnBu'
| 'Magma' | 'Inferno' | 'Plasma' | 'Viridis' | 'Cividis' | 'Turbo' | 'Warm' | 'Cool' | 'CubehelixDefault'
// Cyclical
| 'Rainbow' | 'Sinebow'
// Diverging
| 'RdBu' | 'RdGy' | 'PiYG' | 'BrBG' | 'PRGn' | 'PuOr' | 'RdYlGn' | 'RdYlBu' | 'Spectral'
// Categorical
| 'Category10' | 'Observable10' | 'Tableau10'
| 'Set1' | 'Set2' | 'Set3' | 'Pastel1' | 'Pastel2' | 'Dark2' | 'Paired' | 'Accent'
// Additional lists, not standard for visualization in general, but commonly used for structures
| 'Chainbow'
export const ColorListNameT = literal<ColorListNameT>(
// Color lists from https://observablehq.com/@d3/color-schemes (definitions: https://colorbrewer2.org/export/colorbrewer.js)
// Sequential single-hue
'Reds', 'Oranges', 'Greens', 'Blues', 'Purples', 'Greys',
@@ -162,13 +258,12 @@ export const ColorListNameT = literal(
// Additional lists, not standard for visualization in general, but commonly used for structures
'Chainbow',
);
export type ColorListNameT = ValueFor<typeof ColorListNameT>;
export const ColorDictNameT = literal('ElementSymbol', 'ResidueName', 'ResidueProperties', 'SecondaryStructure');
export type ColorDictNameT = ValueFor<typeof ColorDictNameT>;
export type ColorDictNameT = 'ElementSymbol' | 'ResidueName' | 'ResidueProperties' | 'SecondaryStructure';
export const ColorDictNameT = literal<ColorDictNameT>('ElementSymbol', 'ResidueName', 'ResidueProperties', 'SecondaryStructure');
export const CategoricalPalette = object(
const _CategoricalPalette = object(
{
kind: literal('categorical'),
},
@@ -192,7 +287,8 @@ export const CategoricalPalette = object(
missing_color: nullable(ColorT),
}
);
export type CategoricalPalette = ValueFor<typeof CategoricalPalette>;
export interface CategoricalPalette extends ValueFor<typeof _CategoricalPalette> { }
export const CategoricalPalette: iots.Type<CategoricalPalette> = _CategoricalPalette;
export const CategoricalPaletteDefaults: Required<CategoricalPalette> = {
kind: 'categorical',
@@ -205,7 +301,7 @@ export const CategoricalPaletteDefaults: Required<CategoricalPalette> = {
};
export const DiscretePalette = object(
const _DiscretePalette = object(
{
kind: literal('discrete'),
},
@@ -231,7 +327,8 @@ export const DiscretePalette = object(
value_domain: tuple([nullable(float), nullable(float)]),
}
);
export type DiscretePalette = ValueFor<typeof DiscretePalette>;
export interface DiscretePalette extends ValueFor<typeof _DiscretePalette> { }
export const DiscretePalette: iots.Type<DiscretePalette> = _DiscretePalette;
export const DiscretePaletteDefaults: Required<DiscretePalette> = {
kind: 'discrete',
@@ -242,10 +339,9 @@ export const DiscretePaletteDefaults: Required<DiscretePalette> = {
};
export const ContinuousPalette = object(
const _ContinuousPalette = object(
{
kind: literal('continuous'),
},
// Optionals:
{
@@ -269,7 +365,8 @@ export const ContinuousPalette = object(
overflow_color: nullable(union(literal('auto'), ColorT)),
}
);
export type ContinuousPalette = ValueFor<typeof ContinuousPalette>;
export interface ContinuousPalette extends ValueFor<typeof _ContinuousPalette> { }
export const ContinuousPalette: iots.Type<ContinuousPalette> = _ContinuousPalette;
export const ContinuousPaletteDefaults: Required<ContinuousPalette> = {
kind: 'continuous',

View File

@@ -5,13 +5,16 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra';
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { CameraTransitionManager } from './camera/transition';
import { BehaviorSubject } from 'rxjs';
import { Scene } from '../mol-gl/scene';
import { assertUnreachable } from '../mol-util/type-helpers';
import { Ray3D } from '../mol-math/geometry/primitives/ray3d';
import { Mat4 } from '../mol-math/linear-algebra/3d/mat4';
import { Vec4 } from '../mol-math/linear-algebra/3d/vec4';
import { Vec3 } from '../mol-math/linear-algebra/3d/vec3';
import { EPSILON } from '../mol-math/linear-algebra/3d/common';
export type { ICamera };

View File

@@ -5,8 +5,9 @@
*/
import { Camera } from '../camera';
import { Quat, Vec3 } from '../../mol-math/linear-algebra';
import { lerp } from '../../mol-math/interpolate';
import { Quat } from '../../mol-math/linear-algebra/3d/quat';
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
export { CameraTransitionManager };

View File

@@ -4,7 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra';
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
import { Vec4 } from '../../mol-math/linear-algebra/3d/vec4';
export { Viewport };

View File

@@ -75,7 +75,7 @@ export const Canvas3DParams = {
cameraClipping: PD.Group({
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
minNear: PD.Numeric(5, { min: 0.1, max: 100, step: 0.1 }, { description: 'Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
minNear: PD.Numeric(1, { min: 0.1, max: 100, step: 0.1 }, { description: 'Minimal allowed distance of near clipping plane from the camera. Note, may cause performance issues rendering impostors when set too small and cause issues with outline rendering when too close to 0.' }),
}, { pivot: 'radius' }),
viewport: PD.MappedStatic('canvas', {
canvas: PD.Group({}),
@@ -124,6 +124,9 @@ export const DefaultCanvas3DAttribs = {
xr: DefaultXRManagerAttribs,
};
export type Canvas3DAttribs = typeof DefaultCanvas3DAttribs
export type PartialCanvas3DAttribs = {
[K in keyof Canvas3DAttribs]?: Canvas3DAttribs[K] extends { name: string, params: any } ? Canvas3DAttribs[K] : Partial<Canvas3DAttribs[K]>
}
export { Canvas3DContext };
@@ -373,6 +376,7 @@ interface Canvas3D {
readonly boundingSphere: Readonly<Sphere3D>
readonly boundingSphereVisible: Readonly<Sphere3D>
setProps(props: PartialCanvas3DProps | ((old: Canvas3DProps) => Partial<Canvas3DProps> | void), doNotRequestDraw?: boolean /* = false */): void
setAttribs(attribs: PartialCanvas3DAttribs): void
getImagePass(props: Partial<ImageProps>): ImagePass
getRenderObjects(): GraphicsRenderObject[]
@@ -515,7 +519,7 @@ namespace Canvas3D {
}
}
const xrManager = new XRManager(webgl, input, scene, camera, stereoCamera, helper.pointer, interactionHelper);
const xrManager = new XRManager(webgl, input, scene, camera, stereoCamera, helper.pointer, interactionHelper, p.xr, a.xr);
const xr = {
request: async () => {
@@ -1358,8 +1362,12 @@ namespace Canvas3D {
requestDraw();
}
},
setAttribs: (attribs: PartialCanvas3DAttribs) => {
if (attribs.trackball) controls.setAttribs(attribs.trackball);
if (attribs.xr) xrManager.setAttribs(attribs.xr);
},
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, passes.draw.transparency, props);
return new ImagePass(webgl, assetManager, renderer, scene, camera, helper, props);
},
getRenderObjects(): GraphicsRenderObject[] {
const renderObjects: GraphicsRenderObject[] = [];
@@ -1371,7 +1379,10 @@ namespace Canvas3D {
return getProps();
},
get attribs() {
return a;
return {
trackball: controls.attribs,
xr: xrManager.attribs,
};
},
get input() {
return input;

View File

@@ -75,7 +75,7 @@ export const TrackballControlsParams = {
animate: PD.MappedStatic('off', {
off: PD.EmptyGroup(),
spin: PD.Group({
speed: PD.Numeric(0.3, { min: -5, max: 5, step: 0.1 }, { description: 'Number of rotations per second' }),
speed: PD.Numeric(0.1, { min: -2, max: 2, step: 0.01 }, { description: 'Number of rotations per second' }),
}, { description: 'Spin the 3D scene around the x-axis in view space' }),
rock: PD.Group({
speed: PD.Numeric(0.3, { min: -5, max: 5, step: 0.1 }, { description: 'Number of oscilations per second' }),

View File

@@ -90,11 +90,16 @@ export class XRManager {
private hit: Vec3 | undefined = undefined;
readonly props: XRManagerProps;
readonly attribs: XRManagerAttribs;
setProps(props: Partial<XRManagerProps>) {
Object.assign(this.props, props);
}
setAttribs(attribs: Partial<XRManagerAttribs>) {
Object.assign(this.attribs, attribs);
}
private intersect(camera: ICamera, view: Mat4, plane: Plane3D, targetRayPose: XRPose): { point: Vec3, screen: Vec2 } | undefined {
const point = Vec3();
const ray = getRayFromPose(targetRayPose, view);
@@ -310,6 +315,7 @@ export class XRManager {
constructor(private webgl: WebGLContext, private input: InputObserver, private scene: Scene, private camera: Camera, private stereoCamera: StereoCamera, private pointerHelper: PointerHelper, private interactionHelper: Canvas3dInteractionHelper, props: Partial<XRManagerProps> = {}, attribs: Partial<XRManagerAttribs> = {}) {
this.props = { ...PD.getDefaultValues(XRManagerParams), ...props };
this.attribs = { ...DefaultXRManagerAttribs, ...attribs };
this.hoverSub = this.interactionHelper.events.hover.subscribe(({ position }) => {
this.hit = position;
@@ -323,9 +329,9 @@ export class XRManager {
this.checkSupported();
navigator.xr?.addEventListener('devicechange', this.checkSupported);
const b = { ...DefaultXRManagerBindings, ...attribs.bindings };
this.keyUpSub = input.keyUp.subscribe(({ code, modifiers, key }) => {
const b = this.attribs.bindings;
if (Binding.matchKey(b.exit, code, modifiers, key)) {
this.end();
}
@@ -336,6 +342,8 @@ export class XRManager {
});
this.gestureSub = input.gesture.subscribe(({ scale, button, modifiers }) => {
const b = this.attribs.bindings;
if (Binding.match(b.gestureScale, button, modifiers)) {
this.setScaleFactor(scale);
}

View File

@@ -163,7 +163,7 @@ export class IlluminationPass {
const { gl, state } = this.webgl;
const markingEnabled = MarkingPass.isEnabled(props.marking);
const hasTransparent = scene.opacityAverage < 1;
const hasTransparent = scene.opacityAverage < 1 || scene.volumes.renderables.length > 0;
const hasMarking = markingEnabled && scene.markerAverage > 0;
this.transparentTarget.bind();
@@ -347,7 +347,7 @@ export class IlluminationPass {
const dofEnabled = DofPass.isEnabled(props.postprocessing);
const markingEnabled = MarkingPass.isEnabled(props.marking);
const hasTransparent = scene.opacityAverage < 1;
const hasTransparent = scene.opacityAverage < 1 || scene.volumes.renderables.length > 0;
const hasMarking = markingEnabled && scene.markerAverage > 0;
let needsUpdateCompose = false;

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
@@ -57,10 +58,10 @@ export class ImagePass {
get width() { return this._width; }
get height() { return this._height; }
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, transparency: 'wboit' | 'dpoit' | 'blended', props: Partial<ImageProps>) {
constructor(private webgl: WebGLContext, assetManager: AssetManager, private renderer: Renderer, private scene: Scene, private camera: Camera, helper: Helper, props: Partial<ImageProps>) {
this.props = { ...PD.getDefaultValues(ImageParams), ...props };
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, transparency);
this.drawPass = new DrawPass(webgl, assetManager, 128, 128, scene.transparency);
this.illuminationPass = new IlluminationPass(webgl, this.drawPass);
this.multiSamplePass = new MultiSamplePass(webgl, this.drawPass);
this.multiSampleHelper = new MultiSampleHelper(this.multiSamplePass);
@@ -104,6 +105,7 @@ export class ImagePass {
}
async render(runtime: RuntimeContext) {
this.drawPass.setTransparency(this.scene.transparency);
ShaderManager.ensureRequired(this.webgl, this.scene, this.props);
Camera.copySnapshot(this._camera.state, this.camera.state);
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height);

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { EquivalenceClasses } from '../util';
import { EquivalenceClasses } from '../util/equivalence-classes';
describe('equiv-classes', () => {
it('integer mod classes', () => {

View File

@@ -6,8 +6,8 @@
*/
import * as ColumnHelpers from './column-helpers';
import { Tensor as Tensors } from '../../mol-math/linear-algebra';
import { Tokens } from '../../mol-io/reader/common/text/tokenizer';
import { Tensor as Tensors } from '../../mol-math/linear-algebra/tensor';
import type { Tokens } from '../../mol-io/reader/common/text/tokenizer';
import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../mol-io/reader/common/text/number-parser';
interface Column<T> {

View File

@@ -6,7 +6,7 @@
import { Column } from './column';
import { sortArray } from '../util/sort';
import { StringBuilder } from '../../mol-util';
import { StringBuilder } from '../../mol-util/string-builder';
/** A collection of columns */
type Table<Schema extends Table.Schema = any> = {

View File

@@ -5,7 +5,9 @@
* @author Adam Midlik <midlik@gmail.com>
*/
import { sortArray, hash3, hash4, createRangeArray } from '../../util';
import { createRangeArray } from '../../util/array';
import { hash3, hash4 } from '../../util/hash-functions';
import { sortArray } from '../../util/sort';
import { Interval } from '../interval';
type Nums = ArrayLike<number>

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { iterableToArray } from '../util';
import { iterableToArray } from '../util/array';
// TODO: rename to "linear map" and just do key value mapping from index?

View File

@@ -4,8 +4,11 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Segmentation, OrderedSet, SortedArray, Interval } from '../int';
import { Iterator as _Iterator } from '../iterator';
import { Interval } from './interval';
import { OrderedSet } from './ordered-set';
import { Segmentation } from './segmentation';
import { SortedArray } from './sorted-array';
/** Pairs of min and max indices of sorted, non-overlapping ranges */
type SortedRanges<T extends number = number> = SortedArray<T>

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { hash2 } from '../util';
import { hash2 } from '../util/hash-functions';
/**
* Represents a pair of two integers as a double,

View File

@@ -4,7 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Interval, OrderedSet, SortedArray } from '../../int';
import { Interval } from '../../int/interval';
import { OrderedSet } from '../../int/ordered-set';
import { SortedArray } from '../../int/sorted-array';
import { IntervalIterator } from '../interval-iterator';
describe('interval', () => {

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Column } from '../db';
import { Column } from '../db/column';
export interface Grouping<V, K> {
map: Map<K, V[]>,

View File

@@ -4,8 +4,10 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Interval } from '../int/interval';
import { OrderedSet } from '../int/ordered-set';
import { Segmentation } from '../int/segmentation';
import { Iterator } from '../iterator';
import { OrderedSet, Interval, Segmentation } from '../int';
/** Emits a segment of length one for each element in the interval that is also in the set */
export class IntervalIterator<I extends number = number> implements Iterator<Segmentation.Segment<I>> {

View File

@@ -122,7 +122,9 @@ export function createValueColor(value: Color, colorData?: ColorData): ColorData
/** Creates color uniform */
function createUniformColor(locationIt: LocationIterator, color: LocationColor, colorData?: ColorData): ColorData {
return createValueColor(color(NullLocation, false), colorData);
locationIt.reset();
const loc = locationIt.hasNext ? locationIt.move() : { location: NullLocation, isSecondary: false };
return createValueColor(color(loc.location, loc.isSecondary), colorData);
}
//

View File

@@ -5,7 +5,7 @@
*/
import { hashFnv32a } from '../../../mol-data/util';
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
import { RenderableState } from '../../../mol-gl/renderable';
import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';

View File

@@ -5,7 +5,7 @@
*/
import { hashFnv32a } from '../../../mol-data/util';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { LocationIterator } from '../../util/location-iterator';
import { RenderableState } from '../../../mol-gl/renderable';
import { calculateTransformBoundingSphere, createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';

View File

@@ -8,7 +8,7 @@
import { ChunkedArray } from '../../../mol-data/util';
import { Lines } from './lines';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { Cage } from '../../../mol-geo/primitive/cage';
import { Cage } from '../../primitive/cage';
export interface LinesBuilder {
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void

View File

@@ -8,7 +8,7 @@ import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
import { ChunkedArray } from '../../../mol-data/util';
import { Mesh } from './mesh';
import { Primitive } from '../../primitive/primitive';
import { Cage } from '../../../mol-geo/primitive/cage';
import { Cage } from '../../primitive/cage';
import { addSphere } from './builder/sphere';
import { addCylinder } from './builder/cylinder';

View File

@@ -77,7 +77,9 @@ export function createValueSize(value: number, sizeData?: SizeData): SizeData {
/** Creates size uniform */
export function createUniformSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
return createValueSize(sizeFn(NullLocation), sizeData);
locationIt.reset();
const location = locationIt.hasNext ? locationIt.move().location : NullLocation;
return createValueSize(sizeFn(location), sizeData);
}
export function createTextureSize(sizes: TextureImage<Uint8Array>, type: SizeType, sizeData?: SizeData): SizeData {

View File

@@ -8,7 +8,7 @@ import { ValueCell } from '../../../mol-util';
import { GeometryUtils } from '../geometry';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { TransformData } from '../transform-data';
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
import { Theme } from '../../../mol-theme/theme';
import { SpheresValues } from '../../../mol-gl/renderable/spheres';
import { createColors } from '../color-data';

View File

@@ -2,12 +2,13 @@
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Adam Midlik <midlik@gmail.com>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { ValueCell } from '../../../mol-util';
import { GeometryUtils } from '../geometry';
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
import { TransformData } from '../transform-data';
import { Theme } from '../../../mol-theme/theme';
import { createColors } from '../color-data';
@@ -333,5 +334,5 @@ function getPadding(mappings: Float32Array, depths: Float32Array, charCount: num
const d = Math.abs(depths[i]);
if (d > maxDepth) maxDepth = d;
}
return scale * Math.max(maxDepth, maxOffset);
return Math.max(maxDepth, scale * maxOffset);
}

View File

@@ -8,7 +8,7 @@
import { ValueCell } from '../../../mol-util';
import { Sphere3D } from '../../../mol-math/geometry';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { LocationIterator, PositionLocation } from '../../../mol-geo/util/location-iterator';
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
import { TransformData } from '../transform-data';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';

View File

@@ -786,5 +786,6 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
stencilOp: function () { },
stencilOpSeparate: function () { },
unpackColorSpace: 'srgb',
makeXRCompatible: async function () { },
};
}

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { LinkedList } from '../mol-data/generic';
import { LinkedList } from '../mol-data/generic/linked-list';
import { GraphicsRenderObject } from './render-object';
type N = LinkedList.Node<GraphicsRenderObject>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -38,6 +38,7 @@ const IsosurfaceSchema = {
uGridDim: UniformSpec('v3'),
uGridTexDim: UniformSpec('v3'),
uGridDataDim: UniformSpec('v3'),
uGridTransform: UniformSpec('m4'),
uGridTransformAdjoint: UniformSpec('m3'),
uScale: UniformSpec('v2'),
@@ -54,7 +55,7 @@ function valueChannel(ctx: WebGLContext, volumeData: Texture) {
return isWebGL2(ctx.gl) && volumeData.format === ctx.gl.RED ? 'red' : 'alpha';
}
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean): ComputeRenderable<IsosurfaceValues> {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
const v = ctx.namedComputeRenderables[IsosurfaceName].values as IsosurfaceValues;
@@ -71,6 +72,7 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ValueCell.update(v.uGridDim, gridDim);
ValueCell.update(v.uGridTexDim, gridTexDim);
ValueCell.update(v.uGridDataDim, gridDataDim);
ValueCell.update(v.uGridTransform, transform);
ValueCell.update(v.uGridTransformAdjoint, Mat3.adjointFromMat4(Mat3(), transform));
ValueCell.update(v.uScale, scale);
@@ -81,12 +83,12 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
ctx.namedComputeRenderables[IsosurfaceName].update();
} else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, gridDataDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
}
return ctx.namedComputeRenderables[IsosurfaceName];
}
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture, activeVoxelsBase: Texture, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, levels: number, scale: Vec2, count: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean) {
// console.log('uSize', Math.pow(2, levels))
const values: IsosurfaceValues = {
...QuadValues,
@@ -105,6 +107,7 @@ function createIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Text
uGridDim: ValueCell.create(gridDim),
uGridTexDim: ValueCell.create(gridTexDim),
uGridDataDim: ValueCell.create(gridDataDim),
uGridTransform: ValueCell.create(transform),
uGridTransformAdjoint: ValueCell.create(Mat3.adjointFromMat4(Mat3(), transform)),
uScale: ValueCell.create(scale),
@@ -132,7 +135,7 @@ function setRenderingDefaults(ctx: WebGLContext) {
state.clearColor(0, 0, 0, 0);
}
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
const { drawBuffers } = ctx.extensions;
if (!drawBuffers) throw new Error('need WebGL draw buffers');
@@ -189,7 +192,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
groupTexture.attachFramebuffer(framebuffer, 1);
normalTexture.attachFramebuffer(framebuffer, 2);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
const renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, gridDataDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
ctx.state.currentRenderItemId = -1;
framebuffer.bind();
@@ -225,11 +228,11 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
*
* Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
*/
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
export function extractIsosurface(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, gridDataDim: Vec3, gridTexScale: Vec2, transform: Mat4, isoValue: number, invert: boolean, packedGroup: boolean, axisOrder: Vec3, constantGroup: boolean, vertexTexture?: Texture, groupTexture?: Texture, normalTexture?: Texture) {
if (isTimingMode) ctx.timer.mark('extractIsosurface');
const activeVoxelsTex = calcActiveVoxels(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
const compacted = createHistogramPyramid(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
const gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, gridDataDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
if (isTimingMode) ctx.timer.markEnd('extractIsosurface');
return gv;

View File

@@ -4,7 +4,8 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4 } from '../mol-math/linear-algebra';
import { Mat4 } from '../mol-math/linear-algebra/3d/mat4';
import { Vec3 } from '../mol-math/linear-algebra/3d/vec3';
export interface Object3D {
readonly view: Mat4

View File

@@ -7,12 +7,12 @@
import { Program } from './webgl/program';
import { RenderableValues, Values, RenderableSchema, BaseValues } from './renderable/schema';
import { GraphicsRenderItem, ComputeRenderItem, GraphicsRenderVariant, MultiDrawBaseData, Transparency } from './webgl/render-item';
import { ValueCell } from '../mol-util';
import { ValueCell } from '../mol-util/value-cell';
import { idFactory } from '../mol-util/id-factory';
import { clamp } from '../mol-math/interpolate';
import { Frustum3D } from '../mol-math/geometry/primitives/frustum3d';
import { Plane3D } from '../mol-math/geometry/primitives/plane3d';
import { Sphere3D } from '../mol-math/geometry';
import { Sphere3D } from '../mol-math/geometry/primitives/sphere3d';
import { Vec4 } from '../mol-math/linear-algebra/3d/vec4';
import { WebGLStats } from './webgl/context';
import { isTimingMode } from '../mol-util/debug';

View File

@@ -9,8 +9,7 @@ import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem, Transparency } from '../webgl/render-item';
import { AttributeSpec, Values, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, BaseSchema, UniformSpec, GlobalTextureSchema, GlobalDefineValues, GlobalDefines, GlobalDefineSchema } from './schema';
import { ImageShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
import { InterpolationTypeNames } from '../../mol-geo/geometry/image/image';
import { ValueCell } from '../../mol-util/value-cell';
export const ImageSchema = {
...BaseSchema,
@@ -33,7 +32,8 @@ export const ImageSchema = {
uIsoLevel: UniformSpec('f'),
dInterpolation: DefineSpec('string', InterpolationTypeNames),
/** Same as `InterpolationTypeNames` in '../../mol-geo/geometry/image/image' */
dInterpolation: DefineSpec('string', ['nearest', 'catmulrom', 'mitchell', 'bspline']),
};
export type ImageSchema = typeof ImageSchema
export type ImageValues = Values<ImageSchema>

View File

@@ -70,6 +70,7 @@ interface Scene extends Object3D {
readonly renderables: ReadonlyArray<GraphicsRenderable>
readonly boundingSphere: Sphere3D
readonly boundingSphereVisible: Sphere3D
readonly transparency: Transparency
readonly primitives: Scene.Group
readonly volumes: Scene.Group
@@ -383,6 +384,9 @@ namespace Scene {
}
return boundingSphereVisible;
},
get transparency() {
return transparency;
},
get markerAverage() {
if (markerAverageDirty) {
markerAverage = calculateMarkerAverage();

View File

@@ -22,6 +22,7 @@ uniform bool uInvert;
uniform vec3 uGridDim;
uniform vec3 uGridTexDim;
uniform vec3 uGridDataDim;
uniform mat4 uGridTransform;
uniform mat3 uGridTransformAdjoint;
@@ -93,20 +94,19 @@ vec4 baseVoxel(vec2 pos) {
}
vec4 getGroup(const in vec3 p) {
vec3 gridDim = uGridDim - vec3(1.0, 1.0, 0.0); // remove xy padding
// note that we swap x and z because the texture is flipped around y
#if defined(dAxisOrder_012)
float group = p.z + p.y * gridDim.z + p.x * gridDim.z * gridDim.y; // 210
float group = p.z + p.y * uGridDataDim.z + p.x * uGridDataDim.z * uGridDataDim.y; // 210
#elif defined(dAxisOrder_021)
float group = p.y + p.z * gridDim.y + p.x * gridDim.y * gridDim.z; // 120
float group = p.y + p.z * uGridDataDim.y + p.x * uGridDataDim.y * uGridDataDim.z; // 120
#elif defined(dAxisOrder_102)
float group = p.z + p.x * gridDim.z + p.y * gridDim.z * gridDim.x; // 201
float group = p.z + p.x * uGridDataDim.z + p.y * uGridDataDim.z * uGridDataDim.x; // 201
#elif defined(dAxisOrder_120)
float group = p.x + p.z * gridDim.x + p.y * gridDim.x * gridDim.z; // 021
float group = p.x + p.z * uGridDataDim.x + p.y * uGridDataDim.x * uGridDataDim.z; // 021
#elif defined(dAxisOrder_201)
float group = p.y + p.x * gridDim.y + p.z * gridDim.y * gridDim.x; // 102
float group = p.y + p.x * uGridDataDim.y + p.z * uGridDataDim.y * uGridDataDim.x; // 102
#elif defined(dAxisOrder_210)
float group = p.x + p.y * gridDim.x + p.z * gridDim.x * gridDim.y; // 012
float group = p.x + p.y * uGridDataDim.x + p.z * uGridDataDim.x * uGridDataDim.y; // 012
#endif
return vec4(group > 16777215.5 ? vec3(1.0) : packIntToRGB(group), 1.0);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -48,7 +48,7 @@ vec2 getDepthTransparentWithAlpha(const in vec2 coords) {
}
bool isBackground(const in float depth) {
return depth > 0.9999;
return depth == 1.0;
}
float getPixelSize(const in vec2 coords, const in float depth) {
@@ -108,8 +108,8 @@ void main(void) {
transparentOutlineFlag = 0.0;
bestTransparentAlpha = 0.0;
}
vec2 depthPacked; // Pack depth in G/B channels
vec2 depthPacked; // Pack depth in G/B channels
float outlineTypeFlag = 0.0;
if (opaqueOutlineFlag > 0.0 && transparentOutlineFlag > 0.0) {
outlineTypeFlag = 0.75; // Both
@@ -121,7 +121,7 @@ void main(void) {
outlineTypeFlag = 0.25; // Opaque only
depthPacked = packUnitIntervalToRG(bestOpaqueDepth);
}
float alpha = clamp(bestTransparentAlpha, 0.0, 0.5) * 2.0; // limiting to range [0.0, 0.5] to improve alpha precision since we don't need a wider range
float packedFlagWithAlpha = pack2x4(vec2(outlineTypeFlag, alpha)); // pack outlineType with alpha
gl_FragColor = vec4(packedFlagWithAlpha, depthPacked.x, depthPacked.y, bestTransparentDepth);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -54,13 +54,13 @@ float getDepthTransparent(const in vec2 coords) {
}
bool isBackground(const in float depth) {
return depth > 0.9999; // handle depth packing precision issues
return depth == 1.0;
}
int squaredOutlineScale = dOutlineScale * dOutlineScale;
void getOutline(const in vec2 coords, out bool hasOpaque, out bool hasTransparent, out float opaqueDepth, out float transparentDepth, out float alpha) {
vec2 invTexSize = 1.0 / uTexSize;
hasOpaque = false;
hasTransparent = false;
opaqueDepth = 1.0;
@@ -81,14 +81,14 @@ void getOutline(const in vec2 coords, out bool hasOpaque, out bool hasTransparen
float sampleFlag = sampleFlagWithAlpha.x;
float sampleAlpha = clamp(sampleFlagWithAlpha.y * 0.5, 0.01, 1.0);
if ((sampleFlag > 0.20 && sampleFlag < 0.30) || (sampleFlag > 0.70 && sampleFlag < 0.80)) { // transparent || both
if (sampleOpaqueDepth < opaqueDepth) {
hasOpaque = true;
opaqueDepth = sampleOpaqueDepth;
}
}
if ((((sampleFlag > 0.45 && sampleFlag < 0.55) || (sampleFlag > 0.70 && sampleFlag < 0.80))) && sampleTransparentDepth < transparentDepth) { // transparent || both
hasTransparent = true;
transparentDepth = sampleTransparentDepth;
@@ -184,15 +184,15 @@ void main(void) {
if (hasOpaque) {
float viewDist = abs(getViewZ(outlineOpaqueDepth));
float fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!uTransparentBackground) {
if (!uTransparentBackground) {
color.rgb = mix(uOutlineColor, uFogColor, fogFactor);
} else {
color.a = 1.0 - fogFactor;
color.rgb = mix(uOutlineColor, vec3(0.0), fogFactor);
}
}
}
#ifdef dBlendTransparency
#ifdef dBlendTransparency
if (hasTransparent) {
if (hasOpaque && outlineOpaqueDepth < outlineTransparentDepth) {
blendTransparency = false;

View File

@@ -1,9 +1,10 @@
/**
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Ludovic Autin <ludovic.autin@gmail.com>
* @author Gianluca Tomasello <giagitom@gmail.com>
*/
export const ssaoBlur_frag = `
@@ -36,8 +37,7 @@ float getViewZ(const in float depth) {
}
bool isBackground(const in float depth) {
// checking for 1.0 is not enough, because of precision issues
return depth >= 0.999;
return depth == 1.0;
}
bool isNearClip(const in float depth) {
@@ -78,8 +78,9 @@ void main(void) {
float sum = 0.0;
float kernelSum = 0.0;
int halfKernelSize = dOcclusionKernelSize / 2;
// only if kernelSize is odd
for (int i = -dOcclusionKernelSize / 2; i <= dOcclusionKernelSize / 2; i++) {
for (int i = -halfKernelSize; i <= halfKernelSize; i++) {
if (abs(float(i)) > 1.0 && abs(float(i)) * pixelSize > 0.8) continue;
vec2 sampleCoords = coords + float(i) * offset;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2024 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -64,7 +64,7 @@ vec2 getNoiseVec2(const in vec2 coords) {
}
bool isBackground(const in float depth) {
return depth > 0.999; // handle precision issues with packed depth
return depth == 1.0;
}
float getDepth(const in vec2 coords, const in int transparentFlag) {

View File

@@ -108,9 +108,9 @@ function resetValueChanges(valueChanges: ValueChanges) {
//
export type Transparency = 'blended' | 'wboit' | 'dpoit' | undefined
export type Transparency = 'blended' | 'wboit' | 'dpoit'
function getRenderVariant(variant: string, transparency: Transparency): string {
function getRenderVariant(variant: string, transparency: Transparency | undefined): string {
if (variant === 'color') {
switch (transparency) {
case 'blended': return 'colorBlended';
@@ -136,7 +136,7 @@ export function createComputeRenderItem(ctx: WebGLContext, drawMode: DrawMode, s
*
* - assumes that `values.drawCount` and `values.instanceCount` exist
*/
export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariants: T[], transparency: Transparency): RenderItem<T> {
export function createRenderItem<T extends string>(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, renderVariants: T[], transparency: Transparency | undefined): RenderItem<T> {
const id = getNextRenderItemId();
const { stats, state, resources } = ctx;
const { instancedArrays, vertexArrayObject, multiDrawInstancedBaseVertexBaseInstance, drawInstancedBaseVertexBaseInstance } = ctx.extensions;

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
*
* @author molstar/ciftools package
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
*
* @author molstar/ciftools package
*/

View File

@@ -40,7 +40,7 @@ export const CifCore_Schema = {
* in exceptional circumstances (Brock, 2025) may be reported as
* non-integral.
*
* Reference: Brock, C. P. (2025). Acta Cryst. A81, nnn-nnn.
* Reference: Brock, C. P. (2025). Acta Cryst. A81, 405-408.
*/
formula_units_z: float,
/**
@@ -336,7 +336,12 @@ export const CifCore_Schema = {
*/
site_symmetry_2: str,
/**
* Bond valence calculated from the bond distance.
* Valence assigned to the bond between the sites identified by
* _geom_bond.id calculated according to the bond-valence model
* (Brown, 2002) from the bond distance.
*
* Ref: Brown, I. D. (2002). The Chemical Bond in Inorganic Chemistry:
* the Bond-Valence Model, eq. (3.1). Oxford: Oxford University Press.
*/
valence: float,
},

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.406, IHM 1.28, MA 1.4.7.
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.408, IHM 1.28, MA 1.4.8.
*
* @author molstar/ciftools package
*/
@@ -5078,7 +5078,7 @@ export const mmCIF_Schema = {
/**
* The type of data held in the dataset.
*/
content_type: Aliased<'target' | 'template structure' | 'polymeric template library' | 'spatial restraints' | 'target-template alignment' | 'coevolution MSA' | 'model coordinates' | 'input structure' | 'reference database' | 'other'>(str),
content_type: Aliased<'target' | 'template structure' | 'polymeric template library' | 'spatial restraints' | 'target-template alignment' | 'coevolution MSA' | 'model coordinates' | 'input structure' | 'reference database' | 'intermediate backbone' | 'intermediate sequence' | 'model quality assessment scores' | 'energy estimate' | 'experimental validation' | 'other'>(str),
/**
* Details for other content types.
*/
@@ -5131,7 +5131,7 @@ export const mmCIF_Schema = {
/**
* The mode of calculation of the QA metric.
*/
mode: Aliased<'local' | 'global' | 'local-pairwise' | 'per-feature' | 'per-feature-pair'>(str),
mode: Aliased<'local' | 'global' | 'local-pairwise' | 'per-feature' | 'per-feature-pair' | 'dihedral'>(str),
/**
* Identifier to the set of software used to calculate the QA metric.
* This data item is a pointer to the _ma_software_group.group_id in the

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2025 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>
@@ -109,7 +109,7 @@ function eatEscaped(state: TokenizerState, esc: number) {
++state.position;
return;
default:
if (next === void 0) { // = "end of stream"
if (!Number.isFinite(next)) { // = "end of stream"
// get rid of the quotes.
state.tokenStart++;
state.tokenEnd = state.position;

View File

@@ -6,7 +6,8 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { chunkedSubtask, RuntimeContext } from '../../../../mol-task';
import { RuntimeContext } from '../../../../mol-task/execution/runtime-context';
import { chunkedSubtask } from '../../../../mol-task/util/chunked';
import { StringLike } from '../../../common/string-like';
export { Tokenizer };

View File

@@ -6,8 +6,8 @@
*/
import { GridLookup3D } from '../../geometry';
import { sortArray } from '../../../mol-data/util';
import { OrderedSet } from '../../../mol-data/int';
import { sortArray } from '../../../mol-data/util/sort';
import { OrderedSet } from '../../../mol-data/int/ordered-set';
import { getBoundary } from '../boundary';
const xs = [0, 0, 1];

View File

@@ -7,7 +7,7 @@
import { PositionData } from './common';
import { Vec3 } from '../linear-algebra';
import { OrderedSet } from '../../mol-data/int';
import { OrderedSet } from '../../mol-data/int/ordered-set';
import { BoundaryHelper } from './boundary-helper';
import { Box3D } from '../geometry/primitives/box3d';
import { Sphere3D } from '../geometry/primitives/sphere3d';

View File

@@ -5,7 +5,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../mol-math/linear-algebra/3d/vec3';
import { Vec3 } from '../linear-algebra/3d/vec3';
import { Sphere3D } from './primitives/sphere3d';
// avoiding namespace lookup improved performance in Chrome (Aug 2020)

View File

@@ -1,14 +1,15 @@
/**
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2025 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 { OrderedSet } from '../../mol-data/int';
import { Mat4, Tensor, Vec3, Vec2 } from '../linear-algebra';
import { OrderedSet } from '../../mol-data/int/ordered-set';
import { Mat4 } from '../linear-algebra/3d/mat4';
import { Vec3 } from '../linear-algebra/3d/vec3';
import { Tensor } from '../linear-algebra/tensor';
import { Box3D } from './primitives/box3d';
import { Texture } from '../../mol-gl/webgl/texture';
export interface PositionData {
x: ArrayLike<number>,
@@ -30,15 +31,6 @@ export type DensityData = {
maxRadius: number,
}
export type DensityTextureData = {
transform: Mat4,
texture: Texture,
bbox: Box3D,
gridDim: Vec3,
gridTexDim: Vec3
gridTexScale: Vec2
}
export interface RegularGrid3d {
box: Box3D,
dimensions: Vec3

View File

@@ -4,13 +4,16 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Box3D, DensityData, DensityTextureData } from '../geometry';
import { Box3D, DensityData } from '../geometry';
import { PositionData } from './common';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { Texture } from '../../mol-gl/webgl/texture';
import { GaussianDensityTexture2d, GaussianDensityTexture3d } from './gaussian-density/gpu';
import { Task } from '../../mol-task/task';
import { GaussianDensityCPU } from './gaussian-density/cpu';
import { Mat4 } from '../linear-algebra/3d/mat4';
import { Vec3 } from '../linear-algebra/3d/vec3';
import { Vec2 } from '../linear-algebra/3d/vec2';
export const DefaultGaussianDensityProps = {
resolution: 1,
@@ -27,7 +30,14 @@ export type GaussianDensityTextureData = {
radiusFactor: number
resolution: number
maxRadius: number
} & DensityTextureData
transform: Mat4,
texture: Texture,
bbox: Box3D,
gridDim: Vec3,
gridTexDim: Vec3
gridDataDim: Vec3
gridTexScale: Vec2
}
export function computeGaussianDensity(position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps) {
return Task.create('Gaussian Density', async ctx => {

View File

@@ -4,13 +4,15 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Box3D, fillGridDim } from '../../geometry';
import { Vec3, Mat4, Tensor } from '../../linear-algebra';
import { RuntimeContext } from '../../../mol-task';
import { PositionData } from '../common';
import { OrderedSet } from '../../../mol-data/int';
import { fillGridDim, PositionData } from '../common';
import { OrderedSet } from '../../../mol-data/int/ordered-set';
import { GaussianDensityProps, GaussianDensityData } from '../gaussian-density';
import { fasterExp } from '../../approx';
import { Box3D } from '../primitives/box3d';
import { Vec3 } from '../../linear-algebra/3d/vec3';
import { Tensor } from '../../linear-algebra/tensor';
import { Mat4 } from '../../linear-algebra/3d/mat4';
export async function GaussianDensityCPU(ctx: RuntimeContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps): Promise<GaussianDensityData> {
const { resolution, radiusOffset, smoothness } = props;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -99,8 +99,8 @@ export function GaussianDensityTexture3d(webgl: WebGLContext, position: Position
return finalizeGaussianDensityTexture(data);
}
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius }: _GaussianDensityTextureData): GaussianDensityTextureData {
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius };
function finalizeGaussianDensityTexture({ texture, scale, bbox, gridDim, gridTexDim, gridDataDim, gridTexScale, radiusFactor, resolution, maxRadius }: _GaussianDensityTextureData): GaussianDensityTextureData {
return { transform: getTransform(scale, bbox), texture, bbox, gridDim, gridTexDim, gridDataDim, gridTexScale, radiusFactor, resolution, maxRadius };
}
function getTransform(scale: Vec3, bbox: Box3D) {
@@ -118,6 +118,7 @@ type _GaussianDensityTextureData = {
bbox: Box3D,
gridDim: Vec3,
gridTexDim: Vec3
gridDataDim: Vec3
gridTexScale: Vec2
radiusFactor: number
resolution: number
@@ -206,7 +207,7 @@ function calcGaussianDensityTexture2d(webgl: WebGLContext, position: PositionDat
// printTextureImage(readTexture(webgl, minDistTex), { scale: 0.75 });
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridTexScale, radiusFactor, resolution, maxRadius };
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim, gridDataDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
}
function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionData, box: Box3D, radius: (index: number) => number, props: GaussianDensityProps, texture?: Texture): _GaussianDensityTextureData {
@@ -262,7 +263,7 @@ function calcGaussianDensityTexture3d(webgl: WebGLContext, position: PositionDat
setupGroupIdRendering(webgl, renderable);
render(texture, false);
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
return { texture, scale, bbox: expandedBox, gridDim: dim, gridTexDim: dim, gridDataDim: dim, gridTexScale, radiusFactor, resolution, maxRadius };
}
//

View File

@@ -7,15 +7,16 @@
* ported from NGL (https://github.com/arose/ngl), licensed under MIT
*/
import { Vec3, Tensor } from '../../mol-math/linear-algebra';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { RuntimeContext } from '../../mol-task';
import { OrderedSet } from '../../mol-data/int';
import { PositionData } from './common';
import { Mat4 } from '../../mol-math/linear-algebra/3d/mat4';
import { Box3D, GridLookup3D, fillGridDim } from '../../mol-math/geometry';
import { BaseGeometry } from '../../mol-geo/geometry/base';
import { OrderedSet } from '../../mol-data/int/ordered-set';
import { fillGridDim, PositionData } from './common';
import { Boundary } from './boundary';
import { GridLookup3D } from './lookup3d/grid';
import { Box3D } from './primitives/box3d';
import { Vec3 } from '../linear-algebra/3d/vec3';
import { Tensor } from '../linear-algebra/tensor';
import { Mat4 } from '../linear-algebra/3d/mat4';
function normalToLine(out: Vec3, p: Vec3) {
out[0] = out[1] = out[2] = 1.0;
@@ -48,8 +49,8 @@ function getAngleTables(probePositions: number): AnglesTables {
export const MolecularSurfaceCalculationParams = {
probeRadius: PD.Numeric(1.4, { min: 0, max: 10, step: 0.1 }, { description: 'Radius of the probe tracing the molecular surface.' }),
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }, { description: 'Grid resolution/cell spacing.', ...BaseGeometry.CustomQualityParamInfo }),
probePositions: PD.Numeric(36, { min: 12, max: 90, step: 1 }, { description: 'Number of positions tested for probe target intersection.', ...BaseGeometry.CustomQualityParamInfo }),
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }, { description: 'Grid resolution/cell spacing.' }),
probePositions: PD.Numeric(36, { min: 12, max: 90, step: 1 }, { description: 'Number of positions tested for probe target intersection.' }),
};
export const DefaultMolecularSurfaceCalculationProps = PD.getDefaultValues(MolecularSurfaceCalculationParams);
export type MolecularSurfaceCalculationProps = typeof DefaultMolecularSurfaceCalculationProps

View File

@@ -6,7 +6,7 @@
*/
import { PositionData } from '../common';
import { OrderedSet } from '../../../mol-data/int';
import { OrderedSet } from '../../../mol-data/int/ordered-set';
import { Sphere3D } from './sphere3d';
import { Vec3 } from '../../linear-algebra/3d/vec3';
import { Mat4 } from '../../linear-algebra/3d/mat4';

View File

@@ -5,13 +5,15 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4, EPSILON } from '../../linear-algebra';
import { PositionData } from '../common';
import { OrderedSet } from '../../../mol-data/int';
import { OrderedSet } from '../../../mol-data/int/ordered-set';
import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
import { Box3D } from './box3d';
import { Axes3D } from './axes3d';
import { PrincipalAxes } from '../../linear-algebra/matrix/principal-axes';
import { Vec3 } from '../../linear-algebra/3d/vec3';
import { Mat4 } from '../../linear-algebra/3d/mat4';
import { EPSILON } from '../../linear-algebra/3d/common';
interface Sphere3D {
center: Vec3,

View File

@@ -5,7 +5,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { lerp as scalar_lerp } from '../../mol-math/interpolate';
import { lerp as scalar_lerp } from '../interpolate';
import { Mat3 } from '../linear-algebra/3d/mat3';
import { Mat4 } from '../linear-algebra/3d/mat4';
import { Quat } from '../linear-algebra/3d/quat';

View File

@@ -151,7 +151,7 @@ namespace Mat3 {
}
export function hasNaN(m: Mat3) {
for (let i = 0; i < 9; i++) if (isNaN(m[i])) return true;
for (let i = 0; i < 9; i++) if (Number.isNaN(m[i])) return true;
return false;
}

View File

@@ -111,7 +111,7 @@ namespace Mat4 {
}
export function hasNaN(m: Mat4) {
for (let i = 0; i < 16; i++) if (isNaN(m[i])) return true;
for (let i = 0; i < 16; i++) if (Number.isNaN(m[i])) return true;
return false;
}

View File

@@ -7,9 +7,9 @@
import { Mat4 } from './mat4';
import { Vec3 } from './vec3';
import { EVD } from '../matrix/evd';
import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
import { Matrix } from '../matrix/matrix';
import { Sphere3D } from '../../geometry/primitives/sphere3d';
import { CentroidHelper } from '../../geometry/centroid-helper';
export { MinimizeRmsd };
namespace MinimizeRmsd {

View File

@@ -59,7 +59,7 @@ namespace Quat {
}
export function hasNaN(q: Quat) {
return isNaN(q[0]) || isNaN(q[1]) || isNaN(q[2]) || isNaN(q[3]);
return Number.isNaN(q[0]) || Number.isNaN(q[1]) || Number.isNaN(q[2]) || Number.isNaN(q[3]);
}
export function create(x: number, y: number, z: number, w: number) {

View File

@@ -55,7 +55,7 @@ namespace Vec2 {
}
export function hasNaN(a: Vec2) {
return isNaN(a[0]) || isNaN(a[1]);
return Number.isNaN(a[0]) || Number.isNaN(a[1]);
}
export function toArray<T extends NumberArray>(a: Vec2, out: T, offset: number) {

View File

@@ -52,8 +52,12 @@ export namespace Vec3 {
return _isFinite(a[0]) && _isFinite(a[1]) && _isFinite(a[2]);
}
export function isInteger(a: Vec3): boolean {
return Number.isInteger(a[0]) && Number.isInteger(a[1]) && Number.isInteger(a[2]);
}
export function hasNaN(a: Vec3) {
return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]);
return Number.isNaN(a[0]) || Number.isNaN(a[1]) || Number.isNaN(a[2]);
}
export function setNaN(out: Vec3) {
@@ -573,8 +577,8 @@ export namespace Vec3 {
const a0 = a[0], a1 = a[1], a2 = a[2];
const b0 = b[0], b1 = b[1], b2 = b[2];
return (Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) &&
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) &&
Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)));
}
const rotTemp = zero();
@@ -620,8 +624,28 @@ export namespace Vec3 {
}
/** Get a vector that is similar to `b` but orthogonal to `a` */
export function orthogonalize(out: Vec3, a: Vec3, b: Vec3) {
return normalize(out, cross(out, cross(out, a, b), a));
export function orthogonalize(out: Vec3, a: Vec3, b: Vec3): Vec3 {
// Regular case (`b` not parallel to `a`)
normalize(out, cross(out, cross(out, a, b), a));
if (!Vec3.isZero(out)) return out;
// `b` was parallel to `a`, try orthogonalize(a, X)
out[0] = 1; out[1] = 0; out[2] = 0;
normalize(out, cross(out, cross(out, a, out), a));
if (!Vec3.isZero(out)) return out;
// `X` was parallel to `a`, try orthogonalize(a, Y)
out[0] = 0; out[1] = 1; out[2] = 0;
normalize(out, cross(out, cross(out, a, out), a));
if (!Vec3.isZero(out)) return out;
// `a` was zero, return normalized `b`
normalize(out, b);
if (!Vec3.isZero(out)) return out;
// `b` was zero, return whatever
out[0] = 1; out[1] = 0; out[2] = 0;
return out;
}
/**

View File

@@ -19,9 +19,10 @@
import { Mat4 } from './mat4';
import { NumberArray } from '../../../mol-util/type-helpers';
import { Sphere3D } from '../../geometry/primitives/sphere3d';
import { EPSILON } from './common';
type SphereLike = { center: number[], radius: number };
interface Vec4 extends Array<number> { [d: number]: number, '@type': 'vec4', length: 4 }
function Vec4() {
@@ -54,7 +55,7 @@ namespace Vec4 {
return out;
}
export function fromSphere(out: Vec4, sphere: Sphere3D) {
export function fromSphere(out: Vec4, sphere: SphereLike) {
out[0] = sphere.center[0];
out[1] = sphere.center[1];
out[2] = sphere.center[2];
@@ -62,12 +63,12 @@ namespace Vec4 {
return out;
}
export function ofSphere(sphere: Sphere3D) {
export function ofSphere(sphere: SphereLike) {
return fromSphere(zero(), sphere);
}
export function hasNaN(a: Vec4) {
return isNaN(a[0]) || isNaN(a[1]) || isNaN(a[2]) || isNaN(a[3]);
return Number.isNaN(a[0]) || Number.isNaN(a[1]) || Number.isNaN(a[2]) || Number.isNaN(a[3]);
}
export function toArray<T extends NumberArray>(a: Vec4, out: T, offset: number) {

View File

@@ -1,4 +1,3 @@
import { Mat3 } from './3d/mat3';
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
@@ -9,6 +8,7 @@ import { Mat3 } from './3d/mat3';
import { Mat4 } from './3d/mat4';
import { Vec3 } from './3d/vec3';
import { Vec4 } from './3d/vec4';
import { Mat3 } from './3d/mat3';
export interface Tensor { data: Tensor.Data, space: Tensor.Space }

View File

@@ -6,7 +6,6 @@
* @author Yana Rose <yana.v.rose@gmail.com>
*/
import { substringStartsWith } from '../../../mol-util/string';
import { CifCategory, CifField, CifFrame } from '../../../mol-io/reader/cif';
import { Tokenizer } from '../../../mol-io/reader/common/text/tokenizer';
import { PdbFile } from '../../../mol-io/reader/pdb/schema';
@@ -23,6 +22,16 @@ import { parseConect } from './conect';
import { isDebugMode } from '../../../mol-util/debug';
import { PdbHeaderData, addHeader } from './header';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { StringLike } from '../../../mol-io/common/string-like';
function substringStartsWith(str: StringLike, start: number, end: number, target: string) {
const len = target.length;
if (len > end - start) return false;
for (let i = 0; i < len; i++) {
if (str.charCodeAt(start + i) !== target.charCodeAt(i)) return false;
}
return true;
}
export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
const { lines } = pdb;

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Volume } from '../../mol-model/volume';
import { Grid, Volume } from '../../mol-model/volume';
import { Task } from '../../mol-task';
import { SpacegroupCell, Box3D } from '../../mol-math/geometry';
import { Mat4, Tensor, Vec3 } from '../../mol-math/linear-algebra';
@@ -71,19 +71,22 @@ export function volumeFromCcp4(source: Ccp4File, params?: { voxelSize?: Vec3, of
// always calculate stats when all stats related values are zero
const calcStats = header.AMIN === 0 && header.AMAX === 0 && header.AMEAN === 0 && header.ARMS === 0;
const volgrid: Grid = {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3(), origin_frac, dimensions_frac)) },
cells: data,
stats: {
min: (Number.isNaN(header.AMIN) || calcStats) ? arrayMin(values) : header.AMIN,
max: (Number.isNaN(header.AMAX) || calcStats) ? arrayMax(values) : header.AMAX,
mean: (Number.isNaN(header.AMEAN) || calcStats) ? arrayMean(values) : header.AMEAN,
sigma: (Number.isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
},
};
return {
label: params?.label,
entryId: params?.entryId,
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
cells: data,
stats: {
min: (isNaN(header.AMIN) || calcStats) ? arrayMin(values) : header.AMIN,
max: (isNaN(header.AMAX) || calcStats) ? arrayMax(values) : header.AMAX,
mean: (isNaN(header.AMEAN) || calcStats) ? arrayMean(values) : header.AMEAN,
sigma: (isNaN(header.ARMS) || header.ARMS === 0) ? arrayRms(values) : header.ARMS
},
},
periodicity: Vec3.isInteger(dimensions_frac) ? 'xyz' : 'none',
grid: volgrid,
instances: [{ transform: Mat4.identity() }],
sourceData: Ccp4Format.create(source),
customProperties: new CustomProperties(),

View File

@@ -2,6 +2,7 @@
* Copyright (c) 2018-2025 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 { DensityServer_Data_Database } from '../../mol-io/reader/cif/schema/density-server';
@@ -38,6 +39,7 @@ export function volumeFromDensityServerData(source: DensityServer_Data_Database,
return {
label: params?.label,
entryId: params?.entryId,
periodicity: Vec3.isInteger(dimensions) ? 'xyz' : 'none',
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)) },
cells: data,

View File

@@ -36,6 +36,7 @@ export function volumeFromDsn6(source: Dsn6File, params?: { voxelSize?: Vec3, la
return {
label: params?.label,
entryId: params?.entryId,
periodicity: Vec3.isInteger(dimensions_frac) ? 'xyz' : 'none',
grid: {
transform: { kind: 'spacegroup', cell, fractionalBox: Box3D.create(origin_frac, Vec3.add(Vec3.zero(), origin_frac, dimensions_frac)) },
cells: data,

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