mirror of
https://github.com/molstar/molstar.git
synced 2026-06-07 07:04:22 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ecdc0eafa | ||
|
|
dccfd35c7a | ||
|
|
9e81a4f7a6 | ||
|
|
6f6cc73ce9 | ||
|
|
c248ae11bf | ||
|
|
742be03901 | ||
|
|
00009ef198 | ||
|
|
1cb617524d | ||
|
|
e2e348240b | ||
|
|
b54908492c | ||
|
|
33172862bd | ||
|
|
c5f2767efc | ||
|
|
66f5a81a5d | ||
|
|
9e90e11bfc | ||
|
|
ab372a89d6 | ||
|
|
c6506d515f | ||
|
|
7d0f84ff72 | ||
|
|
31495ab02a | ||
|
|
853ad5c916 | ||
|
|
51fc525215 | ||
|
|
6f9fed180d | ||
|
|
5ecd176f20 |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -5,7 +5,27 @@ Note that since we don't clearly distinguish between a public and private interf
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v5.1.0] - 2025-10-25
|
||||
## [v5.3.0] - 2025-11-5
|
||||
- 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 +33,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
|
||||
|
||||
BIN
examples/trajectory/protein.dcd
Normal file
BIN
examples/trajectory/protein.dcd
Normal file
Binary file not shown.
BIN
examples/trajectory/protein.nc
Normal file
BIN
examples/trajectory/protein.nc
Normal file
Binary file not shown.
264
examples/trajectory/protein.parm7
Normal file
264
examples/trajectory/protein.parm7
Normal 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
|
||||
26
examples/trajectory/protein.pdb
Normal file
26
examples/trajectory/protein.pdb
Normal 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
|
||||
14
examples/trajectory/protein.rst7
Normal file
14
examples/trajectory/protein.rst7
Normal 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
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/argparse": "^2.0.17",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "5.1.2",
|
||||
"version": "5.3.0",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> <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> <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> <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> <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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
8
src/extensions/mvs/index.ts
Normal file
8
src/extensions/mvs/index.ts
Normal 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';
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,23 @@ 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);
|
||||
return applyPrimitiveVisuals(data, refs, clip);
|
||||
},
|
||||
primitives_from_uri(updateParent: UpdateTarget, tree: MolstarNode<'primitives_from_uri'>, context: MolstarLoadingContext): UpdateTarget {
|
||||
const data = UpdateTarget.apply(updateParent, MVSDownloadPrimitiveData, { uri: tree.params.uri, format: tree.params.format });
|
||||
return applyPrimitiveVisuals(data, new Set(tree.params.references));
|
||||
const clip = clippingForNode(tree);
|
||||
return applyPrimitiveVisuals(data, new Set(tree.params.references), 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;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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)
|
||||
),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -573,8 +573,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 +620,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1259,8 +1259,8 @@ export function normalizeWheel(event: any) {
|
||||
}
|
||||
|
||||
// Fall-back if spin cannot be determined
|
||||
if (dx && !spinX) { spinX = (dx < 1) ? -1 : 1; }
|
||||
if (dy && !spinY) { spinY = (dy < 1) ? -1 : 1; }
|
||||
if (dx && !spinX) { spinX = (dx < 0) ? -1 : 1; }
|
||||
if (dy && !spinY) { spinY = (dy < 0) ? -1 : 1; }
|
||||
|
||||
return { spinX, spinY, dx, dy, dz };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user