Compare commits

...

611 Commits

Author SHA1 Message Date
Alexander Rose
1203be103e 0.4.5 2019-11-25 16:30:48 -08:00
Alexander Rose
87b3f32dfa fix ajaxGet: handle case where download had finished before callback were registered 2019-11-25 16:29:02 -08:00
Alexander Rose
d10be352be 0.4.4 2019-11-21 16:23:10 -08:00
Alexander Rose
6f3d8ddd96 fix computeInterUnitBonds 2019-11-21 16:22:14 -08:00
Alexander Rose
1a14e65dcf 0.4.3 2019-11-21 15:26:23 -08:00
Alexander Rose
2757dfdb75 add sleep util 2019-11-21 15:23:34 -08:00
Alexander Rose
1b5526235e make Canvas3D.add/.remove awaitable 2019-11-21 15:23:21 -08:00
David Sehnal
d4836c5fde Merge pull request #21 from molstar/fix-bond-compute
fix struct_conn use for bond computation
2019-11-21 18:29:06 +01:00
Alexander Rose
530810e366 fix struct_conn use for bond computation 2019-11-20 15:35:11 -08:00
Alexander Rose
9f2a4828f4 improve default color-list handling 2019-11-20 15:13:45 -08:00
Alexander Rose
70cc3a612e stats: count single element unit as unit not element 2019-11-20 14:33:03 -08:00
Alexander Rose
6917ba6230 don't treat peptide terminus components as polymers
they don't neccesarily have the right atoms to draw a polymer cartoon
2019-11-20 14:18:35 -08:00
Alexander Rose
710c06672e fix, selection mark not applyed correctly on structure change 2019-11-20 09:05:46 -08:00
Alexander Rose
5d9e14aad1 0.4.2 2019-11-19 15:29:19 -08:00
Alexander Rose
9963cb3770 prevent browser scrolling with middle click (FF on win) 2019-11-19 09:02:20 -08:00
Alexander Rose
5731ef20b3 0.4.1 2019-11-18 16:14:41 -08:00
Alexander Rose
ab89d34440 package updates 2019-11-18 16:13:43 -08:00
Alexander Rose
d9bac2916c fix polymer sequence widget .getLoci
was not taking operator.name into account
2019-11-18 15:21:42 -08:00
Alexander Rose
edaa9610f6 camera focus takes granularity into account 2019-11-18 14:39:32 -08:00
Alexander Rose
90255b1a1b moved loci normalization/granularity to model/loci 2019-11-18 14:38:55 -08:00
Alexander Rose
639f137ad2 fix PrincipalAxes 2019-11-18 12:08:53 -08:00
Alexander Rose
29609e04c2 improved structure docs and sequence description 2019-11-18 11:43:51 -08:00
Alexander Rose
736aaf4433 matrix and axes improvements 2019-11-18 11:43:10 -08:00
Alexander Rose
2d7248962f add oriented-box visual to structure-orientation 2019-11-17 19:02:44 -08:00
Alexander Rose
a13c98c36c fix StructureElementSelectionManager.getPrincipalAxes for multiple entries 2019-11-17 17:32:37 -08:00
Alexander Rose
e298499326 improved handling of Structure.Loci, added whole structure support to Stats 2019-11-17 17:27:57 -08:00
Alexander Rose
6e1e463e1d fix whole structure selection/highlighting 2019-11-17 16:45:16 -08:00
Alexander Rose
2a2261beaf principal axes for loci and orientation for Camera.focus 2019-11-17 16:10:28 -08:00
Alexander Rose
14623f5df2 vec3 improvements, .matchDirection & readonly .negUnit 2019-11-17 16:03:48 -08:00
Alexander Rose
32889779a2 ModelUnitcell cleanup 2019-11-16 19:22:06 -08:00
Alexander Rose
911a7e3636 wip, structure orientation representation 2019-11-16 19:21:37 -08:00
Alexander Rose
bcf8ae2734 wip, principal component refactoring 2019-11-16 19:20:40 -08:00
Alexander Rose
92df3b1f42 console.log 2019-11-16 16:11:53 -08:00
Alexander Rose
5bbed1f9a3 remove console.log statement 2019-11-16 16:10:46 -08:00
Alexander Rose
0a391760b8 fix stats.firstResidueLoc assignment 2019-11-16 15:42:12 -08:00
Alexander Rose
e7eeb8cb55 center/focus with left+alt 2019-11-16 14:46:06 -08:00
Alexander Rose
bee4f38209 0.4.0 2019-11-15 16:35:56 -08:00
Alexander Rose
ff4dcfc220 package updates 2019-11-15 16:34:13 -08:00
Alexander Rose
ad726a97e4 filter display list of select queries, add bonded query 2019-11-15 16:12:49 -08:00
Alexander Rose
b533542792 check if pointer actually moved 2019-11-15 15:15:09 -08:00
Alexander Rose
dbe33cc046 twekaed mouse bindings
left: toggle selection
middle: center
right: show surrounding
2019-11-15 15:06:59 -08:00
Alexander Rose
3ddf5f4bec tweaked StructureRepresentationInteraction visual
make look less like ball+stick default
2019-11-15 15:04:46 -08:00
Alexander Rose
f02d49f181 typo 2019-11-15 15:01:57 -08:00
Alexander Rose
4df68697f2 update CommonSaccharideNames 2019-11-15 15:01:36 -08:00
Alexander Rose
eeb09d9184 allow partial params in .createParams helper 2019-11-15 15:01:17 -08:00
David Sehnal
d8509a145b mol-model: fixed Loci stats 2019-11-15 21:01:38 +01:00
David Sehnal
e3cb799638 mol-model: fix Loci.extentTo* 2019-11-15 20:49:59 +01:00
David Sehnal
ce9469c85b mol-model: Fixed Loci.isSubset 2019-11-15 20:33:08 +01:00
David Sehnal
e62b166805 mol-model: fix Loci Stats.add 2019-11-15 20:12:47 +01:00
David Sehnal
a1f283c641 Merge branch 'master' of https://github.com/molstar/molstar 2019-11-15 20:05:49 +01:00
David Sehnal
2b14c398a8 mol-model: optimized Loci.extendToResidues/Chains 2019-11-15 20:05:44 +01:00
Alexander Rose
d8480d058b don't apply granularity for State Highlight 2019-11-15 09:51:51 -08:00
David Sehnal
b1ac1a36d4 mol-model: extendToWholeChains now works on partitioned chains 2019-11-15 16:27:58 +01:00
David Sehnal
3c2d014ad3 mol-model: count chains in Loci stats 2019-11-15 14:39:17 +01:00
David Sehnal
d940d26312 mol-model: added Unit.Traits (replaces multiChain prop) 2019-11-15 13:23:01 +01:00
Alexander Rose
99028b5c99 use unit.chainGroupId in sequence widget 2019-11-14 18:03:26 -08:00
Alexander Rose
ac94f0659d add unit.multiChain; tweaks to unit.chainGroupId 2019-11-14 18:03:03 -08:00
Alexander Rose
bd85a5a153 sequence widget help icon 2019-11-14 14:57:21 -08:00
Alexander Rose
9aa375a45f handle conformations in selections, marking, labels 2019-11-14 14:16:26 -08:00
David Sehnal
999cb99eb8 mol-data: fixed bug in isSubsetIS 2019-11-14 06:09:30 +01:00
Alexander Rose
12b53bc4bb added failing unittest for isSubsetIS 2019-11-13 18:21:20 -08:00
Alexander Rose
952f3c451f package updates, removed now superfluous @types/webgl2 2019-11-13 13:49:50 -08:00
David Sehnal
ce06375a92 mol-plugin: sequence view remove scrollTo behavior 2019-11-13 16:19:59 +01:00
Alexander Rose
c0f60d48bb tweaked label/description for custom volume param.isRelative 2019-11-12 17:12:54 -08:00
Alexander Rose
e40f08d467 update packages 2019-11-12 17:00:44 -08:00
Alexander Rose
9c977bf90f add ColorSwatch and use instead of ColorNames for color dropdown 2019-11-12 17:00:30 -08:00
Alexander Rose
525f32feab use elementLabel() for sequence widget dropdown 2019-11-12 16:04:13 -08:00
Alexander Rose
cdb698f0d1 added stripTags string helper 2019-11-12 16:03:38 -08:00
Alexander Rose
c65d09f4a8 use previous SequenceView.state when structure hasn't changed 2019-11-12 15:13:13 -08:00
Alexander Rose
7d4e48431c add e.stopPropagation(); to onKeyPress methods in controls 2019-11-12 14:42:36 -08:00
Alexander Rose
6a7268a9c9 improved sequenceNumberPeriod for short sequences 2019-11-12 12:03:30 -08:00
Alexander Rose
eea1137abc prefer EMDB map for 'ELECTRON CRYSTALLOGRAPHY' entries 2019-11-12 11:44:46 -08:00
Alexander Rose
a130121698 fix extra layout(location = X) definitions in shaders 2019-11-12 10:39:46 -08:00
Alexander Rose
153f039ee6 added number-parser spec 2019-11-08 21:23:02 -05:00
Alexander Rose
b7216d28f4 wip, cif-core support 2019-11-08 21:12:01 -05:00
David Sehnal
ee34139094 added setMolStarDebugMode 2019-11-07 17:23:42 +01:00
David Sehnal
9addea79b7 updated packages 2019-11-07 16:56:58 +01:00
Alexander Rose
2c88ace91c added number precision helpers, improved isovalue step calculation 2019-11-05 06:14:35 +03:00
David Sehnal
6eae95f964 mol-plugin: scroll first highlighted/selected element into view 2019-11-02 15:48:20 +01:00
David Sehnal
b44ed7c45a mol-theme: lociLabel tweaks 2019-11-02 15:33:14 +01:00
David Sehnal
b2d2546e39 mol-model: added chainGroupId 2019-11-02 14:58:29 +01:00
David Sehnal
4dbabdf350 mol-plugin: fix SubstructureParentHelper 2019-11-02 13:13:31 +01:00
David Sehnal
df63d8fe0f mol-plugin: support HTML tags in labels, tweaks to default label provider 2019-11-02 12:15:17 +01:00
David Sehnal
c9116575fa mol-model: support isConnectedTo query disjunct and invert 2019-11-02 11:34:46 +01:00
David Sehnal
6707673c7f mol-model: fixes to link related queries 2019-11-02 11:30:02 +01:00
Alexander Rose
1f82446843 0.3.12 2019-11-01 13:43:17 -07:00
Alexander Rose
9942b0e389 avoid extra selection in cellpack loader 2019-11-01 13:41:39 -07:00
Alexander Rose
f57e8501c4 fix order in LociMarkManager.normalizedLoci 2019-11-01 13:25:14 -07:00
Alexander Rose
1e9fa003db don't apply granularity in StructureSelectionControls 2019-11-01 11:29:58 -07:00
Alexander Rose
7868b22918 added disulfide-bridges StructureSelectionQuery 2019-11-01 11:15:40 -07:00
Alexander Rose
e2c80956dc structureElementStatsLabel tweaks 2019-11-01 11:12:48 -07:00
David Sehnal
5d5af58bdf mol-model: removed QueryContextLinkInfo.swap 2019-11-01 16:47:23 +01:00
David Sehnal
8f78106816 mol-data: fix SortedArray.union & mol-model: fix includeConnected 2019-11-01 15:43:04 +01:00
David Sehnal
1923289c51 mol-plugin: better element loci highlight label 2019-11-01 14:55:37 +01:00
David Sehnal
14c22147d5 mol-script: added linkedAtomicPairs generator + fixed assembly-symmetry-axes isApplicable for empty structures 2019-11-01 14:39:15 +01:00
David Sehnal
6d39bc4a20 mol-script: added more link props, support different element reference for atomic props 2019-11-01 13:46:40 +01:00
Alexander Rose
cfaecb590d fix StructureRepresentationInteractionBehavior bundle handling 2019-10-31 19:05:51 -07:00
Alexander Rose
2894c18ba1 0.3.11 2019-10-31 17:13:11 -07:00
Alexander Rose
e6633c2fc6 optimized Bundle.toStructure 2019-10-31 17:10:47 -07:00
Alexander Rose
fe341f0851 fix Bundle.toStructure, #19 2019-10-31 16:55:04 -07:00
Alexander Rose
34a387d4bd optimize StructureRepresentationInteractionBehavior by using bundle 2019-10-31 12:35:49 -07:00
Alexander Rose
f975d9c8dc add names anon molql functions for debugging/profiling 2019-10-31 12:31:25 -07:00
Alexander Rose
563ff98c83 fix MolScript.internal.generator.bundle binding 2019-10-31 12:29:02 -07:00
Alexander Rose
0b9ecb76b9 tweaked ball&stick default size 2019-10-31 10:27:24 -07:00
Alexander Rose
b8de1de3a8 don't calc sec-struc for archival files 2019-10-31 10:22:05 -07:00
Alexander Rose
ac2f48684e updated cif schemas with more meta data fields 2019-10-31 10:18:43 -07:00
Alexander Rose
9928b17a76 fix, carb preset missing connected 2019-10-31 09:44:06 -07:00
Alexander Rose
723a6a1fbd gracefully handle errors when getting em map info 2019-10-30 18:05:44 -07:00
Alexander Rose
51ac75943d query preset tweaks 2019-10-30 17:37:10 -07:00
Alexander Rose
6bcb5eac71 adjust 'auto' quality 2019-10-30 16:30:36 -07:00
Alexander Rose
f2f1e355c2 molql, fix expandConnected 2019-10-30 16:30:08 -07:00
Alexander Rose
4cc0754f11 add isNonStandard atom prop, use polymer repr preset 2019-10-30 15:35:18 -07:00
Alexander Rose
cd30caa12d better align negative sequence numbers in ui 2019-10-30 10:48:19 -07:00
Alexander Rose
41bc74e543 add support for Beta and Gamma peptides 2019-10-30 10:26:35 -07:00
David Sehnal
96a908698d mol-data: fix Column.view if underlying __array is of different type than value 2019-10-30 12:21:29 +01:00
Alexander Rose
77a561024f removed unneccessary z-index 2019-10-29 16:24:47 -07:00
Alexander Rose
5d0176e686 use landscape for reactive layout when width > 1000 2019-10-29 15:41:23 -07:00
David Sehnal
3a6013145e mol-task: fixed subtask cancellation 2019-10-29 21:42:26 +01:00
David Sehnal
c5e2471f34 mol-state/task: use console.error only when not in production mode 2019-10-29 20:37:29 +01:00
Alexander Rose
e9802f2191 0.3.10 2019-10-29 12:07:55 -07:00
Alexander Rose
2d0097fddc package updates 2019-10-29 12:07:02 -07:00
Alexander Rose
1a245db4c9 tweaked bindings formating 2019-10-29 11:56:38 -07:00
Alexander Rose
6a694f9298 wip, tweak tasks css 2019-10-28 17:25:11 -07:00
Alexander Rose
acd1f43018 0.3.9 2019-10-28 16:57:41 -07:00
Alexander Rose
71ed9a2db9 fix extractCrossLinkRestraints 2019-10-28 16:51:21 -07:00
David Sehnal
2b5d9bd213 Merge branch 'master' of https://github.com/molstar/molstar 2019-10-28 22:13:30 +01:00
David Sehnal
32c8664829 mol-plugin: fix transform update UI when error happens 2019-10-28 22:13:10 +01:00
David Sehnal
26e6e0ab72 Task cancellation fix 2019-10-28 22:03:10 +01:00
Alexander Rose
6d9c9bd884 use model.entryId(s) for image filename when possible 2019-10-28 12:07:30 -07:00
David Sehnal
2a74d5eb94 mol-state: added revertible update (and used it in DownloadStrucutre action) 2019-10-28 16:23:41 +01:00
David Sehnal
04d39e0e8d support Task cancellation in plugin UI 2019-10-28 16:01:13 +01:00
David Sehnal
c5e64b39db mol-model-formats: mmCIF substitute undefined atom_site.label_* with auth_* columns (and vice versa) 2019-10-27 11:53:37 +01:00
Alexander Rose
4911585a85 fix seq number in sequence widget for atomic models without auth_seq_id 2019-10-25 18:26:08 -07:00
Alexander Rose
590c00114f 0.3.8 2019-10-25 15:48:20 -07:00
Alexander Rose
111eded34a disable some ui elements when state tree is busy 2019-10-25 12:18:46 -07:00
Alexander Rose
d767900fb1 fix, canvas.render not always returning true when it did render 2019-10-25 12:06:14 -07:00
Alexander Rose
9074d5390a add support for sequence numbers in coarse units 2019-10-24 16:28:06 -07:00
Alexander Rose
91027459ca typos 2019-10-24 15:02:23 -07:00
Alexander Rose
a2eda6c5af added controls display options: outside, portrait, landscape, reactive 2019-10-24 11:32:00 -07:00
Alexander Rose
338489febd 0.3.7 2019-10-24 07:54:31 -07:00
David Sehnal
94e7820baf mol-data: SortedArray fix 2019-10-24 07:40:35 +02:00
Alexander Rose
a47df57709 more SortedArray.union tests (one failing) 2019-10-23 19:32:41 -07:00
Alexander Rose
f73d64f732 0.3.6 2019-10-23 18:15:46 -07:00
Alexander Rose
cfaef4c567 added more volume param docs 2019-10-23 18:14:16 -07:00
Alexander Rose
3ec2c6ded4 ui layout tweaks 2019-10-23 17:18:16 -07:00
Alexander Rose
2afaa35170 increased default wireframe thickness from 1 to 1.5 2019-10-23 17:17:34 -07:00
Alexander Rose
efcf4a77c6 add molecular surface wireframe visual 2019-10-23 17:16:40 -07:00
Alexander Rose
221e1de4e7 improved entity-source color theme 2019-10-23 12:25:59 -07:00
Alexander Rose
f2de2983c8 added isInteger helper 2019-10-23 12:25:38 -07:00
Alexander Rose
82c8499789 updated cif schemas 2019-10-23 11:43:19 -07:00
David Sehnal
d66ccdb255 mol-plugin: sequence view tweaks 2019-10-23 09:27:59 +02:00
David Sehnal
8d13c514b0 mol-data: SortedArray fix 2019-10-23 09:17:25 +02:00
Alexander Rose
f57aafba19 improved sequence numbers in sequence widget UI 2019-10-22 17:25:57 -07:00
Alexander Rose
0a7406db15 added more SortedArray.union tests, one failing 2019-10-22 15:17:18 -07:00
Alexander Rose
2f97e8b329 overset residue numbers in sequence ui 2019-10-22 12:24:53 -07:00
David Sehnal
22a8758852 mol-data: fixed SortedArray.union 2019-10-22 20:48:16 +02:00
Alexander Rose
bc6bc1d57a fix StructureElement.Stats 2019-10-22 10:22:02 -07:00
Alexander Rose
4a692b9a88 ensure applicable repr types are up-to-date 2019-10-22 10:20:55 -07:00
Alexander Rose
0094f800dc fixed entity-source color theme labels 2019-10-21 18:18:26 -07:00
Alexander Rose
9bd616f36f ensure file extension for image download 2019-10-21 17:59:09 -07:00
David Sehnal
4acc36628d mol-plugin: show sequence offsets in UI (wip) 2019-10-21 15:06:58 +02:00
David Sehnal
e550413778 mol-state: added createDefaultParams to StateAction and StateTransformer 2019-10-21 13:45:18 +02:00
David Sehnal
ccdc894979 ability to set default params for volume streaming 2019-10-21 13:39:05 +02:00
David Sehnal
322bc71d52 changed default color list to dark-2 to avoid a clash with EM volume map colors 2019-10-21 12:49:04 +02:00
Alexander Rose
7c55e4d234 0.3.5 2019-10-18 17:02:14 -07:00
Alexander Rose
8c8058290c fixed spacegroup index/number handling 2019-10-18 16:58:53 -07:00
David Sehnal
9d413bf0eb mol-util: palette fix missing valueLabel function 2019-10-18 15:29:45 +02:00
Alexander Rose
8fde5dd1bf 0.3.4 2019-10-17 11:47:10 -07:00
Alexander Rose
2f9ecb9396 package updates 2019-10-17 11:46:26 -07:00
Alexander Rose
85092279fa better handle unsupported extensions 2019-10-17 11:40:32 -07:00
Alexander Rose
d0189523e4 fix Loci stats for partial residue selections 2019-10-17 11:10:10 -07:00
Alexander Rose
a26d03205a tweak bindings to work better with one-button mouse 2019-10-16 18:26:22 -07:00
Alexander Rose
862eda6dd4 better min size limit in image ui 2019-10-16 17:54:46 -07:00
Alexander Rose
4a7f0fc206 hide non-applicable repr types in repr ui 2019-10-16 17:46:27 -07:00
Alexander Rose
6bbee58d39 guard against undefined values in ParameterControls 2019-10-16 16:42:14 -07:00
Alexander Rose
989faed410 added StructureSelectionQuery wrapping expr & query 2019-10-16 16:30:38 -07:00
Alexander Rose
198e2f2043 support for currentSelection in StructureSelectionHelper and surroundings query 2019-10-16 15:48:39 -07:00
Alexander Rose
6ae3d4110d added StructureElement.Loci.toStructure 2019-10-16 15:47:57 -07:00
David Sehnal
c34e1be43b Merge branch 'master' of https://github.com/molstar/molstar 2019-10-16 20:46:40 +02:00
David Sehnal
17a440ad9c moved StructureQuery.runExpr to Script.getStructureSelection to fix cyclic dep 2019-10-16 20:46:25 +02:00
Alexander Rose
04dcfc7adb check seqId compatibility for cyclic peptides 2019-10-16 11:01:07 -07:00
David Sehnal
ca66e334c1 mol-model: added StructureQuery.runExpr 2019-10-16 13:47:06 +02:00
David Sehnal
54bba4c92f mol-script: added internal.generator.current symbol 2019-10-16 13:38:58 +02:00
David Sehnal
9c046b808c mol-script: added Bundle support 2019-10-16 13:26:05 +02:00
David Sehnal
ca5d1865cc mol-model: Loci fixes 2019-10-16 12:48:21 +02:00
Alexander Rose
d8f1aa5bc9 support for streaming multiple em volumes, limit to associate maps 2019-10-15 17:38:42 -07:00
David Sehnal
6ac790e083 mol-model: renamed StructureSelection.toLoci* 2019-10-15 08:49:06 +02:00
David Sehnal
d0870e4bbb added highlight example to basic wrapper, better empty loci handling 2019-10-15 08:44:50 +02:00
Alexander Rose
5138595f36 added more StructureSelectionQueries 2019-10-14 16:26:54 -07:00
Alexander Rose
d9aa5684a9 show non-polymer residues of polymer entities as ball&stick 2019-10-14 14:09:34 -07:00
Alexander Rose
8e2521a7a9 consider coarse/non-coarse backbone when checking distance for backbone links 2019-10-14 14:09:12 -07:00
Alexander Rose
7dd7a117cb wip, terminal gaps 2019-10-11 16:55:39 -07:00
Alexander Rose
defbadf4d7 handle polymer ends in visuals properly 2019-10-11 16:55:22 -07:00
Alexander Rose
131e88080a 0.3.3 2019-10-10 10:43:52 -07:00
Alexander Rose
fb78b886c1 collapse link labels to hide repeated ids 2019-10-10 10:41:56 -07:00
Alexander Rose
aed0b87b16 fix fractional canvas/image dimensions 2019-10-10 10:35:44 -07:00
Alexander Rose
1316cc6a40 fix StructureSelectionControls.focus for single element 2019-10-09 17:52:59 -07:00
Alexander Rose
944d370c14 0.3.2 2019-10-09 16:59:24 -07:00
Alexander Rose
74f9aa6af6 package updates 2019-10-09 16:55:44 -07:00
Alexander Rose
c20c9c9917 fixed setting CollapsableControls default state 2019-10-09 16:54:15 -07:00
Alexander Rose
4801435d72 add controls to create image 2019-10-09 16:10:36 -07:00
Alexander Rose
33fd105ef7 add ImagePass 2019-10-09 16:09:43 -07:00
Alexander Rose
3ea3fb8984 support rendering with transparent background 2019-10-09 16:08:48 -07:00
Alexander Rose
b4bbc544ca 0.3.1 2019-10-07 17:27:35 -07:00
Alexander Rose
5f880e920b add focus button to StructureSelectionControls 2019-10-07 17:26:16 -07:00
Alexander Rose
bcce801dd7 ensure sequence markers are up-to-date 2019-10-07 16:10:21 -07:00
Alexander Rose
00f9dcee4a added StructureSymmetryMatesFromModel transform, fixes for findMatesRadius 2019-10-07 15:31:54 -07:00
Alexander Rose
505af2bc96 fix help for scrollFocus 2019-10-07 12:04:20 -07:00
Alexander Rose
c217aab5fc reduced default unicell cage thickness 2019-10-07 11:23:50 -07:00
Alexander Rose
5d5fd0028f added operator name & hkl color themes 2019-10-07 11:20:10 -07:00
Alexander Rose
c88693dfdd improved color theme legend with labels from data 2019-10-07 11:18:08 -07:00
David Sehnal
0a16ec1bd2 mol-plugin: added TextInput wrapper and RGB color input 2019-10-05 14:52:00 +02:00
Alexander Rose
6f36a3c724 0.3.0 2019-10-04 17:21:16 -07:00
Alexander Rose
71496bd1ef package updates 2019-10-04 17:20:38 -07:00
Alexander Rose
c5a99a7c12 improved lociApply for whole structures 2019-10-04 17:07:08 -07:00
Alexander Rose
cfaf33d696 also look for 4 and 7 member sugar rings 2019-10-04 15:55:47 -07:00
Alexander Rose
e24c76d2bf update modifierkeys on mouse input 2019-10-04 15:40:14 -07:00
Alexander Rose
b6273205a2 removed duplicate type 2019-10-04 15:18:33 -07:00
Alexander Rose
b38193aa19 for unknown carbs draw polygonal prisms 2019-10-04 15:13:00 -07:00
Alexander Rose
f9cfacae23 sequence widget, bind onMouseLeave 2019-10-04 12:26:05 -07:00
Alexander Rose
ac46317dc6 fix lighting for orthographic projection close to camera 2019-10-04 11:53:38 -07:00
Alexander Rose
2b492a5a61 lighter monospace font 2019-10-04 10:44:08 -07:00
Alexander Rose
a2133657f0 chnaged label Unit->Chain in sequence widget 2019-10-04 10:42:07 -07:00
David Sehnal
e8de45789f mol-plugin: optimized sequence control 2019-10-04 16:52:36 +02:00
David Sehnal
3d2bd167ca mol-plugin: use monospace font for sequence 2019-10-04 15:34:59 +02:00
David Sehnal
504c8626dc mol-plugin: fixed PluginCommands.State.Highlight (wip) 2019-10-04 15:22:26 +02:00
Alexander Rose
f4b29dc7e0 ui, offset expanded color param 2019-10-03 17:24:24 -07:00
Alexander Rose
b34c5c743b tweaked ligandPlusConnected selection 2019-10-03 16:40:44 -07:00
Alexander Rose
c57311d6c0 carbohydrate improvements, updated carb table 2019-10-03 16:04:32 -07:00
Alexander Rose
4d786dc697 fix missing awaits in StructureRepresentationHelper.preset 2019-10-03 11:50:18 -07:00
Alexander Rose
062e3e055a show full compId for modified residues in sequence widget 2019-10-03 10:05:31 -07:00
Alexander Rose
1465174a45 Merge branch 'master' of https://github.com/molstar/molstar 2019-10-02 17:57:38 -07:00
Alexander Rose
cc00ada5a3 add model Unitcell representation 2019-10-02 17:57:19 -07:00
Alexander Rose
cbf312b62d calculate symmetry operators for transform around deposited coordinates 2019-10-02 17:56:41 -07:00
Alexander Rose
2be3144086 add .volume to SpacegroupCell 2019-10-02 17:55:03 -07:00
Alexander Rose
da3acd9d19 calculate model center as dynamic prop 2019-10-02 17:53:51 -07:00
Alexander Rose
34b048479b added arrayMinMax util 2019-10-02 17:53:03 -07:00
Alexander Rose
ca92931bf2 mol-geo improvements for mesh, cage and primitive 2019-10-02 17:52:48 -07:00
David Sehnal
0d3daeb823 mol-plugin: semi-transparent viewport controls background 2019-10-02 13:11:50 +02:00
David Sehnal
58b1d7e0eb mol-model & formats: added fallback when chem_comp category is missing 2019-10-02 12:46:23 +02:00
Alexander Rose
c5997ed056 reset buttons and modifier keys state when browser window looses focus 2019-10-01 14:11:01 -07:00
Alexander Rose
da1deee7f3 switched off marker interpolation for webgl2 2019-10-01 10:58:26 -07:00
Alexander Rose
a4eaff3175 add background to canvas icons for legibility with different render background colors 2019-10-01 10:30:44 -07:00
Alexander Rose
211cfc0bd3 fix highlight persisting after rotation 2019-10-01 09:52:18 -07:00
David Sehnal
c0f85b691d mol-plugin: reverted msp-layout-region background !important, because it broke the visual style 2019-10-01 17:42:42 +02:00
David Sehnal
6b93d58ea6 mol-plugin: updated color select control 2019-10-01 17:33:49 +02:00
Alexander Rose
5bce423b49 fully mark carbohydrate terminal links from StructureElement.Loci 2019-09-30 17:38:05 -07:00
Alexander Rose
01b0dde503 set plugin Context.customState: unknown 2019-09-30 16:38:55 -07:00
Alexander Rose
ed1bc8cb7d ensure single element loci have some volume in volume interaction behavior 2019-09-30 15:52:55 -07:00
Alexander Rose
abe559261b show insertion code in labels 2019-09-30 14:36:42 -07:00
Alexander Rose
b0cdf22cb8 various interaction behaviors improvements 2019-09-30 14:26:59 -07:00
Alexander Rose
a61ba71f1e handle mixed sizes in link visuals 2019-09-30 12:11:07 -07:00
Alexander Rose
7061d57559 tweak some bond distance thresholds 2019-09-30 11:06:49 -07:00
Alexander Rose
83a1e6c87c improved entity subtype assignment (use chem comp type) 2019-09-30 11:06:19 -07:00
Alexander Rose
c18888b8de 0.2.15 2019-09-27 17:19:57 -07:00
Alexander Rose
2d80935e00 ignore non-identity 'given' NCS operators 2019-09-27 17:18:14 -07:00
Alexander Rose
4287d158b6 add Mat3.isIdentity 2019-09-27 17:18:06 -07:00
Alexander Rose
4e9b569178 show logo in empty viewer 2019-09-27 16:48:10 -07:00
Alexander Rose
5d626d291b added viewer favicon 2019-09-27 16:48:00 -07:00
Alexander Rose
afa4a01c44 package updates 2019-09-27 16:41:53 -07:00
Alexander Rose
3c4a23c5a3 ensure msp-layout-region background 2019-09-27 15:22:27 -07:00
Alexander Rose
8d92c976d9 css 'select' tweaks 2019-09-27 13:19:01 -07:00
Alexander Rose
1a9adfad29 0.2.14 2019-09-27 11:17:53 -07:00
Alexander Rose
a9e9a5974d fix volumeserver cmd arg types 2019-09-27 11:10:11 -07:00
Alexander Rose
27160aa8fe fix stale array use 2019-09-26 15:33:52 -07:00
David Sehnal
6a71af00cf mol-plugin: Toast support 2019-09-26 16:18:57 +02:00
David Sehnal
6c0938db50 mol-plugin: added InitVolumeStreaming binding param 2019-09-26 15:11:13 +02:00
Alexander Rose
b76173c82f 0.2.13 2019-09-25 17:25:25 -07:00
Alexander Rose
0bcf2b1ff4 fix wrong style.overflow assignment 2019-09-25 16:07:25 -07:00
Alexander Rose
d074415a26 added more param docs 2019-09-25 11:55:00 -07:00
Alexander Rose
42f3e38026 0.2.12 2019-09-24 14:21:58 -07:00
Alexander Rose
74c16ee7ba package updates 2019-09-24 14:19:44 -07:00
Alexander Rose
f6ef22b917 added link cylinder to ellopsoids repr 2019-09-24 14:16:19 -07:00
Alexander Rose
c3f3d7efda check if atomistic before query modified 2019-09-24 12:20:38 -07:00
Alexander Rose
61d44efdc4 fix typo 2019-09-24 12:12:19 -07:00
Alexander Rose
ad200c86ec Merge branch 'master' of https://github.com/molstar/molstar 2019-09-24 12:10:54 -07:00
Alexander Rose
4aecf4e0b4 added ElementSequenceWrapper & ChainSequenceWrapper 2019-09-24 12:09:54 -07:00
Alexander Rose
d81f37c78b added AtomicUnit.residueCount 2019-09-24 12:07:26 -07:00
Alexander Rose
c2979ce5ab StructureSequence, handle empty coarse hierarchies 2019-09-24 12:06:55 -07:00
Alexander Rose
09c7edce88 smaller terminal links and dashed terminal links to metals 2019-09-24 10:11:42 -07:00
David Sehnal
beefb79258 mol-plugin: better default Structure Complex, extended StructureComplexElement transform 2019-09-24 15:17:05 +02:00
David Sehnal
529c6ac81c mol-plugin: fix SelectionFromScript & volume streaming bugs 2019-09-24 14:07:42 +02:00
Alexander Rose
84b988ea96 fix csv parser choking on newline at end of file 2019-09-23 16:46:35 -07:00
Alexander Rose
35e978efc9 added Table.toArrays 2019-09-23 16:45:58 -07:00
David Sehnal
19559d01f7 mol-state: call canAutoUpdate with correct parameters 2019-09-23 16:53:31 +02:00
David Sehnal
56cec343e2 Proteopedia wrapper fix 2019-09-23 16:12:40 +02:00
David Sehnal
20c9bd0130 mol-plugin: CSS fix 2019-09-23 15:56:48 +02:00
David Sehnal
1336997c58 mol-plugin: volume streaming support for LinkLoci, update "current box" when switching to surroundings, init behavior fix 2019-09-23 15:47:55 +02:00
David Sehnal
b178fdefdc mol-plugin: Camera focus duration default value fix 2019-09-23 14:59:44 +02:00
Alexander Rose
453d60060a ui, print length of sequences that are too long 2019-09-22 19:48:59 -07:00
Alexander Rose
901fac97a0 basic support for .3dg files 2019-09-22 19:44:37 -07:00
Alexander Rose
29b6a88343 check for Performance object capabilities 2019-09-22 19:43:13 -07:00
Alexander Rose
a2217c7fc6 fix csv parser chunking 2019-09-22 19:42:31 -07:00
Alexander Rose
3eec30aa42 sequence improvements, create sequence from coarse elements 2019-09-22 18:29:46 -07:00
Alexander Rose
f352e19e90 config for debugging 2019-09-22 09:32:19 -07:00
Alexander Rose
be65ef89bc 0.2.11 2019-09-20 17:23:25 -07:00
Alexander Rose
cfc46073c0 package updates 2019-09-20 17:22:39 -07:00
Alexander Rose
1819a2bbda add wwpdb provider for emdb contourLevel 2019-09-20 17:14:53 -07:00
Alexander Rose
aa3a42f94e added simple xml parser 2019-09-20 17:13:13 -07:00
Alexander Rose
4bff55b612 improve theme applicability checks 2019-09-20 15:08:55 -07:00
Alexander Rose
dbc4e09909 add 'disorder' to uncertainty theme label/description 2019-09-20 14:46:46 -07:00
Alexander Rose
0a074c8a66 add modelNum to location labels 2019-09-20 11:10:40 -07:00
Alexander Rose
8b314ebb75 add border to .msp-layout-standard 2019-09-20 10:20:12 -07:00
Alexander Rose
4dc0791aeb tweaked slider rail/handle color 2019-09-20 10:06:59 -07:00
Alexander Rose
e3483f11b1 adjusted maxCovalentHydrogenBondingLength 2019-09-20 09:32:45 -07:00
Alexander Rose
244a678ab7 fix Props.residue.label_comp_id for micro het 2019-09-19 19:11:57 -07:00
Alexander Rose
8e1d44fabc fixes, seq wrapper mark every loci, binding docs & cleanup 2019-09-19 18:33:44 -07:00
Alexander Rose
954a5b58e0 tweaked structure and volume surroundings behaviors 2019-09-19 18:10:07 -07:00
Alexander Rose
53223bc72b support EveryLoci in StructureRepresentation.mark 2019-09-19 17:10:28 -07:00
Alexander Rose
a11a1fa07e formating 2019-09-19 17:09:26 -07:00
Alexander Rose
9ab4001544 get Behavior defaultParams from transformer params 2019-09-19 17:09:15 -07:00
Alexander Rose
3e3b71d230 added Interval.ofLength 2019-09-19 17:08:36 -07:00
Alexander Rose
186929269b refactored bindings and interactivity 2019-09-19 17:08:20 -07:00
Alexander Rose
afa7a04af0 various ui tweaks, added CollapsableControls 2019-09-19 17:03:06 -07:00
Alexander Rose
2861d12f04 inline help for params, color theme legend ui 2019-09-16 16:47:58 -07:00
Alexander Rose
65e1212b2f wip, focusZoom action 2019-09-13 16:30:55 -07:00
Alexander Rose
47136c8b71 wip, focus input bindings 2019-09-13 12:57:46 -07:00
Alexander Rose
375b829562 wip, simplified camera class 2019-09-13 10:53:56 -07:00
Alexander Rose
2718d42b01 wip, input bindings, radius-based clipping 2019-09-12 16:47:19 -07:00
Alexander Rose
93d4118c0a Merge branch 'master' of https://github.com/molstar/molstar 2019-09-12 09:37:31 -07:00
David Sehnal
3a2a47af12 removed unused comments 2019-09-12 16:09:47 +02:00
David Sehnal
0eacdfca85 added transition duration effect to camera focus 2019-09-12 16:07:48 +02:00
David Sehnal
a7388be25f mol-canvas3d: fixed camera focus 2019-09-12 15:34:27 +02:00
Alexander Rose
5fcdcb1275 webpack.config cleanup, package updates 2019-09-11 17:57:54 -07:00
Alexander Rose
4635bdffb0 0.2.10 2019-09-11 16:11:33 -07:00
Alexander Rose
72b1c36111 basic microheterogeneity support 2019-09-11 15:50:57 -07:00
Alexander Rose
fc5ff601a8 typed generic (Column<T>) return of windowColumn 2019-09-11 15:49:13 -07:00
Alexander Rose
8dd142fc9f fixed ellipsoids with three identical eigenvalues (render sphere) 2019-09-11 10:35:14 -07:00
Alexander Rose
97f24293e2 0.2.9 2019-09-10 16:52:30 -07:00
Alexander Rose
f4beba5215 pdb-parser, aniso records 2019-09-10 16:49:10 -07:00
Alexander Rose
e2abe0f52a pdb-parser, atom-site fixes 2019-09-10 16:48:36 -07:00
Alexander Rose
5d18643374 pdb-parser, move atom-site methods to separate file 2019-09-10 15:50:04 -07:00
Alexander Rose
769f2a30c2 package updates 2019-09-10 15:43:00 -07:00
Alexander Rose
0e040d7744 added occupancy color theme 2019-09-10 15:31:57 -07:00
Alexander Rose
7600d0a44e add ellipsoid repr showing mmcif thermal displacement 2019-09-10 15:10:58 -07:00
Alexander Rose
9113d6d189 only show applicable repr types in ui 2019-09-10 15:10:13 -07:00
Alexander Rose
fd9dac86b9 combined duplicate chem_comp_bond props 2019-09-10 15:09:33 -07:00
Alexander Rose
fe1f3bd4bb use structure.uniqueResidues, removed duplicated code 2019-09-10 11:12:00 -07:00
Alexander Rose
d950718110 linalg: return out array for .toArray, create symmetric mat3 2019-09-10 10:50:34 -07:00
Alexander Rose
4be903b9d5 added atom_site_anisotrop to mmcif schema 2019-09-09 17:35:16 -07:00
Alexander Rose
8050644869 mat3 symmetricEigenvalues and eigenvectors 2019-09-09 17:32:45 -07:00
Alexander Rose
347c1986df require output object for Tensor.to* methods 2019-09-09 17:32:02 -07:00
David Sehnal
eac4a8988b mol-model: added Model.entryId; mol-plugin: toggle validation tooltip; fixed using correct entryId for various custom properties 2019-09-05 16:09:05 +02:00
Alexander Rose
25137f29d2 typescript 3.6 compat fixes 2019-09-04 17:26:42 -07:00
Alexander Rose
4601fe74bb packaage updates 2019-09-04 17:26:21 -07:00
Alexander Rose
f0b54e9cbf 0.2.8 2019-09-04 12:18:43 -07:00
Alexander Rose
fdb45c3624 improved canvas3d.clear 2019-09-04 12:17:22 -07:00
Alexander Rose
f3b1ec0ed8 Merge branch 'master' of https://github.com/molstar/molstar 2019-09-04 12:05:42 -07:00
Alexander Rose
79510614b9 add .uniqueElementCount and .polymerUnitCount to Structure 2019-09-04 12:04:58 -07:00
Alexander Rose
f8c9bc6812 add volume-server npm start script 2019-09-04 12:03:44 -07:00
Alexander Rose
85d35aab90 handle special CCP4 spacegroup numbers 2019-09-04 12:03:14 -07:00
David Sehnal
f5b09dbd10 mol-plugin: added customState to PluginContext 2019-09-03 14:42:59 +02:00
Alexander Rose
57ea322fd6 cellpack loader tweak 2019-08-30 19:09:53 -07:00
Alexander Rose
c31aab5594 wip, cellpack loader 2019-08-30 18:56:30 -07:00
Alexander Rose
a5556e8c41 imroved sequence widget entity dropdown 2019-08-30 18:55:46 -07:00
Alexander Rose
0ce2966c47 lazy calculation of Structure.polymerResidueCount 2019-08-30 18:54:54 -07:00
Alexander Rose
f2c04a13af adjust grid-cell-count in partitionAtomicUnitByResidue 2019-08-30 17:28:33 -07:00
Alexander Rose
60ba0de219 CifField creation optimizations 2019-08-30 17:27:58 -07:00
Alexander Rose
99e515604e skip identical positions in spheres and lines bounding sphere calculation 2019-08-30 17:26:52 -07:00
Alexander Rose
84dfa60fc2 added CifCategory.ofTable and 'list' valueType support for CifField.ofColumn 2019-08-29 17:42:30 -07:00
Alexander Rose
a753095f92 added ImportString and ImportJson transforms 2019-08-29 17:31:44 -07:00
David Sehnal
ce0ff2fed8 mol-model: optionally compute centroid based bounding sphere (disabled for now) 2019-08-29 15:46:47 +02:00
Alexander Rose
1c1deb5ee7 0.2.7 2019-08-28 17:48:22 -07:00
Alexander Rose
e28749b794 overpaint improvements, merge and filter 2019-08-28 17:34:41 -07:00
Alexander Rose
7566cc89ca added transperancy-from-bundle, improved overpaint 2019-08-28 15:29:17 -07:00
Alexander Rose
8b76c09559 added Script namespace with helpers 2019-08-28 15:28:35 -07:00
Alexander Rose
c60d7d3faf removed console statements 2019-08-28 12:49:33 -07:00
Alexander Rose
3eb3d1e27e re-apply select-mark to all representation of an updated structure 2019-08-28 12:43:23 -07:00
Alexander Rose
3083c7d9e0 overpaint improvements 2019-08-28 12:04:47 -07:00
Alexander Rose
9184219976 StructureElement loci & bundle fixes 2019-08-28 12:03:26 -07:00
Alexander Rose
90a3d302f3 loci helper methods, isEmpty 2019-08-28 12:00:39 -07:00
Alexander Rose
02dd5f9d11 fix, set new source in StructureRepresentation3D.update 2019-08-27 17:05:10 -07:00
Alexander Rose
3685c92b52 add missing info in ObjectList param 2019-08-27 17:04:23 -07:00
Alexander Rose
e1a04c8b0b renamed StructureSelection-related transforms 2019-08-27 14:51:10 -07:00
Alexander Rose
5a704b7974 renamed StructureElement.Query to StructureElement.Bundle 2019-08-27 14:29:10 -07:00
Alexander Rose
2376d0a9c6 split up StructureElement 2019-08-27 13:54:00 -07:00
Alexander Rose
87e0c05ec0 tweaked cellpack loader: baseUrl, binary membrane file 2019-08-27 09:54:27 -07:00
Alexander Rose
8140b75086 fixed polymer/chain-id color theme for coarse models 2019-08-27 09:53:38 -07:00
Alexander Rose
1f32d5469e ignoreHydrogens in cellpack presets 2019-08-26 17:30:53 -07:00
Alexander Rose
b09549439f added StructureElement.Stats and improved loci-labels 2019-08-26 17:14:32 -07:00
Alexander Rose
e1b7a5b267 wip, structure tools, repr presets 2019-08-26 17:13:28 -07:00
Alexander Rose
1ed420aeb7 added entity.subtype based on _entity_poly.type and _pdbx_entity_branch.type 2019-08-26 17:10:12 -07:00
Alexander Rose
301a23b1e0 refactored cellpack curve to resuse Vec3 objects 2019-08-25 21:42:07 -07:00
Alexander Rose
e53bb51f15 removed superfluous gl.viewport call 2019-08-25 21:05:18 -07:00
Alexander Rose
4e6e8fed82 add rna in hiv cellpack models 2019-08-25 21:04:40 -07:00
Alexander Rose
b2809ad631 handle curves/fibers in mesoscale models 2019-08-24 16:32:49 -07:00
Alexander Rose
37648b41ea added uInteriorDarkening factor to renderer 2019-08-23 22:28:27 -07:00
Alexander Rose
fac183c2ee allow partial reprParams in StructureRepresentation3DHelpers.createParams 2019-08-23 22:27:20 -07:00
Alexander Rose
b113448e01 added 'serve' npm script 2019-08-23 22:26:25 -07:00
Alexander Rose
c585ba7791 color theme and list tweaks, added turbo 2019-08-23 12:23:56 -07:00
Alexander Rose
afb6c65c48 fix sequence widget update on state changes 2019-08-23 12:23:15 -07:00
Alexander Rose
527e91c9eb fix Structure.unitIndexMap creation when units are unsorted 2019-08-23 12:22:15 -07:00
Alexander Rose
9e2ef87611 don't use Mesh.uniformTriangleGroup when WebGL2 is available 2019-08-22 16:17:22 -07:00
Alexander Rose
f2342c3273 added structure gaussian surface complex-visual 2019-08-22 12:07:07 -07:00
Alexander Rose
347981792e added Structure.serialMapping 2019-08-22 12:04:05 -07:00
Alexander Rose
032d2f2784 removed fillUniform, use Array.fill 2019-08-22 08:44:32 -07:00
Alexander Rose
ddc1119a80 0.2.6 2019-08-21 13:38:22 -07:00
Alexander Rose
0fe6774f04 fixed comparison and typo 2019-08-21 13:37:04 -07:00
Alexander Rose
2a71e44ae8 updated packages 2019-08-21 13:36:37 -07:00
Alexander Rose
1257f1ce85 label tweak 2019-08-21 12:17:05 -07:00
Alexander Rose
07cd9f4b16 css fix 2019-08-21 12:09:19 -07:00
Alexander Rose
ed5ff1c9ce slider ui tweaks 2019-08-21 10:13:42 -07:00
Alexander Rose
7ffc2db76e fix trackball controls dropping inputs 2019-08-21 09:42:30 -07:00
Alexander Rose
03067ca6d6 wip, structure tools quality option 2019-08-20 16:39:32 -07:00
Alexander Rose
a69f1337d7 color refactoring 2019-08-20 12:51:27 -07:00
Alexander Rose
5e83c3350a added Structure.root 2019-08-20 12:51:16 -07:00
Alexander Rose
f051d2d01e use model.entry in volume-streaming 2019-08-20 12:48:59 -07:00
Alexander Rose
257340283b wip, color list refactoring 2019-08-20 09:53:34 -07:00
Alexander Rose
e769d77ec8 wip, color theme refactoring 2019-08-19 18:50:46 -07:00
Alexander Rose
1923535918 limit max number of generated colors 2019-08-19 12:44:34 -07:00
Alexander Rose
dd3fc5620b label fix 2019-08-19 12:43:52 -07:00
Alexander Rose
7165258431 sequence widget fixes 2019-08-19 12:43:31 -07:00
Alexander Rose
df9b367e0b tweaked structure labeling 2019-08-19 12:43:08 -07:00
Alexander Rose
ed1ae71f71 optimized repr loci iteration, marking and extendToWholeChains 2019-08-18 13:01:31 -07:00
Alexander Rose
1c58bca454 fix wrong props object in Structure creation 2019-08-18 12:58:01 -07:00
Alexander Rose
c01be0644e improved handling of polymer trace termini 2019-08-16 20:01:20 -07:00
Alexander Rose
66b9f6104c improved low-poly geo (cylinder, sheet, tube, ribbon) 2019-08-16 17:14:57 -07:00
Alexander Rose
af4d2c4003 Merge branch 'master' of https://github.com/molstar/molstar 2019-08-15 17:18:02 -07:00
Alexander Rose
3b1a2f19a4 wip, structure tools, repr presets 2019-08-15 17:15:50 -07:00
Alexander Rose
6b874786a8 only extract CrossLinkRestraints when needed 2019-08-15 16:07:58 -07:00
Alexander Rose
f9d2560468 fixed symmetry operator when NCS operators are present 2019-08-15 16:07:18 -07:00
David Sehnal
961034584a model-server: config can be (partially) provided as a separate JSON file 2019-08-15 17:22:23 +02:00
David Sehnal
f6d11a59a3 model-server: wip config refactoring 2019-08-15 17:11:55 +02:00
Alexander Rose
53ee758378 wip, sequence widget 2019-08-14 17:21:53 -07:00
Alexander Rose
c80c630810 param control tweaks 2019-08-14 17:21:40 -07:00
Alexander Rose
13cd6e82ba added StructureFromTrajectory, improved multi-model structures 2019-08-14 17:20:40 -07:00
David Sehnal
f2966032d9 model-server: rest api, swagger ui, response schemas, bug fixes 2019-08-14 15:04:07 +02:00
David Sehnal
76503b52f5 wip model-server 2019-08-14 14:21:13 +02:00
Alexander Rose
aa24be8e9b type fixes 2019-08-13 16:44:19 -07:00
Alexander Rose
5d7bb894d4 wip, structure tools 2019-08-13 16:18:44 -07:00
Alexander Rose
fcf559fa6b toggle hydrogens in structure view tools 2019-08-13 15:55:32 -07:00
Alexander Rose
27963b5aed added option to ignore hydrogens in structure representations 2019-08-13 14:54:38 -07:00
Alexander Rose
40d539c4aa viewport ui improvements: mouse handling, config 2019-08-13 11:42:42 -07:00
David Sehnal
d1433aaf7b mol-state: added StateTree.subtreeHasRef 2019-08-13 17:19:36 +02:00
Alexander Rose
8f4bf9a314 added outsideControls to PluginLayoutStateParams 2019-08-12 17:30:27 -07:00
Alexander Rose
cca0f407f6 improved StructureRepresentationHelper 2019-08-12 16:47:09 -07:00
Alexander Rose
1e9e41754a added isApplicable support to representation provider & registry 2019-08-12 16:46:48 -07:00
David Sehnal
38dc2d6e26 mol-script: macro support 2019-08-12 16:00:53 +02:00
David Sehnal
ea46b1c8c8 mol-script: bond* => link*; make link tests "covalent" by default; wip link tests 2019-08-12 15:29:50 +02:00
Alexander Rose
5846d7c4b4 wip, structure view tools 2019-08-09 18:40:07 -07:00
Alexander Rose
214176ce2e handle incompatibility of StructureElement.Query and Structure 2019-08-09 18:18:32 -07:00
Alexander Rose
ee4cb214e1 updated cif schemas 2019-08-09 18:16:58 -07:00
Alexander Rose
01cca9e8a6 use .componentDidUpdate in SliderBase 2019-08-09 11:08:53 -07:00
Alexander Rose
7a23493e19 package updates 2019-08-09 10:26:41 -07:00
Alexander Rose
e109f069a8 wip, structure ui tools 2019-08-08 17:46:23 -07:00
Alexander Rose
82e667e402 added chemCompType and other mol-script improvements 2019-08-08 17:16:47 -07:00
Alexander Rose
c28feb2d1c added objectPrimitive to unit and mol-script 2019-08-08 13:56:21 -07:00
Alexander Rose
8447a2d4f2 add missing alias for cbrt 2019-08-08 13:54:20 -07:00
Alexander Rose
f9ebf1c399 better renderer defaults (occlusian radius, cmaera dist clipping) 2019-08-08 13:53:49 -07:00
Alexander Rose
286b27720a StructureElement fixes 2019-08-07 17:04:10 -07:00
Alexander Rose
dc0e54c275 Merge branch 'master' of https://github.com/molstar/molstar 2019-08-07 08:37:26 -07:00
David Sehnal
b710291d5e mol-plugin: animation controls tweak 2019-08-07 12:15:36 +02:00
David Sehnal
d84d5f38f5 mol-model: implemented includeConnected query 2019-08-07 12:10:24 +02:00
Alexander Rose
9bea13438f improved picking pass 2019-08-06 12:50:45 -07:00
Alexander Rose
2fc28f6005 improved StructureElement.Query 2019-08-06 11:16:50 -07:00
Alexander Rose
bb07d6ec56 imroved download helper and state download 2019-08-06 10:02:49 -07:00
Alexander Rose
3cdfd04048 improved element loci remap, added SortedArray.indexOfInRange 2019-08-06 08:38:36 -07:00
Alexander Rose
2120a258f9 remap loci per repr not per visual 2019-08-06 08:30:21 -07:00
Alexander Rose
2b5e49d215 use LociStructureSelection in plugin structure tools 2019-08-05 15:09:16 -07:00
Alexander Rose
b88bf9bdf2 LociStructureSelection based on StructureElement.Query 2019-08-05 15:08:43 -07:00
Alexander Rose
df0f15d132 added StructureElement.Query for Loci serialization 2019-08-05 15:08:36 -07:00
Alexander Rose
a6319bfb3d tweakes to avoid cyclic import issues 2019-08-05 12:32:01 -07:00
Alexander Rose
d6278cb3eb sorted-ranges, .areEqual and .forEach 2019-08-05 12:30:53 -07:00
Alexander Rose
560da38687 fixed exceptBy and structureSubtract 2019-08-03 14:55:55 -07:00
Alexander Rose
83ba9d8776 avoid .apply for long arg lists 2019-08-02 21:53:59 -07:00
Alexander Rose
ee776e6e3e wip, structure tools 2019-08-02 17:18:07 -07:00
Alexander Rose
3e52496b4e sequence widget, chain -> unit 2019-08-02 17:17:21 -07:00
Alexander Rose
2659b96008 added math.cbrt to mol-script 2019-08-02 17:16:47 -07:00
Alexander Rose
a8be84701b improved screendoor transparency with multi-sample 2019-08-02 10:23:16 -07:00
Alexander Rose
0c79aa1709 show number of selected elements and structures 2019-08-01 16:09:33 -07:00
Alexander Rose
e57a19857f improved pdb reader, entity 2019-08-01 12:36:10 -07:00
Alexander Rose
469dd05cd9 added Column.ofIntTokens, .ofFloatTokens, .ofStringTokens 2019-08-01 12:34:47 -07:00
Alexander Rose
fb72db61bd code simplification 2019-08-01 12:34:01 -07:00
Alexander Rose
c285e30ee0 improved gro format reading 2019-08-01 10:09:20 -07:00
Alexander Rose
789a327322 improved large chain partitioning 2019-08-01 10:06:50 -07:00
Alexander Rose
b8d2021599 partition very large atomic chains per residue 2019-07-30 16:28:00 -07:00
Alexander Rose
36eae744af ensure that "single atom chains" units have same entity id 2019-07-30 16:24:54 -07:00
Alexander Rose
00df6ae52a don't show sequences > 10000 in widget 2019-07-30 16:23:05 -07:00
Alexander Rose
023b65572e viewer, option to load files without adding visuals 2019-07-30 16:22:34 -07:00
Alexander Rose
904e9b869c wip, cellpack loader improvements 2019-07-30 12:03:26 -07:00
Alexander Rose
6f204b960d Merge branch 'master' into meso 2019-07-30 09:12:08 -07:00
Alexander Rose
6217a51fa5 optimized toScriptExpression 2019-07-29 11:32:02 -07:00
Alexander Rose
f1edb05c5c fixed sorted-ranges spec 2019-07-29 10:03:54 -07:00
Alexander Rose
55bd27bb97 wip, structure selection tools 2019-07-26 17:32:51 -07:00
Alexander Rose
e2c9b601a6 wip, structure tools refactoring 2019-07-26 16:36:27 -07:00
Alexander Rose
238191660e Fixed StructureElement.Loci.toScriptExpression 2019-07-26 16:12:03 -07:00
Alexander Rose
0b175acc25 wip, structure tool controls 2019-07-26 15:50:26 -07:00
Alexander Rose
02865cbece trace-iterator to work with unit subsets 2019-07-26 12:44:40 -07:00
Alexander Rose
a3e14bf579 improved sorted-ranges and docs 2019-07-26 12:44:08 -07:00
Alexander Rose
1d502cbb54 loci remapping 2019-07-25 13:49:04 -07:00
Alexander Rose
4894b110b9 fixes, SortedRanges and AtomicPolymerTraceIterator 2019-07-25 10:58:42 -07:00
Alexander Rose
cefd0440a0 docs 2019-07-25 10:57:26 -07:00
David Sehnal
296bfb343e mol-plugin: updated 'select animation' icon 2019-07-24 07:45:41 +02:00
David Sehnal
bc91f0d3ff Support multiple models in StructureElement.Loci.toScriptExpression 2019-07-24 07:35:03 +02:00
David Sehnal
aaa8215a6d Fixed StructureElement.Loci.toScriptExpression 2019-07-24 07:11:38 +02:00
Alexander Rose
bf0b37895d handle modified base rings (DP, DZ) 2019-07-23 15:51:17 -07:00
Alexander Rose
0810ed411d nucleotide cartoon, detect Purin/Pyrimidin from geometry 2019-07-23 15:16:22 -07:00
Alexander Rose
ff8fec542c wip, overpaint controls, overpaint clearing 2019-07-23 12:04:02 -07:00
Alexander Rose
8fb7308572 wip, OverpaintControls 2019-07-22 17:35:17 -07:00
Alexander Rose
c9b7049532 fixed ExplodeStructureRepresentation3D 2019-07-22 17:34:46 -07:00
Alexander Rose
da71332de1 add 'sel.atom.all' macro 2019-07-22 17:34:30 -07:00
Alexander Rose
a0de8dd9f9 fixed seq widget update on object change 2019-07-22 12:14:03 -07:00
Alexander Rose
e226f27041 fixed wrong property use in seq widget 2019-07-22 12:04:27 -07:00
Alexander Rose
804a04d9f8 fixed coarse trace-iterator direction vectors 2019-07-22 11:52:00 -07:00
Alexander Rose
3be06bb3b6 wip, cellpack loader 2019-07-21 18:28:45 -07:00
Alexander Rose
c7b618c246 lighting demo 2019-07-21 18:13:10 -07:00
Alexander Rose
55d990962f better entity handling for pdb files 2019-07-20 15:58:22 -07:00
Alexander Rose
53e0a36539 improved readme deploy section 2019-07-19 18:38:13 -07:00
Alexander Rose
ed7a5219bf 0.2.5 2019-07-19 17:43:32 -07:00
Alexander Rose
8b49ccdc08 tweaked bottom and top height 2019-07-19 17:41:40 -07:00
Alexander Rose
04fd3ade5f improved canvas3d.resetCamera 2019-07-19 14:52:20 -07:00
Alexander Rose
48985cd49d improved scene commit handling for canvas3d and debug-helper 2019-07-19 14:40:45 -07:00
Alexander Rose
dd0707a8a5 fix renderer.spec test 2019-07-19 10:12:22 -07:00
Alexander Rose
b41ebcbbc8 Merge branch 'master' of https://github.com/molstar/molstar
# Conflicts:
#	src/mol-canvas3d/camera.ts
#	src/mol-canvas3d/canvas3d.ts
2019-07-19 09:50:03 -07:00
Alexander Rose
991d2e3a57 async gl repr object handling 2019-07-19 09:46:27 -07:00
Alexander Rose
7f4ac6782f camera tweaks 2019-07-19 09:45:26 -07:00
Alexander Rose
7e7e30a82e fix: removed useless z-sort 2019-07-19 09:44:11 -07:00
Alexander Rose
08e92f12d3 improved MolecularSurface calc: projectToriiRange 2019-07-19 09:42:09 -07:00
Alexander Rose
d713ea6a76 ModelIndex color theme 2019-07-19 09:41:06 -07:00
Alexander Rose
0924020f24 updated packages 2019-07-19 09:11:53 -07:00
David Sehnal
9f10af3ba6 proteopedia-wrapper: clipping commands; mol-canvas3d: camera.getFocus optional direction 2019-06-26 13:41:53 +02:00
Alexander Rose
f754026cc5 better entity placeholder 2019-06-22 12:26:28 -07:00
Alexander Rose
321d98f4c1 hetero sequence wrapper 2019-06-22 11:35:11 -07:00
Alexander Rose
58a49a8512 fix handling of structure loci selection 2019-06-22 08:12:07 -07:00
Alexander Rose
98bb9575b6 simplified polymer sequence wrapper 2019-06-22 07:45:04 -07:00
Alexander Rose
f10a135dea sequence view, better handling of missing residues, full structure loci 2019-06-22 07:34:20 -07:00
Alexander Rose
f300e524d1 updated packages 2019-06-21 17:36:38 -07:00
Alexander Rose
87028c0a0b 0.2.4 2019-06-21 17:21:59 -07:00
Alexander Rose
8ea23e6965 Merge branch 'master' into seq 2019-06-21 17:19:26 -07:00
Alexander Rose
f9d8942814 basic support for missing residues 2019-06-21 16:55:28 -07:00
Alexander Rose
b40df2f1e3 sequence, on state tree change improvments 2019-06-21 16:55:12 -07:00
Alexander Rose
884cb0d9a4 sequence widget refactoring 2019-06-21 12:54:22 -07:00
Alexander Rose
ef1ccd4286 cif schema updates 2019-06-21 12:44:28 -07:00
Alexander Rose
898abda373 sequence & interactivity tweaks 2019-06-21 11:06:49 -07:00
Alexander Rose
e42c664a8c fixes: StructureElement.Loci.union, Structure.parent 2019-06-21 10:56:06 -07:00
Alexander Rose
987bf47827 save interactivity props in state 2019-06-21 09:29:03 -07:00
Alexander Rose
6201dd1d74 improved xtal symmetry support for props & sequence 2019-06-21 09:11:38 -07:00
David Sehnal
5ed17ce4e5 proteopedia-wrapper: evolutionary coloring on current representation 2019-06-21 12:47:14 +02:00
Alexander Rose
e301eca9c2 wip, sequence view, select options 2019-06-20 15:50:31 -07:00
Alexander Rose
8a4ef015a2 added StructureElement.set 2019-06-20 15:42:45 -07:00
Alexander Rose
67f3f3fdbb improved AtomsQueryParams.unitTest and StructureProperties.unit 2019-06-20 15:42:23 -07:00
Alexander Rose
adc5b559cd various tweaks 2019-06-20 15:35:55 -07:00
Alexander Rose
bbaa637118 wip, sequence selector 2019-06-19 17:45:50 -07:00
Alexander Rose
a3094b4d19 wip, per-chain sequence widget 2019-06-19 17:03:20 -07:00
Alexander Rose
c3f937e113 sequence widget refactoring 2019-06-19 14:17:49 -07:00
Alexander Rose
04df327939 renamed lociExpansion to granularity 2019-06-19 12:30:19 -07:00
Alexander Rose
b1a0f46ade improved link loci handling for interactivity 2019-06-19 12:18:53 -07:00
Alexander Rose
389e249862 use PurePluginUIComponent for Residue 2019-06-19 11:48:36 -07:00
Alexander Rose
cfcf9f6818 wip, plugin interactivity 2019-06-18 17:28:57 -07:00
Alexander Rose
bcb8419f37 init repr3d and seq view marker with global selection 2019-06-18 14:31:22 -07:00
Alexander Rose
7d24bcf1dc tweaked resolution quality settings 2019-06-18 14:24:47 -07:00
Alexander Rose
8d0f7a2dc7 factored-out marker-action 2019-06-18 11:48:59 -07:00
Alexander Rose
a7cb7beaa8 Merge branch 'master' into seq 2019-06-18 09:04:33 -07:00
David Sehnal
3c9b82dc04 proteopedia-plugin: Load asym unit fix 2019-06-18 15:42:29 +02:00
David Sehnal
9dba6d5371 proteopedia-wrapper: tweak 2019-06-18 13:53:04 +02:00
David Sehnal
a65bba0969 proteopedia-wrapper: better HET group focusing 2019-06-18 13:39:23 +02:00
David Sehnal
175e009152 proteopedia-wrapper: fix assembly loading 2019-06-18 13:01:38 +02:00
Alexander Rose
897d17c8ed 0.2.3 2019-06-17 16:53:59 -07:00
Alexander Rose
ca866cfa3a Merge pull request #17 from JonStargaryen/encoding-config
Encoding and precision config
2019-06-17 16:48:55 -07:00
Alexander Rose
f6b2c0b2ba wip, sequence view 2019-06-17 16:44:38 -07:00
Alexander Rose
ea419c68ae removed old unused ui code 2019-06-17 15:41:03 -07:00
Alexander Rose
40cf348d40 simple (entity) sequence view 2019-06-17 15:40:51 -07:00
Alexander Rose
93ea759a71 StateTreeSpine.current tweaks 2019-06-17 15:39:12 -07:00
Alexander Rose
3e50377eb8 use as const to quickly make class props readonly 2019-06-17 15:29:37 -07:00
Alexander Rose
3fbd1f8dc4 StructureElementSelectionManager.tryGetRange in both directions 2019-06-17 15:27:22 -07:00
Alexander Rose
f03ce68513 plugin interaction helpers refactoring 2019-06-17 14:37:24 -07:00
Alexander Rose
50e2d542df add getModifiers input helper 2019-06-17 14:33:32 -07:00
Alexander Rose
e53e739d18 ignore non StructureElement loci in StructureElementSelectionManager 2019-06-17 14:32:47 -07:00
Alexander Rose
dfb7f7811f support coarse elements in atomGroups generator 2019-06-14 18:59:37 -07:00
Sebastian Bittrich
115824bbcf omits logging in test 2019-06-14 17:38:03 -07:00
Sebastian Bittrich
438de5760d trim() for safety in filter declaration 2019-06-14 17:37:47 -07:00
Sebastian Bittrich
3f765aedec removes FilteringDirective 2019-06-14 17:12:07 -07:00
Sebastian Bittrich
96144fb10f drops json filter definition in favor of text 2019-06-14 17:03:34 -07:00
Alexander Rose
cc34425712 add and use Structure.areParentsEquivalent 2019-06-13 16:36:18 -07:00
Alexander Rose
9d68838893 added OrderedSet.toString 2019-06-13 15:59:50 -07:00
Alexander Rose
6c68cebca0 show gfp chromophore traces 2019-06-13 13:32:34 -07:00
Alexander Rose
b3e784262d fix typos 2019-06-13 12:00:17 -07:00
Alexander Rose
2fdc22de71 plugin layout config improvements 2019-06-13 09:52:51 -07:00
Alexander Rose
77f0b0033f more recomended vscode extensions 2019-06-13 09:26:10 -07:00
Alexander Rose
69da5abb88 use staging url for rcsb graphql 2019-06-13 09:25:50 -07:00
Alexander Rose
a827e9a449 Merge branch 'master' of https://github.com/molstar/molstar 2019-06-13 09:08:10 -07:00
Alexander Rose
e74a29ae6a added recommeded vscode extensions 2019-06-12 23:21:59 -07:00
Alexander Rose
9b3d2f396e polymer-trace improvements 2019-06-12 23:14:05 -07:00
Alexander Rose
14cf7cc101 direction from/to derived residue prop 2019-06-12 23:10:50 -07:00
Alexander Rose
e5293c4d36 linalg .toString with precision arg 2019-06-12 23:09:24 -07:00
Sebastian Bittrich
3afe21a4c3 fix to whitelist categories when information on fields is present 2019-06-12 14:43:41 -07:00
Alexander Rose
bb50b69bb4 fix typo 2019-06-12 12:33:33 -07:00
Alexander Rose
af7c030338 removed unused HighlightEvent.prev 2019-06-12 12:33:18 -07:00
Alexander Rose
34b1eee876 fix webpack config 2019-06-12 12:32:00 -07:00
Sebastian Bittrich
c9cd1075d3 Merge branch 'master' into encoding-config 2019-06-12 12:22:20 -07:00
Sebastian Bittrich
4fa04f0ff8 changes comment style 2019-06-12 11:42:04 -07:00
Alexander Rose
75f6466fd7 webpack config update 2019-06-11 16:18:16 -07:00
Alexander Rose
503ffd80fd package updates 2019-06-11 15:04:15 -07:00
Alexander Rose
c8ac64a571 wip, server/model/preprocess 2019-06-10 16:15:14 -07:00
Alexander Rose
bf81b902bd chemCompBond creation improvements and prop 2019-06-10 15:30:13 -07:00
David Sehnal
b636cdf9cc Added volume streaming to proteopedia-wrapper example + vol. streaming behavior tweaks 2019-06-10 14:02:12 +02:00
David Sehnal
2d3b85825a mol-plugin: Allow to mount TransformUpdaterControl outside the main plugin 2019-06-10 13:13:30 +02:00
Alexander Rose
a5a34f39e0 don't use boxed primitives 2019-06-07 19:13:23 -07:00
Sebastian Bittrich
fe7e04f61b lowercase string parameters, filtering directive object 2019-06-07 16:56:04 -07:00
Sebastian Bittrich
1a14720e35 remove changes from package-lock 2019-06-07 15:45:12 -07:00
Sebastian Bittrich
18bf743ed2 white-/blacklist filtering 2019-06-07 15:39:07 -07:00
Sebastian Bittrich
f8d085a034 white-/blacklist filtering 2019-06-07 15:38:06 -07:00
Alexander Rose
94bf3a136c added viewer deployment script 2019-06-07 13:17:45 -07:00
Alexander Rose
9e8a8f3e71 updated viewer 2019-06-07 12:36:29 -07:00
Alexander Rose
45f9d93f3a fixed clearOverpaint 2019-06-07 12:18:29 -07:00
Alexander Rose
44a566fdf3 tweaked ObjectListControl ui 2019-06-07 12:18:16 -07:00
Sebastian Bittrich
d7f7770b7c drops browser test, creates spec 2019-06-07 11:49:31 -07:00
Sebastian Bittrich
df6b163505 replace encoding string with union type 2019-06-07 09:27:46 -07:00
Sebastian Bittrich
51f88fff71 merge master 2019-06-05 10:04:18 -07:00
Sebastian Bittrich
6b8db5abc6 reverts trygetencoder order to original 2019-06-05 10:01:46 -07:00
Sebastian Bittrich
37a0b07d56 stub for encoding config 2019-06-04 17:52:02 -07:00
479 changed files with 23857 additions and 7698 deletions

18
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"firsttris.vscode-jest-runner",
"ms-vscode.vscode-typescript-tslint-plugin",
"msjsdiag.debugger-for-chrome",
"slevesque.shader",
"stpn.vscode-graphql",
"wayou.vscode-todo-highlight"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Mol* Viewer",
"url": "http://localhost:1338/build/viewer/index.html",
"sourceMaps": true,
"webRoot": "${workspaceFolder}"
}
]
}

View File

@@ -62,6 +62,8 @@ This project builds on experience from previous solutions:
### Build with debug mode enabled:
DEBUG=molstar npm run watch
Debug/production mode in browsers can be turned on/off during runtime by calling ``setMolStarDebugMode(true/false, true/false)`` from the dev console.
### Build for production:
NODE_ENV=production npm run build
@@ -85,7 +87,7 @@ and navigate to `build/viewer`
Install CIFTools `npm install ciftools -g`
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
cifschema -mip ../../../../mol-data-o src/mol-io/reader/cif/schema/ccd.ts -p CCD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
**GraphQL schemas**
@@ -95,7 +97,7 @@ Install CIFTools `npm install ciftools -g`
### Other scripts
**Create chem comp bond table**
export NODE_PATH="lib"; node --max-old-space-size=8192 build/src/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b
**Test model server**
@@ -133,14 +135,19 @@ To get syntax highlighting for shader and graphql files add the following to Vis
## Publish
## Prerelease
### Prerelease
npm version prerelease # asumes the current version ends with '-dev.X'
npm publish --tag next
## Release
### Release
npm version 0.X.0 # provide valid semver string
npm publish
## Deploy
npm run test
npm run build
node ./scripts/deploy.js # currently updates the viewer on molstar.org/viewer
## Contributing
Just open an issue or make a pull request. All contributions are welcome.

60
data/cif-core.csv Normal file
View File

@@ -0,0 +1,60 @@
audit.block_doi
database_code.depnum_ccdc_archive
chemical.name_systematic
chemical.name_common
chemical.melting_point
chemical_formula.moiety
chemical_formula.sum
chemical_formula.weight
atom_type.symbol
atom_type.description
atom_type_scat.dispersion_real
atom_type_scat.dispersion_imag
atom_type_scat.source
space_group.crystal_system
space_group.name_H-M_full
space_group_symop.operation_xyz
cell.length_a
cell.length_b
cell.length_c
cell.angle_alpha
cell.angle_beta
cell.angle_gamma
cell.volume
cell.formula_units_Z
atom_site.label
atom_site.type_symbol
atom_site.fract_x
atom_site.fract_y
atom_site.fract_z
atom_site.U_iso_or_equiv
atom_site.adp_type
atom_site.occupancy
atom_site.calc_flag
atom_site.refinement_flags
atom_site.disorder_assembly
atom_site.disorder_group
atom_site.site_symmetry_multiplicity
atom_site_aniso.label
atom_site_aniso.U_11
atom_site_aniso.U_22
atom_site_aniso.U_33
atom_site_aniso.U_23
atom_site_aniso.U_13
atom_site_aniso.U_12
geom_bond.atom_site_label_1
geom_bond.atom_site_label_2
geom_bond.distance
geom_bond.site_symmetry_2
geom_bond.publ_flag
1 audit.block_doi
2 database_code.depnum_ccdc_archive
3 chemical.name_systematic
4 chemical.name_common
5 chemical.melting_point
6 chemical_formula.moiety
7 chemical_formula.sum
8 chemical_formula.weight
9 atom_type.symbol
10 atom_type.description
11 atom_type_scat.dispersion_real
12 atom_type_scat.dispersion_imag
13 atom_type_scat.source
14 space_group.crystal_system
15 space_group.name_H-M_full
16 space_group_symop.operation_xyz
17 cell.length_a
18 cell.length_b
19 cell.length_c
20 cell.angle_alpha
21 cell.angle_beta
22 cell.angle_gamma
23 cell.volume
24 cell.formula_units_Z
25 atom_site.label
26 atom_site.type_symbol
27 atom_site.fract_x
28 atom_site.fract_y
29 atom_site.fract_z
30 atom_site.U_iso_or_equiv
31 atom_site.adp_type
32 atom_site.occupancy
33 atom_site.calc_flag
34 atom_site.refinement_flags
35 atom_site.disorder_assembly
36 atom_site.disorder_group
37 atom_site.site_symmetry_multiplicity
38 atom_site_aniso.label
39 atom_site_aniso.U_11
40 atom_site_aniso.U_22
41 atom_site_aniso.U_33
42 atom_site_aniso.U_23
43 atom_site_aniso.U_13
44 atom_site_aniso.U_12
45 geom_bond.atom_site_label_1
46 geom_bond.atom_site_label_2
47 geom_bond.distance
48 geom_bond.site_symmetry_2
49 geom_bond.publ_flag

View File

@@ -4,7 +4,7 @@ const path = require('path')
const basePath = path.join(__dirname, '..', '..', 'src', 'mol-model-props', 'rcsb', 'graphql')
generate({
schema: 'http://rest-dev.rcsb.org/graphql',
schema: 'http://rest-staging.rcsb.org/graphql',
documents: {
[path.join(basePath, 'symmetry.gql.ts')]: {
loader: path.join(__dirname, 'loader.js')

884
examples/867861-core.cif Normal file
View File

@@ -0,0 +1,884 @@
#######################################################################
#
# This file contains crystal structure data downloaded from the
# Cambridge Structural Database (CSD) hosted by the Cambridge
# Crystallographic Data Centre (CCDC).
#
# Full information about CCDC data access policies and citation
# guidelines are available at http://www.ccdc.cam.ac.uk/access/V1
#
# Audit and citation data items may have been added by the CCDC.
# Please retain this information to preserve the provenance of
# this file and to allow appropriate attribution of the data.
#
#######################################################################
data_n1379
_audit_block_doi 10.5517/ccy42jn
_database_code_depnum_ccdc_archive 'CCDC 867861'
loop_
_citation_id
_citation_doi
_citation_year
1 10.1002/chem.201202070 2012
_audit_update_record
;
2012-02-20 deposited with the CCDC.
2016-10-08 downloaded from the CCDC.
;
_audit_creation_method SHELXL-97
_chemical_name_systematic
;
?
;
_chemical_name_common ?
_chemical_melting_point ?
_chemical_formula_moiety 'C76 H90 N10 O14 4(C2 F3 O2) 4(C2 H3 N)'
_chemical_formula_sum 'C92 H102 F12 N14 O22'
_chemical_formula_weight 1983.88
loop_
_atom_type_symbol
_atom_type_description
_atom_type_scat_dispersion_real
_atom_type_scat_dispersion_imag
_atom_type_scat_source
C C 0.0181 0.0091 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
H H 0.0000 0.0000 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
N N 0.0311 0.0180 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
O O 0.0492 0.0322 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
F F 0.0727 0.0534 'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
_symmetry_cell_setting Triclinic
_symmetry_space_group_name_H-M P-1
loop_
_symmetry_equiv_pos_as_xyz
'x, y, z'
'-x, -y, -z'
_cell_length_a 11.0829(8)
_cell_length_b 14.6829(10)
_cell_length_c 16.8532(17)
_cell_angle_alpha 105.728(6)
_cell_angle_beta 100.310(6)
_cell_angle_gamma 110.620(4)
_cell_volume 2353.3(3)
_cell_formula_units_Z 1
_cell_measurement_temperature 100(2)
_cell_measurement_reflns_used 5934
_cell_measurement_theta_min 2.86
_cell_measurement_theta_max 64.30
_exptl_crystal_description plate
_exptl_crystal_colour violet
_exptl_crystal_size_max 0.57
_exptl_crystal_size_mid 0.18
_exptl_crystal_size_min 0.05
_exptl_crystal_density_meas ?
_exptl_crystal_density_diffrn 1.400
_exptl_crystal_density_method ?
_exptl_crystal_F_000 1036
_exptl_absorpt_coefficient_mu 0.995
_exptl_absorpt_correction_type integration
_exptl_absorpt_correction_T_min 0.6022
_exptl_absorpt_correction_T_max 0.9482
_exptl_absorpt_process_details 'XPREP, face-indexed'
_exptl_special_details
;
?
;
_diffrn_ambient_temperature 100(2)
_diffrn_radiation_wavelength 1.54178
_diffrn_radiation_type CuK\a
_diffrn_radiation_source microsource
_diffrn_radiation_monochromator 'Quazar optics'
_diffrn_measurement_device_type 'Bruker APEX-II CCD'
_diffrn_measurement_method '\f and \w scans'
_diffrn_detector_area_resol_mean ?
_diffrn_reflns_number 16613
_diffrn_reflns_av_R_equivalents 0.1477
_diffrn_reflns_av_sigmaI/netI 0.1112
_diffrn_reflns_limit_h_min -12
_diffrn_reflns_limit_h_max 8
_diffrn_reflns_limit_k_min -17
_diffrn_reflns_limit_k_max 17
_diffrn_reflns_limit_l_min -19
_diffrn_reflns_limit_l_max 19
_diffrn_reflns_theta_min 2.86
_diffrn_reflns_theta_max 64.94
_reflns_number_total 7680
_reflns_number_gt 5560
_reflns_threshold_expression >2sigma(I)
_computing_data_collection 'Bruker APEX2'
_computing_cell_refinement 'Bruker SAINT'
_computing_data_reduction 'Bruker SAINT'
_computing_structure_solution 'SHELXS-97 (Sheldrick, 2008)'
_computing_structure_refinement 'SHELXL-97 (Sheldrick, 2008)'
_computing_molecular_graphics 'Bruker SHELXTL'
_computing_publication_material 'Bruker SHELXTL'
_refine_special_details
;
Refinement of F^2^ against ALL reflections. The weighted R-factor wR and
goodness of fit S are based on F^2^, conventional R-factors R are based
on F, with F set to zero for negative F^2^. The threshold expression of
F^2^ > 2sigma(F^2^) is used only for calculating R-factors(gt) etc. and is
not relevant to the choice of reflections for refinement. R-factors based
on F^2^ are statistically about twice as large as those based on F, and R-
factors based on ALL data will be even larger.
Rigid bond restraints (esd 0.002) were imposed on the displacement parameters,
as well as restraints on similar amplitudes (esd 0.002) separated by less
than 1.7 Ang. on C27, C29, and N102.
Distance restraints were refined on the bond between C28 and C29.
;
_refine_ls_structure_factor_coef Fsqd
_refine_ls_matrix_type full
_refine_ls_weighting_scheme calc
_refine_ls_weighting_details
'calc w=1/[\s^2^(Fo^2^)+(0.1912P)^2^+4.8134P] where P=(Fo^2^+2Fc^2^)/3'
_atom_sites_solution_primary direct
_atom_sites_solution_secondary difmap
_atom_sites_solution_hydrogens geom
_refine_ls_hydrogen_treatment mixed
_refine_ls_extinction_method none
_refine_ls_extinction_coef ?
_refine_ls_number_reflns 7680
_refine_ls_number_parameters 633
_refine_ls_number_restraints 1
_refine_ls_R_factor_all 0.1349
_refine_ls_R_factor_gt 0.1101
_refine_ls_wR_factor_ref 0.3402
_refine_ls_wR_factor_gt 0.3102
_refine_ls_goodness_of_fit_ref 1.089
_refine_ls_restrained_S_all 1.096
_refine_ls_shift/su_max 0.000
_refine_ls_shift/su_mean 0.000
loop_
_atom_site_label
_atom_site_type_symbol
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_U_iso_or_equiv
_atom_site_adp_type
_atom_site_occupancy
_atom_site_symmetry_multiplicity
_atom_site_calc_flag
_atom_site_refinement_flags
_atom_site_disorder_assembly
_atom_site_disorder_group
C1 C -0.3373(4) -0.2086(3) -0.2547(3) 0.0229(10) Uani 1 1 d . . .
H1 H -0.3820 -0.2827 -0.2795 0.027 Uiso 1 1 calc R . .
C2 C -0.1956(4) -0.0511(3) -0.2602(3) 0.0215(10) Uani 1 1 d . . .
H2 H -0.1401 -0.0147 -0.2886 0.026 Uiso 1 1 calc R . .
C3 C -0.3583(4) -0.1574(3) -0.1814(3) 0.0213(9) Uani 1 1 d . . .
H3 H -0.4168 -0.1960 -0.1558 0.026 Uiso 1 1 calc R . .
C4 C -0.2146(4) 0.0039(3) -0.1873(3) 0.0180(9) Uani 1 1 d . . .
H4 H -0.1736 0.0780 -0.1660 0.022 Uiso 1 1 calc R . .
C5 C -0.2943(4) -0.0488(3) -0.1440(3) 0.0163(9) Uani 1 1 d . . .
C6 C -0.3053(4) 0.0091(3) -0.0600(3) 0.0170(9) Uani 1 1 d . . .
C7 C -0.3538(4) -0.0444(3) -0.0071(3) 0.0193(9) Uani 1 1 d . . .
H7 H -0.3850 -0.1181 -0.0264 0.023 Uiso 1 1 calc R . .
C8 C -0.2620(4) 0.1168(3) -0.0291(3) 0.0194(9) Uani 1 1 d . . .
H8 H -0.2304 0.1553 -0.0640 0.023 Uiso 1 1 calc R . .
C9 C -0.3563(4) 0.0094(3) 0.0725(3) 0.0218(10) Uani 1 1 d . . .
H9 H -0.3904 -0.0276 0.1077 0.026 Uiso 1 1 calc R . .
C10 C -0.2648(4) 0.1679(3) 0.0515(3) 0.0190(9) Uani 1 1 d . . .
H10 H -0.2340 0.2416 0.0724 0.023 Uiso 1 1 calc R . .
C11 C -0.3024(4) 0.1707(4) 0.1919(3) 0.0227(10) Uani 1 1 d . . .
H11A H -0.3772 0.1277 0.2088 0.027 Uiso 1 1 calc R . .
H11B H -0.3094 0.2371 0.1964 0.027 Uiso 1 1 calc R . .
C12 C -0.1670(4) 0.1920(3) 0.2509(2) 0.0184(9) Uani 1 1 d . . .
C13 C -0.1547(5) 0.1179(4) 0.2849(3) 0.0223(10) Uani 1 1 d . . .
H13 H -0.2328 0.0589 0.2788 0.027 Uiso 1 1 calc R . .
C14 C -0.0541(4) 0.2821(3) 0.2667(2) 0.0204(9) Uani 1 1 d . . .
H14 H -0.0631 0.3352 0.2475 0.024 Uiso 1 1 calc R . .
C15 C -0.0272(5) 0.1313(4) 0.3277(3) 0.0231(10) Uani 1 1 d . . .
H15 H -0.0192 0.0806 0.3506 0.028 Uiso 1 1 calc R . .
C16 C 0.0731(4) 0.2946(3) 0.3110(2) 0.0195(9) Uani 1 1 d . . .
C17 C 0.0890(4) 0.2163(3) 0.3381(2) 0.0204(10) Uani 1 1 d . . .
C18 C 0.2252(5) 0.2149(4) 0.3687(3) 0.0273(11) Uani 1 1 d . . .
H18A H 0.2965 0.2872 0.3968 0.033 Uiso 1 1 calc R . .
H18B H 0.2237 0.1802 0.4116 0.033 Uiso 1 1 calc R . .
C19 C 0.2862(4) 0.4582(4) 0.3974(3) 0.0247(10) Uani 1 1 d . . .
H19 H 0.3032 0.4532 0.4530 0.030 Uiso 1 1 calc R . .
C20 C 0.3577(5) 0.5371(4) 0.3739(3) 0.0279(11) Uani 1 1 d . . .
C21 C 0.4805(5) 0.6358(4) 0.4267(3) 0.0333(12) Uani 1 1 d . . .
H21A H 0.4882 0.6869 0.3975 0.040 Uiso 1 1 calc R . .
H21B H 0.4699 0.6656 0.4837 0.040 Uiso 1 1 calc R . .
C22 C 0.6303(6) 0.5857(4) 0.3599(3) 0.0374(12) Uani 1 1 d . . .
H22A H 0.6523 0.6416 0.3355 0.045 Uiso 1 1 calc R . .
H22B H 0.5494 0.5242 0.3176 0.045 Uiso 1 1 calc R . .
C23 C 0.7460(6) 0.5575(5) 0.3762(4) 0.0413(13) Uani 1 1 d . . .
H23A H 0.7730 0.5406 0.3231 0.050 Uiso 1 1 calc R . .
H23B H 0.8246 0.6169 0.4226 0.050 Uiso 1 1 calc R . .
C24 C 0.7952(6) 0.4206(5) 0.3967(4) 0.0496(16) Uani 1 1 d . . .
H24A H 0.8893 0.4743 0.4264 0.060 Uiso 1 1 calc R . .
H24B H 0.7870 0.3874 0.3353 0.060 Uiso 1 1 calc R . .
C25 C 0.7663(11) 0.3428(7) 0.4361(4) 0.074(3) Uani 1 1 d . . .
H25A H 0.8372 0.3165 0.4378 0.089 Uiso 1 1 calc R . .
H25B H 0.7691 0.3754 0.4965 0.089 Uiso 1 1 calc R . .
C26 C 0.6441(8) 0.1838(7) 0.4342(5) 0.071(2) Uani 1 1 d . . .
H26A H 0.7153 0.1600 0.4249 0.085 Uiso 1 1 calc R . .
H26B H 0.6577 0.2137 0.4970 0.085 Uiso 1 1 calc R . .
C27 C 0.5204(10) 0.1080(8) 0.3921(7) 0.125(5) Uani 1 1 d . . .
H27A H 0.5220 0.0408 0.3913 0.150 Uiso 1 1 calc R . .
H27B H 0.4964 0.1039 0.3313 0.150 Uiso 1 1 calc R . .
C28 C 0.3730(9) 0.0886(7) 0.4988(5) 0.097(4) Uani 1 1 d D . .
H28A H 0.4284 0.0506 0.5097 0.116 Uiso 1 1 calc R . .
H28B H 0.4078 0.1526 0.5510 0.116 Uiso 1 1 calc R . .
C29 C 0.2559(11) 0.033(2) 0.4964(8) 0.281(16) Uani 1 1 d D . .
H29A H 0.2452 0.0690 0.5518 0.337 Uiso 1 1 calc R . .
H29B H 0.2589 -0.0322 0.5000 0.337 Uiso 1 1 calc R . .
C30 C 0.0569(12) -0.1026(9) 0.4169(5) 0.089(3) Uani 1 1 d . . .
H30A H 0.0442 -0.1134 0.4708 0.107 Uiso 1 1 calc R . .
H30B H 0.1035 -0.1448 0.3928 0.107 Uiso 1 1 calc R . .
C31 C -0.0702(10) -0.1360(6) 0.3569(6) 0.083(3) Uani 1 1 d . . .
H31A H -0.1307 -0.2064 0.3530 0.100 Uiso 1 1 calc R . .
H31B H -0.1113 -0.0878 0.3776 0.100 Uiso 1 1 calc R . .
C32 C -0.1964(9) -0.1774(5) 0.2168(5) 0.067(2) Uani 1 1 d . . .
H32A H -0.2564 -0.2441 0.2195 0.080 Uiso 1 1 calc R . .
H32B H -0.2342 -0.1258 0.2343 0.080 Uiso 1 1 calc R . .
C33 C -0.1881(7) -0.1926(5) 0.1297(4) 0.0593(19) Uani 1 1 d . . .
H33A H -0.2803 -0.2327 0.0880 0.071 Uiso 1 1 calc R . .
H33B H -0.1354 -0.2340 0.1169 0.071 Uiso 1 1 calc R . .
C34 C -0.0909(4) -0.0989(4) 0.0440(3) 0.0231(10) Uani 1 1 d . . .
C35 C -0.1178(5) -0.1907(4) -0.0206(3) 0.0290(11) Uani 1 1 d . . .
H35 H -0.1660 -0.2560 -0.0162 0.035 Uiso 1 1 calc R . .
C36 C -0.0214(4) -0.0005(3) 0.0371(3) 0.0182(9) Uani 1 1 d . . .
C37 C 0.0055(4) 0.0950(3) 0.1019(3) 0.0212(10) Uani 1 1 d . . .
H37 H -0.0240 0.0948 0.1515 0.025 Uiso 1 1 calc R . .
C38 C 0.0734(4) 0.1868(4) 0.0931(3) 0.0249(10) Uani 1 1 d . . .
H38 H 0.0914 0.2503 0.1372 0.030 Uiso 1 1 calc R . .
N1 N -0.2553(3) -0.1567(3) -0.2921(2) 0.0202(8) Uani 1 1 d . . .
N2 N -0.3112(3) 0.1137(3) 0.1013(2) 0.0181(8) Uani 1 1 d . . .
N3 N 0.1854(4) 0.3880(3) 0.3242(2) 0.0221(8) Uani 1 1 d . . .
N4 N 0.1947(4) 0.4236(3) 0.2574(2) 0.0281(9) Uani 1 1 d . . .
N5 N 0.2991(4) 0.5136(3) 0.2881(3) 0.0315(10) Uani 1 1 d . . .
O1 O 0.6028(3) 0.6210(3) 0.4395(2) 0.0301(8) Uani 1 1 d . . .
O2 O 0.7045(4) 0.4689(3) 0.4018(2) 0.0429(10) Uani 1 1 d . . .
O3 O 0.6381(7) 0.2580(4) 0.3907(4) 0.0743(17) Uani 1 1 d . . .
O4 O 0.4041(4) 0.1218(3) 0.4321(2) 0.0412(10) Uani 1 1 d . . .
O5 O 0.1363(5) 0.0023(6) 0.4355(3) 0.0786(19) Uani 1 1 d . . .
O6 O -0.0597(6) -0.1394(4) 0.2749(3) 0.0757(17) Uani 1 1 d . . .
O7 O -0.1244(3) -0.0939(3) 0.1185(2) 0.0320(8) Uani 1 1 d . . .
C101 C 0.6294(5) 0.3676(4) 0.1211(3) 0.0286(11) Uani 1 1 d . . .
C102 C 0.5798(8) 0.4458(5) 0.1022(5) 0.066(2) Uani 1 1 d . . .
C103 C 0.5077(5) 0.8961(4) 0.2466(3) 0.0342(12) Uani 1 1 d . . .
C104 C 0.3819(8) 0.8078(6) 0.2433(5) 0.077(3) Uani 1 1 d . . .
C105 C 0.0707(7) 0.4215(5) 0.0313(4) 0.0516(16) Uani 1 1 d . . .
C106 C 0.2104(7) 0.4969(6) 0.0645(5) 0.066(2) Uani 1 1 d . . .
H10A H 0.2627 0.4718 0.0303 0.099 Uiso 1 1 calc R . .
H10B H 0.2471 0.5066 0.1251 0.099 Uiso 1 1 calc R . .
H10C H 0.2161 0.5637 0.0606 0.099 Uiso 1 1 calc R . .
C107 C 0.0569(7) 0.6249(7) 0.2999(5) 0.071(2) Uani 1 1 d . . .
C108 C 0.0316(8) 0.5978(7) 0.2128(5) 0.077(2) Uani 1 1 d . . .
H10D H 0.0994 0.5753 0.1954 0.115 Uiso 1 1 calc R . .
H10E H 0.0360 0.6581 0.1969 0.115 Uiso 1 1 calc R . .
H10F H -0.0588 0.5404 0.1833 0.115 Uiso 1 1 calc R . .
O101 O 0.5421(4) 0.2774(3) 0.0882(3) 0.0463(10) Uani 1 1 d . . .
O102 O 0.7472(4) 0.4014(3) 0.1666(3) 0.0485(11) Uani 1 1 d . . .
O103 O 0.4882(4) 0.9389(3) 0.1955(2) 0.0356(9) Uani 1 1 d . . .
O104 O 0.6107(4) 0.9176(3) 0.3029(3) 0.0517(11) Uani 1 1 d . . .
F101 F 0.4877(8) 0.4530(7) 0.1408(4) 0.148(3) Uani 1 1 d . . .
F102 F 0.5188(6) 0.4177(4) 0.0191(3) 0.105(2) Uani 1 1 d . . .
F103 F 0.6733(8) 0.5407(3) 0.1298(5) 0.160(4) Uani 1 1 d . . .
F104 F 0.2811(5) 0.7655(4) 0.1734(3) 0.098(2) Uani 1 1 d . . .
F105 F 0.3328(8) 0.8406(9) 0.3070(4) 0.233(7) Uani 1 1 d . . .
F106 F 0.4002(9) 0.7351(6) 0.2617(7) 0.224(6) Uani 1 1 d . . .
N101 N -0.0392(7) 0.3613(5) 0.0045(4) 0.0715(19) Uani 1 1 d . . .
N102 N 0.0774(10) 0.6479(13) 0.3716(6) 0.185(7) Uani 1 1 d . . .
loop_
_atom_site_aniso_label
_atom_site_aniso_U_11
_atom_site_aniso_U_22
_atom_site_aniso_U_33
_atom_site_aniso_U_23
_atom_site_aniso_U_13
_atom_site_aniso_U_12
C1 0.015(2) 0.023(2) 0.022(2) -0.0003(18) -0.0032(18) 0.0097(17)
C2 0.021(2) 0.032(2) 0.012(2) 0.0047(17) -0.0022(17) 0.0168(19)
C3 0.0099(19) 0.030(2) 0.021(2) 0.0059(18) -0.0007(17) 0.0104(17)
C4 0.017(2) 0.027(2) 0.0111(19) 0.0045(16) -0.0012(16) 0.0157(18)
C5 0.0087(18) 0.025(2) 0.0134(19) 0.0028(16) -0.0032(15) 0.0121(16)
C6 0.0075(18) 0.031(2) 0.014(2) 0.0067(17) -0.0017(15) 0.0130(17)
C7 0.0074(18) 0.029(2) 0.019(2) 0.0047(17) 0.0003(16) 0.0093(17)
C8 0.0135(19) 0.028(2) 0.013(2) 0.0039(17) -0.0035(16) 0.0120(17)
C9 0.013(2) 0.031(2) 0.020(2) 0.0072(18) 0.0012(17) 0.0105(18)
C10 0.0112(19) 0.029(2) 0.018(2) 0.0066(17) 0.0025(16) 0.0122(17)
C11 0.019(2) 0.038(2) 0.014(2) 0.0056(18) 0.0059(17) 0.0178(19)
C12 0.019(2) 0.029(2) 0.0068(18) -0.0007(16) 0.0013(16) 0.0169(18)
C13 0.024(2) 0.030(2) 0.013(2) 0.0023(17) 0.0047(18) 0.0159(19)
C14 0.027(2) 0.027(2) 0.0079(19) -0.0015(16) 0.0012(17) 0.0200(19)
C15 0.030(2) 0.034(2) 0.012(2) 0.0056(17) 0.0059(18) 0.023(2)
C16 0.023(2) 0.028(2) 0.0057(18) -0.0018(16) 0.0027(16) 0.0154(19)
C17 0.024(2) 0.032(2) 0.0063(18) -0.0012(16) 0.0008(16) 0.0209(19)
C18 0.028(2) 0.040(3) 0.010(2) -0.0024(18) -0.0038(18) 0.025(2)
C19 0.023(2) 0.034(2) 0.012(2) -0.0015(18) -0.0024(17) 0.017(2)
C20 0.024(2) 0.031(2) 0.019(2) 0.0008(19) -0.0030(19) 0.011(2)
C21 0.027(3) 0.034(3) 0.028(3) -0.001(2) 0.001(2) 0.014(2)
C22 0.038(3) 0.046(3) 0.027(3) 0.011(2) 0.010(2) 0.018(2)
C23 0.039(3) 0.054(3) 0.033(3) 0.012(3) 0.015(2) 0.022(3)
C24 0.051(3) 0.061(4) 0.034(3) -0.002(3) 0.006(3) 0.038(3)
C25 0.145(8) 0.082(5) 0.039(4) 0.023(4) 0.042(5) 0.090(6)
C26 0.068(5) 0.096(6) 0.055(4) 0.020(4) 0.007(4) 0.053(5)
C27 0.105(7) 0.115(7) 0.120(8) -0.033(6) -0.051(6) 0.106(7)
C28 0.082(6) 0.105(6) 0.049(4) 0.052(4) -0.036(4) -0.012(5)
C29 0.074(7) 0.62(4) 0.114(10) 0.252(18) 0.001(7) 0.029(14)
C30 0.149(9) 0.141(9) 0.054(5) 0.065(5) 0.057(6) 0.113(8)
C31 0.110(7) 0.062(5) 0.082(6) 0.050(4) 0.030(5) 0.021(5)
C32 0.094(6) 0.046(4) 0.061(4) 0.021(3) 0.040(4) 0.021(4)
C33 0.056(4) 0.048(4) 0.046(4) 0.013(3) 0.018(3) -0.007(3)
C34 0.014(2) 0.035(2) 0.014(2) 0.0059(18) -0.0019(17) 0.0096(18)
C35 0.025(2) 0.032(2) 0.021(2) 0.0050(19) -0.0033(19) 0.010(2)
C36 0.0082(18) 0.030(2) 0.0130(19) 0.0013(17) -0.0030(15) 0.0125(17)
C37 0.017(2) 0.032(2) 0.013(2) -0.0004(17) -0.0039(16) 0.0181(19)
C38 0.024(2) 0.029(2) 0.016(2) -0.0031(18) -0.0035(18) 0.017(2)
N1 0.0187(18) 0.032(2) 0.0087(16) -0.0003(14) -0.0050(14) 0.0193(16)
N2 0.0109(16) 0.033(2) 0.0095(16) 0.0031(14) -0.0003(13) 0.0141(15)
N3 0.0216(18) 0.030(2) 0.0120(17) -0.0001(15) -0.0016(14) 0.0163(16)
N4 0.029(2) 0.029(2) 0.0150(18) 0.0034(16) -0.0032(16) 0.0089(17)
N5 0.033(2) 0.032(2) 0.021(2) 0.0036(17) -0.0041(17) 0.0140(18)
O1 0.0235(17) 0.0363(18) 0.0205(16) 0.0026(14) 0.0003(13) 0.0105(14)
O2 0.054(2) 0.060(2) 0.031(2) 0.0152(18) 0.0199(18) 0.040(2)
O3 0.137(5) 0.064(3) 0.083(4) 0.044(3) 0.084(4) 0.073(4)
O4 0.0309(19) 0.049(2) 0.043(2) 0.0191(18) -0.0025(16) 0.0215(17)
O5 0.045(3) 0.155(6) 0.048(3) 0.062(3) 0.024(2) 0.031(3)
O6 0.093(4) 0.048(3) 0.058(3) 0.026(2) -0.016(3) 0.012(3)
O7 0.0255(17) 0.045(2) 0.0179(16) 0.0091(14) 0.0051(14) 0.0090(15)
C101 0.033(3) 0.030(3) 0.021(2) 0.0047(19) 0.011(2) 0.014(2)
C102 0.082(5) 0.053(4) 0.047(4) 0.000(3) -0.010(4) 0.039(4)
C103 0.041(3) 0.035(3) 0.021(2) 0.008(2) -0.002(2) 0.017(2)
C104 0.073(5) 0.069(5) 0.048(4) 0.042(4) -0.023(4) -0.012(4)
C105 0.055(4) 0.045(3) 0.055(4) 0.018(3) 0.001(3) 0.028(3)
C106 0.057(4) 0.060(4) 0.070(5) 0.018(4) 0.001(4) 0.026(4)
C107 0.041(4) 0.113(7) 0.059(5) 0.035(4) 0.016(3) 0.028(4)
C108 0.057(4) 0.081(5) 0.061(5) 0.019(4) 0.011(4) 0.003(4)
O101 0.033(2) 0.038(2) 0.060(3) 0.0192(19) 0.0048(19) 0.0098(17)
O102 0.041(2) 0.038(2) 0.053(2) 0.0099(18) -0.0001(19) 0.0125(18)
O103 0.038(2) 0.043(2) 0.0269(18) 0.0176(16) 0.0086(15) 0.0154(17)
O104 0.043(2) 0.055(2) 0.045(2) 0.023(2) -0.0085(19) 0.0130(19)
F101 0.159(6) 0.220(8) 0.099(4) 0.007(5) 0.016(4) 0.166(7)
F102 0.148(5) 0.067(3) 0.074(3) 0.030(2) -0.031(3) 0.045(3)
F103 0.177(7) 0.030(2) 0.180(6) 0.007(3) -0.087(5) 0.030(3)
F104 0.073(3) 0.084(3) 0.072(3) 0.054(2) -0.037(2) -0.028(2)
F105 0.132(6) 0.291(12) 0.076(4) 0.035(6) 0.034(4) -0.108(8)
F106 0.161(7) 0.104(5) 0.286(11) 0.146(7) -0.127(7) -0.041(5)
N101 0.063(4) 0.050(3) 0.082(4) 0.025(3) -0.013(3) 0.018(3)
N102 0.077(6) 0.35(2) 0.072(6) 0.079(9) 0.015(5) 0.027(9)
_geom_special_details
;
All esds (except the esd in the dihedral angle between two l.s. planes)
are estimated using the full covariance matrix. The cell esds are taken
into account individually in the estimation of esds in distances, angles
and torsion angles; correlations between esds in cell parameters are only
used when they are defined by crystal symmetry. An approximate (isotropic)
treatment of cell esds is used for estimating esds involving l.s. planes.
;
loop_
_geom_bond_atom_site_label_1
_geom_bond_atom_site_label_2
_geom_bond_distance
_geom_bond_site_symmetry_2
_geom_bond_publ_flag
C1 N1 1.334(6) . ?
C1 C3 1.367(6) . ?
C1 H1 0.9500 . ?
C2 N1 1.353(6) . ?
C2 C4 1.371(6) . ?
C2 H2 0.9500 . ?
C3 C5 1.394(6) . ?
C3 H3 0.9500 . ?
C4 C5 1.403(6) . ?
C4 H4 0.9500 . ?
C5 C6 1.490(6) . ?
C6 C8 1.390(6) . ?
C6 C7 1.397(6) . ?
C7 C9 1.371(6) . ?
C7 H7 0.9500 . ?
C8 C10 1.375(6) . ?
C8 H8 0.9500 . ?
C9 N2 1.342(6) . ?
C9 H9 0.9500 . ?
C10 N2 1.352(6) . ?
C10 H10 0.9500 . ?
C11 N2 1.497(5) . ?
C11 C12 1.517(6) . ?
C11 H11A 0.9900 . ?
C11 H11B 0.9900 . ?
C12 C14 1.382(6) . ?
C12 C13 1.396(6) . ?
C13 C15 1.384(6) . ?
C13 H13 0.9500 . ?
C14 C16 1.395(6) . ?
C14 H14 0.9500 . ?
C15 C17 1.383(7) . ?
C15 H15 0.9500 . ?
C16 C17 1.400(6) . ?
C16 N3 1.418(6) . ?
C17 C18 1.515(6) . ?
C18 N1 1.505(5) 2 ?
C18 H18A 0.9900 . ?
C18 H18B 0.9900 . ?
C19 N3 1.356(5) . ?
C19 C20 1.357(7) . ?
C19 H19 0.9500 . ?
C20 N5 1.366(6) . ?
C20 C21 1.491(6) . ?
C21 O1 1.435(6) . ?
C21 H21A 0.9900 . ?
C21 H21B 0.9900 . ?
C22 O1 1.429(6) . ?
C22 C23 1.484(8) . ?
C22 H22A 0.9900 . ?
C22 H22B 0.9900 . ?
C23 O2 1.431(7) . ?
C23 H23A 0.9900 . ?
C23 H23B 0.9900 . ?
C24 O2 1.420(7) . ?
C24 C25 1.442(11) . ?
C24 H24A 0.9900 . ?
C24 H24B 0.9900 . ?
C25 O3 1.416(11) . ?
C25 H25A 0.9900 . ?
C25 H25B 0.9900 . ?
C26 C27 1.331(12) . ?
C26 O3 1.481(9) . ?
C26 H26A 0.9900 . ?
C26 H26B 0.9900 . ?
C27 O4 1.605(11) . ?
C27 H27A 0.9900 . ?
C27 H27B 0.9900 . ?
C28 C29 1.252(12) . ?
C28 O4 1.391(8) . ?
C28 H28A 0.9900 . ?
C28 H28B 0.9900 . ?
C29 O5 1.361(11) . ?
C29 H29A 0.9900 . ?
C29 H29B 0.9900 . ?
C30 O5 1.388(12) . ?
C30 C31 1.407(13) . ?
C30 H30A 0.9900 . ?
C30 H30B 0.9900 . ?
C31 O6 1.396(10) . ?
C31 H31A 0.9900 . ?
C31 H31B 0.9900 . ?
C32 C33 1.449(9) . ?
C32 O6 1.464(10) . ?
C32 H32A 0.9900 . ?
C32 H32B 0.9900 . ?
C33 O7 1.453(7) . ?
C33 H33A 0.9900 . ?
C33 H33B 0.9900 . ?
C34 O7 1.362(5) . ?
C34 C35 1.377(7) . ?
C34 C36 1.425(7) . ?
C35 C38 1.404(7) 2 ?
C35 H35 0.9500 . ?
C36 C36 1.417(9) 2 ?
C36 C37 1.417(6) . ?
C37 C38 1.356(7) . ?
C37 H37 0.9500 . ?
C38 C35 1.404(7) 2 ?
C38 H38 0.9500 . ?
N1 C18 1.505(5) 2 ?
N3 N4 1.370(5) . ?
N4 N5 1.300(6) . ?
C101 O101 1.227(6) . ?
C101 O102 1.232(6) . ?
C101 C102 1.518(9) . ?
C102 F103 1.301(9) . ?
C102 F102 1.318(8) . ?
C102 F101 1.324(11) . ?
C103 O104 1.224(6) . ?
C103 O103 1.227(6) . ?
C103 C104 1.512(9) . ?
C104 F106 1.260(10) . ?
C104 F104 1.290(7) . ?
C104 F105 1.341(14) . ?
C105 N101 1.139(9) . ?
C105 C106 1.443(10) . ?
C106 H10A 0.9800 . ?
C106 H10B 0.9800 . ?
C106 H10C 0.9800 . ?
C107 N102 1.118(11) . ?
C107 C108 1.360(11) . ?
C108 H10D 0.9800 . ?
C108 H10E 0.9800 . ?
C108 H10F 0.9800 . ?
loop_
_geom_angle_atom_site_label_1
_geom_angle_atom_site_label_2
_geom_angle_atom_site_label_3
_geom_angle
_geom_angle_site_symmetry_1
_geom_angle_site_symmetry_3
_geom_angle_publ_flag
N1 C1 C3 121.3(4) . . ?
N1 C1 H1 119.3 . . ?
C3 C1 H1 119.3 . . ?
N1 C2 C4 120.4(4) . . ?
N1 C2 H2 119.8 . . ?
C4 C2 H2 119.8 . . ?
C1 C3 C5 120.4(4) . . ?
C1 C3 H3 119.8 . . ?
C5 C3 H3 119.8 . . ?
C2 C4 C5 120.4(4) . . ?
C2 C4 H4 119.8 . . ?
C5 C4 H4 119.8 . . ?
C3 C5 C4 116.9(4) . . ?
C3 C5 C6 121.8(4) . . ?
C4 C5 C6 121.2(4) . . ?
C8 C6 C7 117.6(4) . . ?
C8 C6 C5 122.4(4) . . ?
C7 C6 C5 119.9(4) . . ?
C9 C7 C6 120.1(4) . . ?
C9 C7 H7 120.0 . . ?
C6 C7 H7 120.0 . . ?
C10 C8 C6 120.4(4) . . ?
C10 C8 H8 119.8 . . ?
C6 C8 H8 119.8 . . ?
N2 C9 C7 120.9(4) . . ?
N2 C9 H9 119.5 . . ?
C7 C9 H9 119.5 . . ?
N2 C10 C8 120.3(4) . . ?
N2 C10 H10 119.8 . . ?
C8 C10 H10 119.8 . . ?
N2 C11 C12 107.7(3) . . ?
N2 C11 H11A 110.2 . . ?
C12 C11 H11A 110.2 . . ?
N2 C11 H11B 110.2 . . ?
C12 C11 H11B 110.2 . . ?
H11A C11 H11B 108.5 . . ?
C14 C12 C13 119.9(4) . . ?
C14 C12 C11 120.0(4) . . ?
C13 C12 C11 120.0(4) . . ?
C15 C13 C12 119.2(4) . . ?
C15 C13 H13 120.4 . . ?
C12 C13 H13 120.4 . . ?
C12 C14 C16 119.7(4) . . ?
C12 C14 H14 120.2 . . ?
C16 C14 H14 120.2 . . ?
C17 C15 C13 122.1(4) . . ?
C17 C15 H15 118.9 . . ?
C13 C15 H15 118.9 . . ?
C14 C16 C17 121.1(4) . . ?
C14 C16 N3 117.0(4) . . ?
C17 C16 N3 121.8(4) . . ?
C15 C17 C16 117.5(4) . . ?
C15 C17 C18 119.0(4) . . ?
C16 C17 C18 123.2(4) . . ?
N1 C18 C17 109.0(3) 2 . ?
N1 C18 H18A 109.9 2 . ?
C17 C18 H18A 109.9 . . ?
N1 C18 H18B 109.9 2 . ?
C17 C18 H18B 109.9 . . ?
H18A C18 H18B 108.3 . . ?
N3 C19 C20 105.0(4) . . ?
N3 C19 H19 127.5 . . ?
C20 C19 H19 127.5 . . ?
C19 C20 N5 108.7(4) . . ?
C19 C20 C21 129.7(4) . . ?
N5 C20 C21 121.6(5) . . ?
O1 C21 C20 113.0(4) . . ?
O1 C21 H21A 109.0 . . ?
C20 C21 H21A 109.0 . . ?
O1 C21 H21B 109.0 . . ?
C20 C21 H21B 109.0 . . ?
H21A C21 H21B 107.8 . . ?
O1 C22 C23 109.1(4) . . ?
O1 C22 H22A 109.9 . . ?
C23 C22 H22A 109.9 . . ?
O1 C22 H22B 109.9 . . ?
C23 C22 H22B 109.9 . . ?
H22A C22 H22B 108.3 . . ?
O2 C23 C22 108.0(5) . . ?
O2 C23 H23A 110.1 . . ?
C22 C23 H23A 110.1 . . ?
O2 C23 H23B 110.1 . . ?
C22 C23 H23B 110.1 . . ?
H23A C23 H23B 108.4 . . ?
O2 C24 C25 110.9(6) . . ?
O2 C24 H24A 109.5 . . ?
C25 C24 H24A 109.5 . . ?
O2 C24 H24B 109.5 . . ?
C25 C24 H24B 109.5 . . ?
H24A C24 H24B 108.0 . . ?
O3 C25 C24 112.1(6) . . ?
O3 C25 H25A 109.2 . . ?
C24 C25 H25A 109.2 . . ?
O3 C25 H25B 109.2 . . ?
C24 C25 H25B 109.2 . . ?
H25A C25 H25B 107.9 . . ?
C27 C26 O3 98.8(7) . . ?
C27 C26 H26A 112.0 . . ?
O3 C26 H26A 112.0 . . ?
C27 C26 H26B 112.0 . . ?
O3 C26 H26B 112.0 . . ?
H26A C26 H26B 109.7 . . ?
C26 C27 O4 114.9(7) . . ?
C26 C27 H27A 108.5 . . ?
O4 C27 H27A 108.5 . . ?
C26 C27 H27B 108.5 . . ?
O4 C27 H27B 108.5 . . ?
H27A C27 H27B 107.5 . . ?
C29 C28 O4 124.1(6) . . ?
C29 C28 H28A 106.3 . . ?
O4 C28 H28A 106.3 . . ?
C29 C28 H28B 106.3 . . ?
O4 C28 H28B 106.3 . . ?
H28A C28 H28B 106.4 . . ?
C28 C29 O5 128.7(9) . . ?
C28 C29 H29A 105.1 . . ?
O5 C29 H29A 105.1 . . ?
C28 C29 H29B 105.1 . . ?
O5 C29 H29B 105.1 . . ?
H29A C29 H29B 105.9 . . ?
O5 C30 C31 110.9(6) . . ?
O5 C30 H30A 109.5 . . ?
C31 C30 H30A 109.5 . . ?
O5 C30 H30B 109.5 . . ?
C31 C30 H30B 109.5 . . ?
H30A C30 H30B 108.1 . . ?
O6 C31 C30 111.7(8) . . ?
O6 C31 H31A 109.3 . . ?
C30 C31 H31A 109.3 . . ?
O6 C31 H31B 109.3 . . ?
C30 C31 H31B 109.3 . . ?
H31A C31 H31B 107.9 . . ?
C33 C32 O6 108.1(6) . . ?
C33 C32 H32A 110.1 . . ?
O6 C32 H32A 110.1 . . ?
C33 C32 H32B 110.1 . . ?
O6 C32 H32B 110.1 . . ?
H32A C32 H32B 108.4 . . ?
C32 C33 O7 111.8(5) . . ?
C32 C33 H33A 109.3 . . ?
O7 C33 H33A 109.3 . . ?
C32 C33 H33B 109.3 . . ?
O7 C33 H33B 109.3 . . ?
H33A C33 H33B 107.9 . . ?
O7 C34 C35 124.0(4) . . ?
O7 C34 C36 115.2(4) . . ?
C35 C34 C36 120.8(4) . . ?
C34 C35 C38 119.3(5) . 2 ?
C34 C35 H35 120.4 . . ?
C38 C35 H35 120.4 2 . ?
C36 C36 C37 119.6(5) 2 . ?
C36 C36 C34 118.4(5) 2 . ?
C37 C36 C34 121.9(4) . . ?
C38 C37 C36 119.9(4) . . ?
C38 C37 H37 120.1 . . ?
C36 C37 H37 120.1 . . ?
C37 C38 C35 122.0(4) . 2 ?
C37 C38 H38 119.0 . . ?
C35 C38 H38 119.0 2 . ?
C1 N1 C2 120.4(4) . . ?
C1 N1 C18 120.5(4) . 2 ?
C2 N1 C18 119.1(4) . 2 ?
C9 N2 C10 120.6(4) . . ?
C9 N2 C11 119.3(4) . . ?
C10 N2 C11 119.9(4) . . ?
C19 N3 N4 110.0(4) . . ?
C19 N3 C16 130.4(4) . . ?
N4 N3 C16 119.3(3) . . ?
N5 N4 N3 107.0(3) . . ?
N4 N5 C20 109.4(4) . . ?
C22 O1 C21 112.0(4) . . ?
C24 O2 C23 111.2(5) . . ?
C25 O3 C26 101.1(6) . . ?
C28 O4 C27 123.6(7) . . ?
C29 O5 C30 109.4(11) . . ?
C31 O6 C32 107.8(7) . . ?
C34 O7 C33 116.5(4) . . ?
O101 C101 O102 129.2(5) . . ?
O101 C101 C102 113.1(5) . . ?
O102 C101 C102 117.7(5) . . ?
F103 C102 F102 108.6(7) . . ?
F103 C102 F101 104.9(7) . . ?
F102 C102 F101 105.0(7) . . ?
F103 C102 C101 114.3(6) . . ?
F102 C102 C101 112.4(5) . . ?
F101 C102 C101 111.0(7) . . ?
O104 C103 O103 130.2(5) . . ?
O104 C103 C104 115.4(5) . . ?
O103 C103 C104 114.3(5) . . ?
F106 C104 F104 107.4(7) . . ?
F106 C104 F105 99.1(9) . . ?
F104 C104 F105 105.1(9) . . ?
F106 C104 C103 115.6(8) . . ?
F104 C104 C103 117.1(5) . . ?
F105 C104 C103 110.5(7) . . ?
N101 C105 C106 179.3(9) . . ?
C105 C106 H10A 109.5 . . ?
C105 C106 H10B 109.5 . . ?
H10A C106 H10B 109.5 . . ?
C105 C106 H10C 109.5 . . ?
H10A C106 H10C 109.5 . . ?
H10B C106 H10C 109.5 . . ?
N102 C107 C108 179.3(14) . . ?
C107 C108 H10D 109.5 . . ?
C107 C108 H10E 109.5 . . ?
H10D C108 H10E 109.5 . . ?
C107 C108 H10F 109.5 . . ?
H10D C108 H10F 109.5 . . ?
H10E C108 H10F 109.5 . . ?
loop_
_geom_torsion_atom_site_label_1
_geom_torsion_atom_site_label_2
_geom_torsion_atom_site_label_3
_geom_torsion_atom_site_label_4
_geom_torsion
_geom_torsion_site_symmetry_1
_geom_torsion_site_symmetry_2
_geom_torsion_site_symmetry_3
_geom_torsion_site_symmetry_4
_geom_torsion_publ_flag
N1 C1 C3 C5 0.3(6) . . . . ?
N1 C2 C4 C5 1.1(6) . . . . ?
C1 C3 C5 C4 3.1(6) . . . . ?
C1 C3 C5 C6 -174.6(4) . . . . ?
C2 C4 C5 C3 -3.8(5) . . . . ?
C2 C4 C5 C6 173.9(3) . . . . ?
C3 C5 C6 C8 -168.8(4) . . . . ?
C4 C5 C6 C8 13.6(5) . . . . ?
C3 C5 C6 C7 14.7(5) . . . . ?
C4 C5 C6 C7 -162.9(4) . . . . ?
C8 C6 C7 C9 -0.6(5) . . . . ?
C5 C6 C7 C9 176.0(3) . . . . ?
C7 C6 C8 C10 1.4(5) . . . . ?
C5 C6 C8 C10 -175.2(3) . . . . ?
C6 C7 C9 N2 -0.7(6) . . . . ?
C6 C8 C10 N2 -0.8(6) . . . . ?
N2 C11 C12 C14 88.4(5) . . . . ?
N2 C11 C12 C13 -87.7(5) . . . . ?
C14 C12 C13 C15 -5.7(6) . . . . ?
C11 C12 C13 C15 170.4(4) . . . . ?
C13 C12 C14 C16 4.9(6) . . . . ?
C11 C12 C14 C16 -171.2(4) . . . . ?
C12 C13 C15 C17 0.2(6) . . . . ?
C12 C14 C16 C17 1.4(6) . . . . ?
C12 C14 C16 N3 179.1(3) . . . . ?
C13 C15 C17 C16 5.9(6) . . . . ?
C13 C15 C17 C18 -167.5(4) . . . . ?
C14 C16 C17 C15 -6.7(6) . . . . ?
N3 C16 C17 C15 175.7(4) . . . . ?
C14 C16 C17 C18 166.4(4) . . . . ?
N3 C16 C17 C18 -11.2(6) . . . . ?
C15 C17 C18 N1 83.8(5) . . . 2 ?
C16 C17 C18 N1 -89.3(5) . . . 2 ?
N3 C19 C20 N5 0.2(5) . . . . ?
N3 C19 C20 C21 -179.5(5) . . . . ?
C19 C20 C21 O1 78.8(7) . . . . ?
N5 C20 C21 O1 -100.9(6) . . . . ?
O1 C22 C23 O2 66.1(5) . . . . ?
O2 C24 C25 O3 65.4(6) . . . . ?
O3 C26 C27 O4 -86.2(9) . . . . ?
O4 C28 C29 O5 -7(4) . . . . ?
O5 C30 C31 O6 69.5(9) . . . . ?
O6 C32 C33 O7 -71.5(8) . . . . ?
O7 C34 C35 C38 -177.1(4) . . . 2 ?
C36 C34 C35 C38 1.4(6) . . . 2 ?
O7 C34 C36 C36 177.6(4) . . . 2 ?
C35 C34 C36 C36 -1.1(7) . . . 2 ?
O7 C34 C36 C37 -2.5(6) . . . . ?
C35 C34 C36 C37 178.9(4) . . . . ?
C36 C36 C37 C38 -0.8(7) 2 . . . ?
C34 C36 C37 C38 179.2(4) . . . . ?
C36 C37 C38 C35 0.5(6) . . . 2 ?
C3 C1 N1 C2 -3.1(6) . . . . ?
C3 C1 N1 C18 175.3(4) . . . 2 ?
C4 C2 N1 C1 2.4(6) . . . . ?
C4 C2 N1 C18 -176.0(3) . . . 2 ?
C7 C9 N2 C10 1.4(6) . . . . ?
C7 C9 N2 C11 -173.8(3) . . . . ?
C8 C10 N2 C9 -0.6(6) . . . . ?
C8 C10 N2 C11 174.5(3) . . . . ?
C12 C11 N2 C9 85.7(4) . . . . ?
C12 C11 N2 C10 -89.6(4) . . . . ?
C20 C19 N3 N4 -0.2(5) . . . . ?
C20 C19 N3 C16 -174.0(4) . . . . ?
C14 C16 N3 C19 129.4(5) . . . . ?
C17 C16 N3 C19 -52.9(6) . . . . ?
C14 C16 N3 N4 -43.9(5) . . . . ?
C17 C16 N3 N4 133.8(4) . . . . ?
C19 N3 N4 N5 0.2(5) . . . . ?
C16 N3 N4 N5 174.8(4) . . . . ?
N3 N4 N5 C20 -0.1(5) . . . . ?
C19 C20 N5 N4 -0.1(6) . . . . ?
C21 C20 N5 N4 179.7(4) . . . . ?
C23 C22 O1 C21 -171.9(4) . . . . ?
C20 C21 O1 C22 63.3(6) . . . . ?
C25 C24 O2 C23 170.3(5) . . . . ?
C22 C23 O2 C24 164.7(4) . . . . ?
C24 C25 O3 C26 170.9(5) . . . . ?
C27 C26 O3 C25 177.5(7) . . . . ?
C29 C28 O4 C27 -129(2) . . . . ?
C26 C27 O4 C28 -85.6(11) . . . . ?
C28 C29 O5 C30 136(3) . . . . ?
C31 C30 O5 C29 177.4(11) . . . . ?
C30 C31 O6 C32 178.6(7) . . . . ?
C33 C32 O6 C31 -173.5(6) . . . . ?
C35 C34 O7 C33 2.2(7) . . . . ?
C36 C34 O7 C33 -176.4(5) . . . . ?
C32 C33 O7 C34 169.3(6) . . . . ?
O101 C101 C102 F103 -174.2(7) . . . . ?
O102 C101 C102 F103 6.5(10) . . . . ?
O101 C101 C102 F102 -49.8(9) . . . . ?
O102 C101 C102 F102 130.9(7) . . . . ?
O101 C101 C102 F101 67.5(7) . . . . ?
O102 C101 C102 F101 -111.9(7) . . . . ?
O104 C103 C104 F106 -34.8(12) . . . . ?
O103 C103 C104 F106 149.3(9) . . . . ?
O104 C103 C104 F104 -163.1(8) . . . . ?
O103 C103 C104 F104 21.0(11) . . . . ?
O104 C103 C104 F105 76.7(10) . . . . ?
O103 C103 C104 F105 -99.2(8) . . . . ?
_diffrn_measured_fraction_theta_max 0.961
_diffrn_reflns_theta_full 64.94
_diffrn_measured_fraction_theta_full 0.961
_refine_diff_density_max 1.693
_refine_diff_density_min -0.992
_refine_diff_density_rms 0.109
# start Validation Reply Form
_vrf_PLAT213_I
;
PROBLEM: Atom C5 has ADP max/min Ratio ..... 5.1 oblat
RESPONSE: The atoms of the glycol chain and of the CBPQT4+ ring showed elongated
displacement parameters. Attempts to model this disorder did not significantly
improve the refinement.
;
_vrf_PLAT222_I
;
PROBLEM: Large Non-Solvent H Uiso(max)/Uiso(min) .. 10.0 Ratio
RESPONSE: Hydrogen atoms were refined as riding models with their isotropic
displacement parameters linked to their parent atoms.
In this case the parent atom exhibits disorder with an elongated
displacement parameter and therefore the riding hydrogen atom is also large.
;
_vrf_PLAT241_I
;
PROBLEM: Check High Ueq as Compared to Neighbors for C27
RESPONSE: C27 and C29 are part of the disordered glycol chain, however they
are bonded to C26 and O5 which are relatively well-ordered parts of the
structure.
;
# end Validation Reply Form

4528
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.2.2",
"version": "0.4.5",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -15,14 +15,16 @@
"test": "npm run lint && jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"build-tsc": "tsc",
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/",
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
"build-webpack": "webpack --mode production",
"watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"watch-tsc": "tsc -watch",
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html}\" lib/ --watch",
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
"watch-webpack": "webpack -w --mode development --display minimal",
"serve": "http-server -p 1338",
"model-server": "node lib/servers/model/server.js",
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
"preversion": "npm run test",
"postversion": "git push && git push --tags",
"prepublishOnly": "npm run test && npm run build"
@@ -62,53 +64,54 @@
"license": "MIT",
"devDependencies": {
"benchmark": "^2.1.4",
"circular-dependency-plugin": "^5.0.2",
"concurrently": "^4.1.0",
"circular-dependency-plugin": "^5.2.0",
"concurrently": "^5.0.0",
"cpx": "^1.5.0",
"css-loader": "^2.1.1",
"css-loader": "^3.2.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^3.0.1",
"file-loader": "^4.2.0",
"fs-extra": "^8.1.0",
"graphql-code-generator": "^0.18.2",
"graphql-codegen-time": "^0.18.2",
"graphql-codegen-typescript-template": "^0.18.2",
"jest": "^24.8.0",
"http-server": "^0.11.1",
"jest": "^24.9.0",
"jest-raw-loader": "^1.0.1",
"mini-css-extract-plugin": "^0.7.0",
"node-sass": "^4.12.0",
"raw-loader": "^2.0.0",
"resolve-url-loader": "^3.1.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"ts-jest": "^24.0.2",
"tslint": "^5.17.0",
"typescript": "^3.5.1",
"uglify-js": "^3.6.0",
"util.promisify": "^1.0.0",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2"
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"raw-loader": "^3.1.0",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.0",
"simple-git": "^1.126.0",
"style-loader": "^1.0.0",
"ts-jest": "^24.1.0",
"tslint": "^5.20.1",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@types/argparse": "^1.0.36",
"@types/benchmark": "^1.0.31",
"@types/compression": "0.0.36",
"@types/express": "^4.16.1",
"@types/jest": "^24.0.13",
"@types/node": "^12.0.4",
"@types/node-fetch": "^2.3.4",
"@types/react": "^16.8.19",
"@types/react-dom": "^16.8.4",
"@types/swagger-ui-dist": "3.0.0",
"@types/webgl2": "0.0.4",
"@types/compression": "1.0.1",
"@types/express": "^4.17.2",
"@types/jest": "^24.0.23",
"@types/node": "^12.12.9",
"@types/node-fetch": "^2.5.3",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4",
"@types/swagger-ui-dist": "3.0.3",
"argparse": "^1.0.10",
"compression": "^1.7.4",
"express": "^4.17.1",
"graphql": "^14.3.1",
"graphql": "^14.5.8",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"rxjs": "^6.5.2",
"swagger-ui-dist": "^3.22.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"rxjs": "^6.5.3",
"swagger-ui-dist": "^3.24.3",
"util.promisify": "^1.0.0",
"xhr2": "^0.2.0"
}
}

59
scripts/deploy.js Normal file
View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
const git = require('simple-git')
const path = require('path')
const fs = require("fs")
const fse = require("fs-extra")
const remoteUrl = "https://github.com/molstar/molstar.github.io.git"
const buildDir = path.resolve(__dirname, '../build/')
const deployDir = path.resolve(buildDir, 'deploy/')
const localPath = path.resolve(deployDir, 'molstar.github.io/')
function log(command, stdout, stderr) {
if (command) {
console.log('\n###', command)
stdout.pipe(process.stdout)
stderr.pipe(process.stderr)
}
}
function copyViewer() {
console.log('\n###', 'copy viewer files')
const viewerBuildPath = path.resolve(buildDir, '../build/viewer/')
const viewerDeployPath = path.resolve(localPath, 'viewer/')
fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true })
}
if (!fs.existsSync(localPath)) {
console.log('\n###', 'create localPath')
fs.mkdirSync(localPath, { recursive: true })
}
process.chdir(localPath);
if (!fs.existsSync(path.resolve(localPath, '.git/'))) {
console.log('\n###', 'clone repository')
git()
.outputHandler(log)
.clone(remoteUrl, localPath)
.fetch(['--all'])
.exec(copyViewer)
.add(['-A'])
.commit('updated viewer')
.push()
} else {
console.log('\n###', 'update repository')
git()
.outputHandler(log)
.fetch(['--all'])
.reset(['--hard', 'origin/master'])
.exec(copyViewer)
.add(['-A'])
.commit('updated viewer')
.push()
}

View File

@@ -19,4 +19,12 @@ export class BasicWrapperControls extends PluginUIComponent {
<TransformUpdaterControl nodeRef='ihm-visual' header={{ name: 'I/HM Visual' }} initiallyCollapsed={true} />
</div>;
}
}
export class CustomToastMessage extends PluginUIComponent {
render() {
return <>
Custom <i>Toast</i> content. No timeout.
</>;
}
}

View File

@@ -33,18 +33,18 @@ export namespace StateHelper {
};
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
const query = MS.struct.generator.atomGroups({
const expression = MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.auth_asym_id(), auth_asym_id])
})
return b.apply(StateTransforms.Model.StructureSelection, { query, label: `Chain ${auth_asym_id}` });
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Chain ${auth_asym_id}` });
}
export function select(b: StateBuilder.To<PSO.Molecule.Structure>, query: Expression) {
return b.apply(StateTransforms.Model.StructureSelection, { query });
export function select(b: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression) {
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression });
}
export function selectSurroundingsOfFirstResidue(b: StateBuilder.To<PSO.Molecule.Structure>, comp_id: string, radius: number) {
const query = MS.struct.modifier.includeSurroundings({
const expression = MS.struct.modifier.includeSurroundings({
0: MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), comp_id]),
@@ -53,7 +53,7 @@ export namespace StateHelper {
]),
radius
})
return b.apply(StateTransforms.Model.StructureSelection, { query, label: `Surr. ${comp_id} (${radius} ang)` });
return b.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: `Surr. ${comp_id} (${radius} ang)` });
}
export function identityTransform(b: StateBuilder.To<PSO.Molecule.Structure>, m: Mat4) {
@@ -83,9 +83,9 @@ export namespace StateHelper {
return visualRoot;
}
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, query: Expression, coloring?: BuiltInColorThemeName) {
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, coloring?: BuiltInColorThemeName) {
visualRoot
.apply(StateTransforms.Model.StructureSelection, { query })
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, coloring), { tags: 'het-visual' });
return visualRoot;

View File

@@ -105,10 +105,18 @@
addControl('Apply Stripes', () => BasicMolStarWrapper.coloring.applyStripes());
addHeader('Interactivity');
addControl('Highlight seq_id=7', () => BasicMolStarWrapper.interactivity.highlightOn());
addControl('Clear Highlight', () => BasicMolStarWrapper.interactivity.clearHighlight());
addHeader('Tests');
addControl('Static Superposition', () => BasicMolStarWrapper.tests.staticSuperposition());
addControl('Dynamic Superposition', () => BasicMolStarWrapper.tests.dynamicSuperposition());
addControl('Validation Tooltip', () => BasicMolStarWrapper.tests.toggleValidationTooltip());
addControl('Show Toasts', () => BasicMolStarWrapper.tests.showToasts());
addControl('Hide Toasts', () => BasicMolStarWrapper.tests.hideToasts());
////////////////////////////////////////////////////////

View File

@@ -11,12 +11,16 @@ import { PluginCommands } from '../../mol-plugin/command';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
import { StateBuilder, StateTransform } from '../../mol-state';
import { StripedResidues } from './coloring';
// import { BasicWrapperControls } from './controls';
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
import { CustomToastMessage } from './controls';
import { EmptyLoci } from '../../mol-model/loci';
import { StructureSelection } from '../../mol-model/structure';
import { Script } from '../../mol-script/script';
require('mol-plugin/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
@@ -61,7 +65,7 @@ class BasicWrapper {
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
@@ -142,6 +146,22 @@ class BasicWrapper {
}
}
interactivity = {
highlightOn: () => {
const seq_id = 7;
const data = (this.plugin.state.dataState.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
'group-by': Q.struct.atomProperty.macromolecular.residueKey()
}), data);
const loci = StructureSelection.toLociWithSourceUnits(sel);
this.plugin.interactivity.lociHighlights.highlightOnly({ loci });
},
clearHighlight: () => {
this.plugin.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
tests = {
staticSuperposition: async () => {
const state = this.plugin.state.dataState;
@@ -152,6 +172,28 @@ class BasicWrapper {
dynamicSuperposition: async () => {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
},
toggleValidationTooltip: async () => {
const state = this.plugin.state.behaviorState;
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
},
showToasts: () => {
PluginCommands.Toast.Show.dispatch(this.plugin, {
title: 'Toast 1',
message: 'This is an example text, timeout 3s',
key: 'toast-1',
timeoutMs: 3000
});
PluginCommands.Toast.Show.dispatch(this.plugin, {
title: 'Toast 2',
message: CustomToastMessage,
key: 'toast-2'
});
},
hideToasts: () => {
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-2' });
}
}
}

View File

@@ -86,7 +86,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
const query = compile<StructureSelection>(pivot);
const xs = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure));
const selections = xs.map(s => StructureSelection.toLoci(query(new QueryContext(s.obj!.data))));
const selections = xs.map(s => StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.obj!.data))));
const transforms = superposeStructures(selections);
const visuals = state.build();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -15,12 +15,13 @@ const readFile = util.promisify(fs.readFile)
const writeFile = util.promisify(fs.writeFile)
import { Progress } from '../../mol-task'
import { Database, Table, DatabaseCollection, Column } from '../../mol-data/db'
import { Database, Table, DatabaseCollection } from '../../mol-data/db'
import { CIF } from '../../mol-io/reader/cif'
import { CifWriter } from '../../mol-io/writer/cif'
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd'
import { SetUtils } from '../../mol-util/set'
import { DefaultMap } from '../../mol-util/map'
import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras';
export async function ensureAvailable(path: string, url: string) {
if (FORCE_DOWNLOAD || !fs.existsSync(path)) {
@@ -74,16 +75,6 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>,
type CCB = Table<CCD_Schema['chem_comp_bond']>
type CCA = Table<CCD_Schema['chem_comp_atom']>
const ChemCompBond_Schema = {
comp_id: CCD_Schema['chem_comp_bond'].comp_id,
atom_id_1: CCD_Schema['chem_comp_bond'].atom_id_1,
atom_id_2: CCD_Schema['chem_comp_bond'].atom_id_2,
value_order: CCD_Schema['chem_comp_bond'].value_order,
pdbx_aromatic_flag: CCD_Schema['chem_comp_bond'].pdbx_aromatic_flag,
pdbx_stereo_config: CCD_Schema['chem_comp_bond'].pdbx_stereo_config,
molstar_protonation_variant: Column.Schema.Str()
}
function ccbKey(compId: string, atomId1: string, atomId2: string) {
return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`
}
@@ -202,14 +193,14 @@ async function createBonds() {
}
}
const bondTable = Table.ofArrays(ChemCompBond_Schema, {
const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, {
comp_id, atom_id_1, atom_id_2, value_order,
pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
})
const bondDatabase = Database.ofTables(
TABLE_NAME,
{ chem_comp_bond: ChemCompBond_Schema },
{ chem_comp_bond: mmCIF_chemCompBond_schema },
{ chem_comp_bond: bondTable }
)
@@ -220,12 +211,15 @@ async function run(out: string, binary = false) {
const bonds = await createBonds()
const cif = getEncodedCif(TABLE_NAME, bonds, binary)
if (!fs.existsSync(path.dirname(out))) {
fs.mkdirSync(path.dirname(out));
}
writeFile(out, cif)
}
const TABLE_NAME = 'CHEM_COMP_BONDS'
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'data')
const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data')
const CCD_PATH = path.join(DATA_DIR, 'components.cif')
const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif')
const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'

View File

@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Mol* Lighting Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
position: absolute;
left: 160px;
top: 100px;
width: 600px;
height: 600px;
border: 1px solid #ccc;
}
#controls {
position: absolute;
width: 150px;
top: 100px;
left: 780px;
}
#controls > button {
display: block;
width: 100%;
text-align: left;
margin: 5px 0px;
}
#controls > input, #controls > select {
width: 100%;
display: block;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<script type="text/javascript" src="./index.js"></script>
</head>
<body>
<div id='controls'></div>
<div id="app"></div>
<script>
LightingDemo.init('app')
LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' })
addHeader('Example PDB IDs');
addControl('1M07', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1M07.cif', assemblyId: '1' }));
addControl('6HY0', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6HY0.cif', assemblyId: '1' }));
addControl('6QVK', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/6QVK.cif', assemblyId: '1' }));
addControl('1RB8', () => LightingDemo.load({ url: 'https://files.rcsb.org/download/1RB8.cif', assemblyId: '1' }));
addSeparator()
addHeader('Lighting Presets');
addControl('Illustrative', () => LightingDemo.setPreset('illustrative'));
addControl('Standard', () => LightingDemo.setPreset('standard'));
addControl('Ambient Occlusion', () => LightingDemo.setPreset('occlusion'));
////////////////////////////////////////////////////////
function $(id) { return document.getElementById(id); }
function addControl(label, action) {
var btn = document.createElement('button');
btn.onclick = action;
btn.innerText = label;
$('controls').appendChild(btn);
}
function addSeparator() {
var hr = document.createElement('br');
$('controls').appendChild(hr);
}
function addHeader(header) {
var h = document.createElement('h3');
h.innerText = header;
$('controls').appendChild(h);
}
</script>
</body>
</html>

View File

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

View File

@@ -112,7 +112,7 @@ const state: State = {
function formatParams(def: QueryDefinition) {
const prms = Object.create(null);
for (const p of def.params) {
for (const p of def.jsonParams) {
prms[p.name] = p.exampleValues ? p.exampleValues[0] : void 0;
}
return JSON.stringify(prms, void 0, 2);

View File

@@ -19,7 +19,7 @@ function paramInfo(param: PD.Any, offset: number): string {
case 'conditioned': return getParams(param.conditionParams, offset);
case 'multi-select': return `Array of ${oToS(param.options)}`;
case 'color': return 'Color as 0xrrggbb';
case 'color-scale': return `One of ${oToS(param.options)}`;
case 'color-list': return `One of ${oToS(param.options)}`;
case 'vec3': return `3D vector [x, y, z]`;
case 'file': return `JavaScript File Handle`;
case 'select': return `One of ${oToS(param.options)}`;
@@ -30,7 +30,7 @@ function paramInfo(param: PD.Any, offset: number): string {
case 'line-graph': return `A list of 2d vectors [xi, yi][]`;
case 'object-list': return `Array of\n${paramInfo(PD.Group(param.element), offset + 2)}`;
// TODO: support more languages
case 'script-expression': return `An expression in the specified language { language: 'mol-script', expressiong: string }`;
case 'script': return `An expression in the specified language { language: 'mol-script', expressiong: string }`;
default:
const _: never = param;
console.warn(`${_} has no associated UI component`);

View File

@@ -15,6 +15,7 @@ import { OrderedSet } from '../../mol-data/int';
import { openCif, downloadCif } from './helpers';
import { Vec3 } from '../../mol-math/linear-algebra';
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
import { Sequence } from '../../mol-model/sequence';
async function downloadFromPdb(pdb: string) {
@@ -110,9 +111,10 @@ export function printSequence(model: Model) {
console.log('\nSequence\n=============');
const { byEntityKey } = model.sequence;
for (const key of Object.keys(byEntityKey)) {
const seq = byEntityKey[+key];
console.log(`${seq.entityId} (${seq.sequence.kind} ${seq.num.value(0)} (offset ${seq.sequence.offset}), ${seq.num.value(seq.num.rowCount - 1)}) (${seq.compId.value(0)}, ${seq.compId.value(seq.compId.rowCount - 1)})`);
console.log(`${seq.sequence.sequence}`);
const { sequence, entityId } = byEntityKey[+key];
const { seqId, compId } = sequence
console.log(`${entityId} (${sequence.kind} ${seqId.value(0)} (offset ${sequence.offset}), ${seqId.value(seqId.rowCount - 1)}) (${compId.value(0)}, ${compId.value(compId.rowCount - 1)})`);
console.log(`${Sequence.getSequenceString(sequence)}`);
}
console.log();
}
@@ -146,7 +148,7 @@ export function printRings(structure: Structure) {
export function printUnits(structure: Structure) {
console.log('\nUnits\n=============');
const l = StructureElement.create();
const l = StructureElement.Location.create();
for (const unit of structure.units) {
l.unit = unit;
@@ -159,14 +161,14 @@ export function printUnits(structure: Structure) {
console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`);
const props = StructureProperties.coarse;
const seq = l.unit.model.sequence;
const modelSeq = l.unit.model.sequence;
for (let j = 0, _j = Math.min(size, 3); j < _j; j++) {
l.element = OrderedSet.getAt(elements, j);
const residues: string[] = [];
const start = props.seq_id_begin(l), end = props.seq_id_end(l);
const compId = seq.byEntityKey[props.entityKey(l)].compId.value;
const compId = modelSeq.byEntityKey[props.entityKey(l)].sequence.compId.value;
for (let e = start; e <= end; e++) residues.push(compId(e));
console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`);
}

View File

@@ -0,0 +1,228 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Ludovic Autin <autin@scripps.edu>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Quat, Mat4 } from '../../../../mol-math/linear-algebra';
import { NumberArray } from '../../../../mol-util/type-helpers';
interface Frame {
t: Vec3,
r: Vec3,
s: Vec3,
}
const a0Tmp = Vec3()
const a1Tmp = Vec3()
const a2Tmp = Vec3()
const a3Tmp = Vec3()
function CubicInterpolate(out: Vec3, y0: Vec3, y1: Vec3, y2: Vec3, y3: Vec3, mu: number): Vec3 {
const mu2 = mu * mu;
Vec3.sub(a0Tmp, y3, y2)
Vec3.sub(a0Tmp, a0Tmp, y0)
Vec3.add(a0Tmp, a0Tmp, y1)
Vec3.sub(a1Tmp, y0, y1)
Vec3.sub(a1Tmp, a1Tmp, a0Tmp)
Vec3.sub(a2Tmp, y2, y0)
Vec3.copy(a3Tmp, y1)
out[0] = a0Tmp[0] * mu * mu2 + a1Tmp[0] * mu2 + a2Tmp[0] * mu + a3Tmp[0]
out[1] = a0Tmp[1] * mu * mu2 + a1Tmp[1] * mu2 + a2Tmp[1] * mu + a3Tmp[1]
out[2] = a0Tmp[2] * mu * mu2 + a1Tmp[2] * mu2 + a2Tmp[2] * mu + a3Tmp[2]
return out
}
const cp0 = Vec3()
const cp1 = Vec3()
const cp2 = Vec3()
const cp3 = Vec3()
const currentPosition = Vec3()
function ResampleControlPoints(points: NumberArray, segmentLength: number) {
const nP = points.length / 3
// insert a point at the end and at the begining
// controlPoints.Insert(0, controlPoints[0] + (controlPoints[0] - controlPoints[1]) / 2.0f);
// controlPoints.Add(controlPoints[nP - 1] + (controlPoints[nP - 1] - controlPoints[nP - 2]) / 2.0f);
let resampledControlPoints: Vec3[] = []
// resampledControlPoints.Add(controlPoints[0]);
// resampledControlPoints.Add(controlPoints[1]);
let idx = 1
// const currentPosition = Vec3.create(points[idx * 3], points[idx * 3 + 1], points[idx * 3 + 2])
Vec3.fromArray(currentPosition, points, idx * 3)
let lerpValue = 0.0
// Normalize the distance between control points
while (true) {
if (idx + 2 >= nP) break
Vec3.fromArray(cp0, points, (idx - 1) * 3)
Vec3.fromArray(cp1, points, idx * 3)
Vec3.fromArray(cp2, points, (idx + 1) * 3)
Vec3.fromArray(cp3, points, (idx + 2) * 3)
// const cp0 = Vec3.create(points[(idx-1)*3], points[(idx-1)*3+1], points[(idx-1)*3+2]) // controlPoints[currentPointId - 1];
// const cp1 = Vec3.create(points[idx*3], points[idx*3+1], points[idx*3+2]) // controlPoints[currentPointId];
// const cp2 = Vec3.create(points[(idx+1)*3], points[(idx+1)*3+1], points[(idx+1)*3+2]) // controlPoints[currentPointId + 1];
// const cp3 = Vec3.create(points[(idx+2)*3], points[(idx+2)*3+1], points[(idx+2)*3+2]); // controlPoints[currentPointId + 2];
let found = false
for (; lerpValue <= 1; lerpValue += 0.01) {
// lerp?slerp
// let candidate:Vec3 = Vec3.lerp(Vec3.zero(), cp0, cp1, lerpValue);
// const candidate:Vec3 = Vec3.bezier(Vec3.zero(), cp0, cp1, cp2, cp3, lerpValue);
const candidate = CubicInterpolate(Vec3(), cp0, cp1, cp2, cp3, lerpValue)
const d = Vec3.distance(currentPosition, candidate);
if (d > segmentLength) {
resampledControlPoints.push(candidate)
Vec3.copy(currentPosition, candidate)
found = true
break
}
}
if (!found) {
lerpValue = 0
idx += 1
}
}
return resampledControlPoints
}
const prevV = Vec3()
const tmpV1 = Vec3()
const tmpV2 = Vec3()
const tmpV3 = Vec3()
// easier to align to theses normals
function GetSmoothNormals(points: Vec3[]) {
const nP: number = points.length;
const smoothNormals: Vec3[] = []
if (points.length < 3) {
for (let i = 0; i < points.length; ++i)
smoothNormals.push(Vec3.normalize(Vec3(), points[i]))
return smoothNormals;
}
let p0 = points[0]
let p1 = points[1]
let p2 = points[2]
const p21 = Vec3.sub(tmpV1, p2, p1)
const p01 = Vec3.sub(tmpV2, p0, p1)
const p0121 = Vec3.cross(tmpV3, p01, p21)
Vec3.normalize(prevV, p0121)
smoothNormals.push(Vec3.clone(prevV))
for (let i = 1; i < points.length - 1; ++i) {
p0 = points[i - 1]
p1 = points[i]
p2 = points[i + 1]
const t = Vec3.normalize(tmpV1, Vec3.sub(tmpV1, p2 , p0))
const b = Vec3.normalize(tmpV2, Vec3.cross(tmpV2, t, prevV))
const n = Vec3.normalize(Vec3(), Vec3.cross(tmpV3, t, b))
Vec3.negate(n, n)
Vec3.copy(prevV, n)
smoothNormals.push(n)
}
const last = Vec3()
Vec3.normalize(last, Vec3.cross(last,
Vec3.sub(tmpV1, points[nP - 3], points[nP-2]),
Vec3.sub(tmpV2, points[nP-2] , points[nP-1]))
)
smoothNormals.push(last)
return smoothNormals;
}
const frameTmpV1 = Vec3()
const frameTmpV2 = Vec3()
const frameTmpV3 = Vec3()
function getFrame(reference: Vec3, tangent: Vec3) {
const t = Vec3.normalize(Vec3(), tangent);
// make reference vector orthogonal to tangent
const proj_r_to_t = Vec3.scale(
frameTmpV1, tangent, Vec3.dot(reference, tangent) / Vec3.dot(tangent, tangent)
)
const r = Vec3.normalize(Vec3(), Vec3.sub(frameTmpV2, reference, proj_r_to_t))
// make bitangent vector orthogonal to the others
const s = Vec3.normalize(Vec3(), Vec3.cross(frameTmpV3, t, r))
return { t, r, s }
}
const mfTmpV1 = Vec3()
const mfTmpV2 = Vec3()
const mfTmpV3 = Vec3()
const mfTmpV4 = Vec3()
const mfTmpV5 = Vec3()
const mfTmpV6 = Vec3()
const mfTmpV7 = Vec3()
const mfTmpV8 = Vec3()
const mfTmpV9 = Vec3()
// easier to align to theses normals
// https://github.com/bzamecnik/gpg/blob/master/rotation-minimizing-frame/rmf.py
function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
const frames: Frame[] = [];
const t0 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[1], points[0]))
frames.push(getFrame(normals[0], t0))
for (let i = 0; i< points.length-2; ++i) {
const t2 = Vec3.normalize(mfTmpV1, Vec3.sub(mfTmpV1, points[i+2], points[i+1]))
const v1 = Vec3.sub(mfTmpV2, points[i + 1], points[i]) // this is tangeant
const c1 = Vec3.dot(v1, v1)
// compute r_i^L = R_1 * r_i
const v1r = Vec3.scale(mfTmpV3, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].r))
const ref_L_i = Vec3.sub(mfTmpV4, frames[i].r, v1r)
// compute t_i^L = R_1 * t_i
const v1t = Vec3.scale(mfTmpV5, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].t))
const tan_L_i = Vec3.sub(mfTmpV6, frames[i].t, v1t)
// # compute reflection vector of R_2
const v2 = Vec3.sub(mfTmpV7, t2 , tan_L_i)
const c2 = Vec3.dot(v2, v2)
// compute r_(i+1) = R_2 * r_i^L
const v2l = Vec3.scale(mfTmpV8, v1, (2.0/c2) * Vec3.dot(v2, ref_L_i))
const ref_next = Vec3.sub(mfTmpV9, ref_L_i, v2l) // ref_L_i - (2 / c2) * v2.dot(ref_L_i) * v2
frames.push(getFrame(ref_next, t2)) // frames.append(Frame(ref_next, tangents[i+1]))
}
return frames;
}
const rpTmpVec1 = Vec3()
export function getMatFromResamplePoints(points: NumberArray) {
const segmentLength = 3.4
const new_points = ResampleControlPoints(points, 3.4)
const npoints = new_points.length
const new_normal = GetSmoothNormals(new_points)
const frames = GetMiniFrame(new_points, new_normal)
const limit = npoints
const transforms: Mat4[] = []
const pti = Vec3.copy(rpTmpVec1, new_points[0]);
// console.log(new_points.length)
// console.log(points.length/3)
// console.log(limit)
// console.log(segmentLength)
for (let i = 0; i<npoints-2; ++i) {
const pti1: Vec3 = new_points[i+1] // Vec3.create(points[(i+1)*3],points[(i+1)*3+1],points[(i+1)*3+2]);
const d = Vec3.distance(pti, pti1)
if (d >= segmentLength) {
// use twist or random?
const quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t) // Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),new_normal[i]);//Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),direction);new_normal
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random()*3.60 ) // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
const m = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat)) // Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),quat1,quat2));//Mat4.fromQuat(Mat4.zero(),quat);//Mat4.identity();//Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),rq,quat));
// let pos:Vec3 = Vec3.add(Vec3.zero(),pti1,pti)
// pos = Vec3.scale(pos,pos,1.0/2.0);
// Vec3.makeRotation(Mat4.zero(),Vec3.create(0,0,1),frames[i].t);//
Mat4.setTranslation(m, pti1)
// let m2:Mat4 = GetTubePropertiesMatrix(pti,pti1);
// let q:Quat = Quat.rotationTo(Quat.zero(), Vec3.create(0,1,0),Vec3.create(0,0,1))
// m2=Mat4.mul(Mat4.identity(),Mat4.fromQuat(Mat4.zero(),q),m2);
transforms.push(m)
Vec3.copy(pti, pti1)
}
if (transforms.length >= limit) break
}
return transforms
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Quat } from '../../../../mol-math/linear-algebra';
export interface CellPack {
cell: Cell
packings: CellPacking[]
}
export interface CellPacking {
name: string,
location: 'surface' | 'interior' | 'cytoplasme',
ingredients: Packing['ingredients']
}
//
export interface Cell {
recipe: Recipe
cytoplasme?: Packing
compartments?: { [key: string]: Compartment }
}
export interface Recipe {
setupfile: string
/** First entry is name, secound is path: [name: string, path: string][] */
paths: [string, string][]
version: string
name: string
}
export interface Compartment {
surface?: Packing
interior?: Packing
}
export interface Packing {
ingredients: { [key: string]: Ingredient }
}
export interface Ingredient {
source: IngredientSource
results: [Vec3, Quat][]
name: string
positions?: [Vec3[]] // why wrapped in an extra array?
radii?: [number[]] // why wrapped in an extra array?
/** Number of `curveX` properties in the object where `X` is a 0-indexed number */
nbCurve?: number
/** Curve properties are Vec3[] but that is not expressable in TypeScript */
[curveX: string]: unknown
}
export interface IngredientSource {
pdb: string
transform: { center: boolean, translate?: Vec3 }
biomt?: boolean
}

View File

@@ -0,0 +1,474 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { StateAction } from '../../../../mol-state';
import { PluginContext } from '../../../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Ingredient, CellPacking, Cell } from './data';
import { getFromPdb, getFromCellPackDB } from './util';
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../../../mol-math/geometry';
import { Task } from '../../../../mol-task';
import { StructureRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation';
import { StateTransforms } from '../../../../mol-plugin/state/transforms';
import { distinctColors } from '../../../../mol-util/color/distinct';
import { ModelIndexColorThemeProvider } from '../../../../mol-theme/color/model-index';
import { Hcl } from '../../../../mol-util/color/spaces/hcl';
import { ParseCellPack, StructureFromCellpack, DefaultCellPackBaseUrl } from './state';
import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder';
import { getMatFromResamplePoints } from './curve';
import { compile } from '../../../../mol-script/runtime/query/compiler';
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
import { ColorTheme } from '../../../../mol-theme/color';
import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
import { ModelFormat } from '../../../../mol-model-formats/structure/format';
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
import { Column } from '../../../../mol-data/db';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`
}
async function getModel(id: string, baseUrl: string) {
let model: Model;
if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
// return
const cif = await getFromPdb(id)
model = (await trajectoryFromMmCIF(cif).run())[0]
} else {
const pdb = await getFromCellPackDB(id, baseUrl)
model = (await trajectoryFromPDB(pdb).run())[0]
}
return model
}
async function getStructure(model: Model, props: { assembly?: string } = {}) {
let structure = Structure.ofModel(model)
const { assembly } = props
if (assembly) {
structure = await StructureSymmetry.buildAssembly(structure, assembly).run()
}
const query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
'entity-test': MS.core.rel.eq([MS.ammp('entityType'), 'polymer'])
})
])
const compiled = compile<StructureSelection>(query)
const result = compiled(new QueryContext(structure))
structure = StructureSelection.unionStructure(result)
return structure
}
function getTransform(trans: Vec3, rot: Quat) {
const q: Quat = Quat.create(-rot[3], rot[0], rot[1], rot[2])
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q)
Mat4.transpose(m, m)
Mat4.scale(m, m, Vec3.create(-1.0, 1.0, -1.0))
Mat4.setTranslation(m, trans)
return m
}
function getResultTransforms(results: Ingredient['results']) {
return results.map((r: Ingredient['results'][0]) => getTransform(r[0], r[1]))
}
function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0
const instances: Mat4[] = []
for (let i = 0; i < n; ++i) {
const cname = `curve${i}`
if (!(cname in ingredient)) {
// console.warn(`Expected '${cname}' in ingredient`)
continue
}
const _points = ingredient[cname] as Vec3[]
if (_points.length <= 2) {
// TODO handle curve with 2 or less points
continue
}
const points = new Float32Array(_points.length * 3)
for (let i = 0, il = _points.length; i < il; ++i) Vec3.toArray(_points[i], points, i * 3)
const newInstances = getMatFromResamplePoints(points)
instances.push(...newInstances)
}
return instances
}
function getAssembly(transforms: Mat4[], structure: Structure) {
const builder = Structure.Builder()
const { units } = structure;
for (let i = 0, il = transforms.length; i < il; ++i) {
const id = `${i + 1}`
const op = SymmetryOperator.create(id, transforms[i], { id, operList: [ id ] })
for (const unit of units) {
builder.addWithOperator(unit, op)
}
}
return builder.getStructure();
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
const d = model.sourceData.data.atom_site
const n = d._rowCount
const rowCount = n * transforms.length
const { offsets, count } = model.atomicHierarchy.chainAtomSegments
const x = d.Cartn_x.toArray()
const y = d.Cartn_y.toArray()
const z = d.Cartn_z.toArray()
const Cartn_x = new Float32Array(rowCount)
const Cartn_y = new Float32Array(rowCount)
const Cartn_z = new Float32Array(rowCount)
const map = new Uint32Array(rowCount)
const seq = new Int32Array(rowCount)
let offset = 0
for (let c = 0; c < count; ++c) {
const cStart = offsets[c]
const cEnd = offsets[c + 1]
const cLength = cEnd - cStart
for (let t = 0, tl = transforms.length; t < tl; ++t) {
const m = transforms[t]
for (let j = cStart; j < cEnd; ++j) {
const i = offset + j - cStart
const xj = x[j], yj = y[j], zj = z[j]
Cartn_x[i] = m[0] * xj + m[4] * yj + m[8] * zj + m[12]
Cartn_y[i] = m[1] * xj + m[5] * yj + m[9] * zj + m[13]
Cartn_z[i] = m[2] * xj + m[6] * yj + m[10] * zj + m[14]
map[i] = j
seq[i] = t + 1
}
offset += cLength
}
}
function multColumn<T>(column: Column<T>) {
const array = column.toArray()
return Column.ofLambda({
value: row => array[map[row]],
areValuesEqual: (rowA, rowB) => map[rowA] === map[rowB] || array[map[rowA]] === array[map[rowB]],
rowCount, schema: column.schema
})
}
const _atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
auth_asym_id: CifField.ofColumn(multColumn(d.auth_asym_id)),
auth_atom_id: CifField.ofColumn(multColumn(d.auth_atom_id)),
auth_comp_id: CifField.ofColumn(multColumn(d.auth_comp_id)),
auth_seq_id: CifField.ofNumbers(seq),
B_iso_or_equiv: CifField.ofColumn(Column.ofConst(0, rowCount, Column.Schema.float)),
Cartn_x: CifField.ofNumbers(Cartn_x),
Cartn_y: CifField.ofNumbers(Cartn_y),
Cartn_z: CifField.ofNumbers(Cartn_z),
group_PDB: CifField.ofColumn(Column.ofConst('ATOM', rowCount, Column.Schema.str)),
id: CifField.ofColumn(Column.ofLambda({
value: row => row,
areValuesEqual: (rowA, rowB) => rowA === rowB,
rowCount, schema: d.id.schema,
})),
label_alt_id: CifField.ofColumn(multColumn(d.label_alt_id)),
label_asym_id: CifField.ofColumn(multColumn(d.label_asym_id)),
label_atom_id: CifField.ofColumn(multColumn(d.label_atom_id)),
label_comp_id: CifField.ofColumn(multColumn(d.label_comp_id)),
label_seq_id: CifField.ofNumbers(seq),
label_entity_id: CifField.ofColumn(Column.ofConst('1', rowCount, Column.Schema.str)),
occupancy: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.float)),
type_symbol: CifField.ofColumn(multColumn(d.type_symbol)),
pdbx_PDB_ins_code: CifField.ofColumn(Column.ofConst('', rowCount, Column.Schema.str)),
pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst(1, rowCount, Column.Schema.int)),
}
const categories = {
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
}
return {
header: name,
categoryNames: Object.keys(categories),
categories
};
}
async function getCurve(name: string, transforms: Mat4[], model: Model) {
const cif = getCifCurve(name, transforms, model)
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = ModelFormat.mmCIF(cif)
const models = await _parse_mmCif(format, ctx)
return models[0]
})
const curveModel = await curveModelTask.run()
return getStructure(curveModel)
}
async function getIngredientStructure(ingredient: Ingredient, baseUrl: string) {
const { name, source, results, nbCurve } = ingredient
// TODO can these be added to the library?
if (name === 'HIV1_CAhex_0_1_0') return
if (name === 'HIV1_CAhexCyclophilA_0_1_0') return
if (name === 'iLDL') return
if (name === 'peptides') return
if (name === 'lypoglycane') return
if (source.pdb === 'None') return
const model = await getModel(source.pdb || name, baseUrl)
if (!model) return
if (nbCurve) {
return getCurve(name, getCurveTransforms(ingredient), model)
} else {
const structure = await getStructure(model, { assembly: source.biomt ? '1' : undefined })
return getAssembly(getResultTransforms(results), structure)
}
}
export function createStructureFromCellPack(packing: CellPacking, baseUrl: string) {
return Task.create('Create Packing Structure', async ctx => {
const { ingredients, name } = packing
const structures: Structure[] = []
for (const iName in ingredients) {
if (ctx.shouldUpdate) await ctx.update(iName)
const s = await getIngredientStructure(ingredients[iName], baseUrl)
if (s) structures.push(s)
}
if (ctx.shouldUpdate) await ctx.update(`${name} - units`)
const builder = Structure.Builder({ label: name })
let offsetInvariantId = 0
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`)
let maxInvariantId = 0
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId
if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId
builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId)
}
offsetInvariantId += maxInvariantId
}
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`)
const s = builder.getStructure()
return s
})
}
export const LoadCellPackModel = StateAction.build({
display: { name: 'Load CellPack Model' },
params: {
id: PD.Select('influenza_model1.json', [
['blood_hiv_immature_inside.json', 'blood_hiv_immature_inside'],
['BloodHIV1.0_mixed_fixed_nc1.cpr', 'BloodHIV1.0_mixed_fixed_nc1'],
['HIV-1_0.1.6-8_mixed_radii_pdb.cpr', 'HIV-1_0.1.6-8_mixed_radii_pdb'],
['influenza_model1.json', 'influenza_model1'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
['curveTest', 'Curve Test'],
]),
baseUrl: PD.Text(DefaultCellPackBaseUrl),
preset: PD.Group({
traceOnly: PD.Boolean(false),
representation: PD.Select('spacefill', [
['spacefill', 'Spacefill'],
['gaussian-surface', 'Gaussian Surface'],
['point', 'Point'],
] as ['spacefill' | 'gaussian-surface' | 'point', string][])
}, { isExpanded: true })
},
from: PSO.Root
})(({ state, params }, ctx: PluginContext) => Task.create('CellPack Loader', async taskCtx => {
const url = getCellPackModelUrl(params.id, params.baseUrl)
const root = state.build().toRoot();
let cellPackBuilder: any
if (params.id === 'curveTest') {
const url = `${params.baseUrl}/extras/rna_allpoints.json`
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
const { points } = await (new Response(data)).json() as { points: number[] }
const curve0: Vec3[] = []
for (let j = 0, jl = Math.min(points.length, 3 * 100); j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j))
}
const cell: Cell = {
recipe: { setupfile: '', paths: [], version: '', name: 'Curve Test' },
compartments: {
'CurveCompartment': {
interior: {
ingredients: {
'CurveIngredient': {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
}
}
}
}
}
}
cellPackBuilder = root
.apply(StateTransforms.Data.ImportJson, { data: cell }, { state: { isGhost: true } })
.apply(ParseCellPack)
} else {
cellPackBuilder = root
.apply(StateTransforms.Data.Download, { url, isBinary: false, label: params.id }, { state: { isGhost: true } })
.apply(StateTransforms.Data.ParseJson, undefined, { state: { isGhost: true } })
.apply(ParseCellPack)
}
const cellPackObject = await state.updateTree(cellPackBuilder).runInContext(taskCtx)
const { packings } = cellPackObject.data
const tree = state.build().to(cellPackBuilder.ref);
const isHiv = (
params.id === 'BloodHIV1.0_mixed_fixed_nc1.cpr' ||
params.id === 'HIV-1_0.1.6-8_mixed_radii_pdb.cpr'
)
if (isHiv) {
for (let i = 0, il = packings.length; i < il; ++i) {
if (packings[i].name === 'HIV1_capsid_3j3q_PackInner_0_1_0') {
const url = `${params.baseUrl}/extras/rna_allpoints.json`
const data = await ctx.fetch({ url, type: 'string' }).runInContext(taskCtx);
const { points } = await (new Response(data)).json() as { points: number[] }
const curve0: Vec3[] = []
for (let j = 0, jl = points.length; j < jl; j += 3) {
curve0.push(Vec3.fromArray(Vec3(), points, j))
}
packings[i].ingredients['RNA'] = {
source: { pdb: 'RNA_U_Base.pdb', transform: { center: false } },
results: [],
name: 'RNA',
nbCurve: 1,
curve0
}
}
}
}
const colors = distinctColors(packings.length)
for (let i = 0, il = packings.length; i < il; ++i) {
const hcl = Hcl.fromColor(Hcl(), colors[i])
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number]
const p = { packing: i, baseUrl: params.baseUrl }
let cellpackTree = tree.apply(StructureFromCellpack, p)
if (params.preset.traceOnly) {
const expression = MS.struct.generator.atomGroups({
'atom-test': MS.core.logic.or([
MS.core.rel.eq([MS.ammp('label_atom_id'), 'CA']),
MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
])
})
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } })
}
cellpackTree
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
color: getColorParams(hue)
})
)
}
if (isHiv) {
const url = `${params.baseUrl}/membranes/hiv_lipids.bcif`
tree.apply(StateTransforms.Data.Download, { label: 'hiv_lipids', url, isBinary: true }, { state: { isGhost: true } })
.apply(StateTransforms.Data.ParseCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.TrajectoryFromMmCif, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.ModelFromTrajectory, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
color: UniformColorThemeProvider
})
)
}
console.time('cellpack')
await state.updateTree(tree).runInContext(taskCtx);
console.timeEnd('cellpack')
}));
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
const { representation, traceOnly } = params
switch (representation) {
case 'spacefill':
return traceOnly
? [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ sizeFactor: 2, ignoreHydrogens: true })
] as [any, any]
: [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ ignoreHydrogens: true })
] as [any, any]
case 'gaussian-surface':
return [
ctx.structureRepresentation.registry.get('gaussian-surface'),
() => ({
quality: 'custom', resolution: 10, radiusOffset: 2,
alpha: 1.0, flatShaded: false, doubleSided: false,
ignoreHydrogens: true
})
] as [any, any]
case 'point':
return [
ctx.structureRepresentation.registry.get('point'),
() => ({ ignoreHydrogens: true })
] as [any, any]
}
}
function getColorParams(hue: [number, number]) {
return [
ModelIndexColorThemeProvider,
(c: ColorTheme.Provider<any>, ctx: ThemeRegistryContext) => {
return {
palette: {
name: 'generate',
params: {
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800,
maxCount: 75
}
}
}
}
] as [any, any]
}

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin/state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Task } from '../../../../mol-task';
import { CellPack as _CellPack, Cell, CellPacking } from './data';
import { createStructureFromCellPack } from './model';
// export const DefaultCellPackBaseUrl = 'https://cdn.jsdelivr.net/gh/mesoscope/cellPACK_data@master/cellPACK_database_1.1.0/'
export const DefaultCellPackBaseUrl = 'https://mgldev.scripps.edu/projects/autoPACK/web/cellpackproject/'
export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
export { ParseCellPack }
type ParseCellPack = typeof ParseCellPack
const ParseCellPack = PluginStateTransform.BuiltIn({
name: 'parse-cellpack',
display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
from: PSO.Format.Json,
to: CellPack
})({
apply({ a }) {
return Task.create('Parse CellPack', async ctx => {
const cell = a.data as Cell
const packings: CellPacking[] = []
const { compartments, cytoplasme } = cell
if (compartments) {
for (const name in compartments) {
const { surface, interior } = compartments[name]
if (surface) packings.push({ name, location: 'surface', ingredients: surface.ingredients })
if (interior) packings.push({ name, location: 'interior', ingredients: interior.ingredients })
}
}
if (cytoplasme) packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients })
return new CellPack({ cell, packings });
});
}
});
export { StructureFromCellpack }
type StructureFromCellpack = typeof ParseCellPack
const StructureFromCellpack = PluginStateTransform.BuiltIn({
name: 'structure-from-cellpack',
display: { name: 'Structure from CellPack', description: 'Create Structure from CellPack Packing' },
from: CellPack,
to: PSO.Molecule.Structure,
params: a => {
if (!a) {
return {
packing: PD.Numeric(0, {}, { description: 'Packing Index' }),
baseUrl: PD.Text(DefaultCellPackBaseUrl)
};
}
const options = a.data.packings.map((d, i) => [i, d.name] as [number, string])
return {
packing: PD.Select(0, options),
baseUrl: PD.Text(DefaultCellPackBaseUrl)
}
}
})({
apply({ a, params }) {
return Task.create('Structure from CellPack', async ctx => {
const packing = a.data.packings[params.packing]
const structure = await createStructureFromCellPack(packing, params.baseUrl).runInContext(ctx)
return new PSO.Molecule.Structure(structure, { label: packing.name })
});
}
});

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CIF } from '../../../../mol-io/reader/cif'
import { parsePDB } from '../../../../mol-io/reader/pdb/parser';
async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data);
const parsed = await comp.run();
if (parsed.isError) throw parsed;
return parsed.result;
}
async function parsePDBfile(data: string, id: string) {
const comp = parsePDB(data, id);
const parsed = await comp.run();
if (parsed.isError) throw parsed;
return parsed.result;
}
async function downloadCif(url: string, isBinary: boolean) {
const data = await fetch(url);
return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
}
async function downloadPDB(url: string, id: string) {
const data = await fetch(url);
return parsePDBfile(await data.text(), id);
}
export async function getFromPdb(id: string) {
const parsed = await downloadCif(`https://files.rcsb.org/download/${id}.cif`, false);
return parsed.blocks[0];
}
function getCellPackDataUrl(id: string, baseUrl: string) {
const url = `${baseUrl}/other/${id}`
return url.endsWith('.pdb') ? url : `${url}.pdb`
}
export async function getFromCellPackDB(id: string, baseUrl: string) {
const name = id.endsWith('.pdb') ? id.substring(0, id.length - 4) : id
const parsed = await downloadPDB(getCellPackDataUrl(id, baseUrl), name);
return parsed;
}

View File

@@ -16,7 +16,7 @@ import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots'
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
import { Text } from '../../../mol-geo/geometry/text/text';
import { UUID } from '../../../mol-util';
import { ColorNames } from '../../../mol-util/color/tables';
import { ColorNames } from '../../../mol-util/color/names';
import { Camera } from '../../../mol-canvas3d/camera';
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
@@ -93,32 +93,32 @@ function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, struc
let i = 0;
for (const l of params.e.labels) {
const query = createQuery([l.i_atom]);
const expression = createExpression([l.i_atom]);
const group = b.to(template.structure)
.group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
group
.apply(StateTransforms.Model.StructureSelection, { query, label: 'Atom' })
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
.apply(StateTransforms.Representation.StructureLabels3D, {
target: { name: 'static-text', params: { value: l.text || '' } },
options: labelOptions
});
group
.apply(StateTransforms.Model.StructureSelection, { query: MS.struct.modifier.wholeResidues([query]), label: 'Residue' })
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
}
if (params.e.selected && params.e.selected.length > 0) {
b.to(template.structure)
.apply(StateTransforms.Model.StructureSelection, { query: createQuery(params.e.selected), label: `Selected` })
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
}
// TODO
// for (const l of params.e.distances) {
// b.to('structure')
// .apply(StateTransforms.Model.StructureSelection, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
// .apply(StateTransforms.Representation.StructureLabels3D, {
// target: { name: 'static-text', params: { value: l. || '' } },
// options: labelOptions
@@ -138,28 +138,24 @@ function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, struc
}
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
const direction = Vec3.sub(Vec3.zero(), e.pos, e.in);
const direction = Vec3.sub(Vec3(), e.pos, e.in);
Vec3.normalize(direction, direction);
const up = Vec3.sub(Vec3.zero(), e.pos, e.up);
const up = Vec3.sub(Vec3(), e.pos, e.up);
Vec3.normalize(up, up);
const s: Camera.Snapshot = {
mode: 'perspective',
position: Vec3.scaleAndAdd(Vec3.zero(), e.pos, direction, e.slab.zoom),
target: e.pos,
direction,
up,
near: e.slab.zoom + e.slab.z_front,
far: e.slab.zoom + e.slab.z_back,
fogNear: e.slab.zoom + e.slab.z_front,
fogFar: e.slab.zoom + e.slab.z_back,
fov: Math.PI / 4,
zoom: 1
position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
target: e.pos,
radius: (e.slab.z_back - e.slab.z_front) / 2,
fog: 50,
up,
};
return s;
}
function createQuery(atomIndices: number[]) {
function createExpression(atomIndices: number[]) {
if (atomIndices.length === 0) return MS.struct.generator.empty();
return MS.struct.generator.atomGroups({

BIN
src/apps/viewer/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<title>Mol* Viewer</title>
<style>
* {

View File

@@ -2,14 +2,18 @@
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import './favicon.ico'
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/command';
import { PluginSpec } from '../../mol-plugin/spec';
import { CreateJoleculeState } from './extensions/jolecule';
import { LoadCellPackModel } from './extensions/cellpack/model';
import { StructureFromCellpack } from './extensions/cellpack/state';
require('mol-plugin/skin/light.scss')
function getParam(name: string, regex: string): string {
@@ -21,7 +25,12 @@ const hideControls = getParam('hide-controls', `[^&]+`) === '1';
function init() {
const spec: PluginSpec = {
actions: [...DefaultPluginSpec.actions, PluginSpec.Action(CreateJoleculeState)],
actions: [
...DefaultPluginSpec.actions,
PluginSpec.Action(CreateJoleculeState),
PluginSpec.Action(LoadCellPackModel),
PluginSpec.Action(StructureFromCellpack),
],
behaviors: [...DefaultPluginSpec.behaviors],
animations: [...DefaultPluginSpec.animations || []],
customParamEditors: DefaultPluginSpec.customParamEditors,
@@ -29,6 +38,9 @@ function init() {
initial: {
isExpanded: true,
showControls: !hideControls
},
controls: {
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
}
}
};

View File

@@ -27,7 +27,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
name: 'proteopedia-wrapper-evolutionary-conservation',
display: 'Evolutionary Conservation',
async getData(model: Model) {
const id = model.label.toLowerCase();
const id = model.entryId.toLowerCase();
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
const json = await req.json();
const annotations = (json && json.residueAnnotations) || [];

View File

@@ -1,3 +1,18 @@
== v3.4 ==
* Fixed HET group reset.
* Updated core.
* Removed Camera Cliping.
== v3.3 ==
* Camera Clipping.
== v3.2 ==
* Fixed assembly loading.
* Better HET group focus.
== v3.0 ==
* Fixed initial camera zoom.

View File

@@ -54,7 +54,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
if (ctx.structure) {
const l = StructureElement.create()
const l = StructureElement.Location.create()
const { models } = ctx.structure
const asymIdSerialMap = new Map<string, number>()
for (let i = 0, il = models.length; i < il; ++i) {
@@ -67,7 +67,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
}
color = (location: Location): Color => {
if (StructureElement.isLocation(location)) {
if (StructureElement.Location.is(location)) {
const asym_id = getAsymId(location.unit);
const o = asymIdSerialMap.get(asym_id(location)) || 0;
return colors[o % colorCount].color;

View File

@@ -18,9 +18,9 @@ export interface ModelInfo {
export namespace ModelInfo {
async function getPreferredAssembly(ctx: PluginContext, model: Model) {
if (model.label.length <= 3) return void 0;
if (model.entryId.length <= 3) return void 0;
try {
const id = model.label.toLowerCase();
const id = model.entryId.toLowerCase();
const src = await ctx.runTask(ctx.fetch({ url: `https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary/${id}` })) as string;
const json = JSON.parse(src);
const data = json && json[id];
@@ -105,6 +105,8 @@ export enum StateElements {
ModelProps = 'model-props',
Assembly = 'assembly',
VolumeStreaming = 'volume-streaming',
Sequence = 'sequence',
SequenceVisual = 'sequence-visual',
Het = 'het',
@@ -113,5 +115,6 @@ export enum StateElements {
Water = 'water',
WaterVisual = 'water-visual',
HetGroupFocus = 'het-group-focus'
HetGroupFocus = 'het-group-focus',
HetGroupFocusGroup = 'het-group-focus-group'
}

View File

@@ -40,6 +40,13 @@
width: 100%;
display: block;
}
#volume-streaming-wrapper {
position: absolute;
top: 100px;
left: 780px;
width: 300px;
}
</style>
<link rel="stylesheet" type="text/css" href="app.css" />
<script type="text/javascript" src="./index.js"></script>
@@ -55,6 +62,7 @@
</select>
</div>
<div id="app"></div>
<div id="volume-streaming-wrapper"></div>
<script>
// it might be a good idea to define these colors in a separate script file
var CustomColors = [0x00ff00, 0x0000ff];
@@ -66,7 +74,7 @@
function $(id) { return document.getElementById(id); }
var pdbId = '1eve', assemblyId= 'preferred';
var pdbId = '1cbs', assemblyId= 'preferred';
var url = 'https://www.ebi.ac.uk/pdbe/static/entry/' + pdbId + '_updated.cif';
var format = 'cif';
@@ -122,7 +130,11 @@
addSeparator();
addHeader('Camera');
addControl('Toggle Spin', () => PluginWrapper.toggleSpin());
addControl('Reset Position', () => PluginWrapper.camera.resetPosition());
addControl('Toggle Spin', () => PluginWrapper.camera.toggleSpin());
// Same as "wheel icon" and Viewport options
// addControl('Clip', () => PluginWrapper.viewport.setSettings({ clip: [33, 66] }));
// addControl('Reset Clip', () => PluginWrapper.viewport.setSettings({ clip: [1, 100] }));
addSeparator();
@@ -141,7 +153,8 @@
addSeparator();
addHeader('Misc');
addControl('Apply Evo Cons', () => PluginWrapper.coloring.evolutionaryConservation());
addControl('Apply Evo Cons Style', () => PluginWrapper.coloring.evolutionaryConservation());
addControl('Apply Evo Cons Colors', () => PluginWrapper.coloring.evolutionaryConservation({ sequence: true, het: false, keepStyle: true }));
addControl('Default Visuals', () => PluginWrapper.updateStyle());
addSeparator();
@@ -151,6 +164,11 @@
addHetGroupsContainer();
addSeparator();
addHeader('Exp. Data');
addControl('Init', () => PluginWrapper.experimentalData.init($('volume-streaming-wrapper')));
addControl('Remove', () => PluginWrapper.experimentalData.remove());
addSeparator();
addHeader('State');
var snapshot;

View File

@@ -4,6 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as ReactDOM from 'react-dom';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import { PluginContext } from '../../mol-plugin/context';
@@ -13,11 +14,11 @@ import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transfo
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
import { StateBuilder, StateObject } from '../../mol-state';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { EvolutionaryConservation } from './annotation';
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { ControlsWrapper } from './ui/controls';
import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
import { PluginState } from '../../mol-plugin/state';
import { Scheduler } from '../../mol-task';
import { createProteopediaCustomTheme } from './coloring';
@@ -25,7 +26,9 @@ import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
import { BuiltInColorThemes } from '../../mol-theme/color';
import { BuiltInSizeThemes } from '../../mol-theme/size';
import { ColorNames } from '../../mol-util/color/tables';
import { ColorNames } from '../../mol-util/color/names';
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
// import { Vec3 } from 'mol-math/linear-algebra';
// import { ParamDefinition } from 'mol-util/param-definition';
// import { Text } from 'mol-geo/geometry/text/text';
@@ -33,7 +36,7 @@ require('../../mol-plugin/skin/light.scss')
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 3;
static VERSION_MINOR = 1;
static VERSION_MINOR = 4;
private _ev = RxEventHelper.create();
@@ -78,7 +81,7 @@ class MolStarProteopediaWrapper {
return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
}
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats) {
const parsed = format === 'cif'
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
@@ -187,7 +190,7 @@ class MolStarProteopediaWrapper {
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = '', representationStyle }: LoadParams) {
async load({ url, format = 'cif', assemblyId = 'deposited', representationStyle }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.dataState;
@@ -200,14 +203,17 @@ class MolStarProteopediaWrapper {
if (loadType === 'full') {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
const modelTree = this.model(this.download(state.build().toRoot(), url), format, assemblyId);
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
await this.applyState(modelTree);
const info = await this.doInfo(true);
const structureTree = this.structure((assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
const structureTree = this.structure(asmId);
await this.applyState(structureTree);
} else {
const tree = state.build();
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
const info = await this.doInfo(true);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: asmId }));
await this.applyState(tree);
}
@@ -235,6 +241,38 @@ class MolStarProteopediaWrapper {
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
}
viewport = {
setSettings: (settings?: Canvas3DProps) => {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
settings: settings || DefaultCanvas3DParams
});
}
};
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset.dispatch(this.plugin, { }),
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
// if (!options) {
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
// settings: {
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
// clip: DefaultCanvas3DParams.clip
// }
// });
// return;
// }
// options = options || { };
// const props = this.plugin.canvas3d.props;
// const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
// const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
// });
// }
}
animate = {
modelIndex: {
maxFPS: 8,
@@ -247,53 +285,85 @@ class MolStarProteopediaWrapper {
}
coloring = {
evolutionaryConservation: async () => {
await this.updateStyle({ sequence: { kind: 'spacefill' } }, true);
evolutionaryConservation: async (params?: { sequence?: boolean, het?: boolean, keepStyle?: boolean }) => {
if (!params || !params.keepStyle) {
await this.updateStyle({ sequence: { kind: 'spacefill' } }, true);
}
const state = this.state;
// const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
// for (const v of visuals) {
// }
const tree = state.build();
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
// for (const v of visuals) {
// }
if (!params || !!params.sequence) {
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
}
if (params && !!params.het) {
tree.to(StateElements.HetVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
}
}
private experimentalDataElement?: Element = void 0;
experimentalData = {
init: async (parent: Element) => {
const asm = this.state.select(StateElements.Assembly)[0].obj!;
const params = InitVolumeStreaming.createDefaultParams(asm, this.plugin);
params.options.behaviorRef = StateElements.VolumeStreaming;
params.defaultView = 'box';
params.options.channelParams['fo-fc(+ve)'] = { wireframe: true };
params.options.channelParams['fo-fc(-ve)'] = { wireframe: true };
await this.plugin.runTask(this.state.applyAction(InitVolumeStreaming, params, StateElements.Assembly));
this.experimentalDataElement = parent;
volumeStreamingControls(this.plugin, parent);
},
remove: () => {
const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
if (!r) return;
PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
if (this.experimentalDataElement) {
ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
this.experimentalDataElement = void 0;
}
}
}
hetGroups = {
reset: () => {
const update = this.state.build().delete(StateElements.HetGroupFocus);
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
},
focusFirst: async (resn: string) => {
focusFirst: async (compId: string) => {
if (!this.state.transforms.has(StateElements.Assembly)) return;
await PluginCommands.Camera.Reset.dispatch(this.plugin, { });
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
const update = this.state.build();
update.delete(StateElements.HetGroupFocus);
update.delete(StateElements.HetGroupFocusGroup);
const surroundings = MS.struct.modifier.includeSurroundings({
0: MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), resn]),
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
})
]),
radius: 5,
'as-whole-residues': true
});
const core = MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([MS.struct.atomProperty.macromolecular.label_comp_id(), compId]),
'group-by': MS.core.str.concat([MS.struct.atomProperty.core.operatorName(), MS.struct.atomProperty.macromolecular.residueKey()])
})
]);
const surroundings = MS.struct.modifier.includeSurroundings({ 0: core, radius: 5, 'as-whole-residues': true });
const sel = update.to(StateElements.Assembly)
.apply(StateTransforms.Model.StructureSelection, { label: resn, query: surroundings }, { ref: StateElements.HetGroupFocus });
const group = update.to(StateElements.Assembly).group(StateTransforms.Misc.CreateGroup, { label: compId }, { ref: StateElements.HetGroupFocusGroup });
sel.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Core', expression: core }, { ref: StateElements.HetGroupFocus })
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createCoreVisualParams());
group.apply(StateTransforms.Model.StructureSelectionFromExpression, { label: 'Surroundings', expression: surroundings })
.apply(StateTransforms.Representation.StructureRepresentation3D, this.createSurVisualParams());
// sel.apply(StateTransforms.Representation.StructureLabels3D, {
// target: { name: 'residues', params: { } },
// options: {
@@ -313,7 +383,7 @@ class MolStarProteopediaWrapper {
// const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
// Vec3.normalize(position, position);
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
const snapshot = this.plugin.canvas3d.camera.getFocus(sphere.center, 0.75 * sphere.radius);
const snapshot = this.plugin.canvas3d.camera.getFocus(sphere.center, Math.max(sphere.radius, 5));
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
}
}
@@ -327,6 +397,15 @@ class MolStarProteopediaWrapper {
});
}
private createCoreVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
repr: BuiltInStructureRepresentations['ball-and-stick'],
// color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
// size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
});
}
snapshot = {
get: () => {
return this.plugin.state.getSnapshot();

View File

@@ -5,10 +5,14 @@
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PluginUIComponent } from '../../../mol-plugin/ui/base';
import { CurrentObject } from '../../../mol-plugin/ui/plugin';
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
import { PluginContext } from '../../../mol-plugin/context';
import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
import { StateElements } from '../helpers';
export class ControlsWrapper extends PluginUIComponent {
render() {
@@ -18,4 +22,11 @@ export class ControlsWrapper extends PluginUIComponent {
<CameraSnapshots />
</div>;
}
}
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={plugin}>
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />
</PluginContextContainer>,
parent);
}

View File

@@ -28,9 +28,9 @@ function messageTree(root: Progress.Node, prefix = ''): string {
function createTask<T>(delayMs: number, r: T): Task<T> {
return Task.create('delayed value ' + r, async ctx => {
ctx.update('Processing delayed... ' + r, true);
ctx.update(`Processing delayed ${r} after ${delayMs}ms`, true);
await Scheduler.delay(delayMs);
if (ctx.shouldUpdate) await ctx.update({ message: 'hello from delayed... ' });
if (ctx.shouldUpdate) await ctx.update({ message: `hello from delayed ${r} ${delayMs}` });
return r;
}, () => console.log('On abort called ' + r));
}
@@ -63,7 +63,7 @@ export function testTree() {
const r = await c1 + await c2 + await c3;
if (ctx.shouldUpdate) await ctx.update({ message: 'Almost done...' });
return r + 1;
});
}, () => console.log('On abort O'));
}
export type ChunkedState = { i: number, current: number, total: number }
@@ -115,7 +115,7 @@ async function test() {
// const r = await Run(testTree(), abortingObserver, 250);
// console.log(r);
const m = await ms({ i: 10 }).run(logP);
const m = await testTree().run(abortingObserver, 50);
console.log(m);
} catch (e) {
console.error(e);

View File

@@ -1,56 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ColorTheme } from '../../mol-theme/color';
import { Color } from '../../mol-util/color';
export interface ColorThemeComponentProps {
colorTheme: ColorTheme<any>
}
export interface ColorThemeComponentState {
}
export class ColorThemeComponent extends React.Component<ColorThemeComponentProps, ColorThemeComponentState> {
state = {
}
render() {
const ct = this.props.colorTheme
return <div>
<span>Color Theme Info </span>
{ct.description ? <div><i>{ct.description}</i></div> : ''}
{
ct.legend && ct.legend.kind === 'scale-legend'
? <div
style={{
width: '100%',
height: '30px',
background: `linear-gradient(to right, ${ct.legend.colors.map(c => Color.toStyle(c)).join(', ')})`
}}
>
<span style={{float: 'left', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.minLabel}</span>
<span style={{float: 'right', padding: '6px', color: 'white', fontWeight: 'bold', backgroundColor: 'rgba(0, 0, 0, 0.2)'}}>{ct.legend.maxLabel}</span>
</div>
: ct.legend && ct.legend.kind === 'table-legend'
? <div>
{ct.legend.table.map((value, i) => {
const [name, color] = value
return <div key={i} style={{minWidth: '60px', marginRight: '5px', display: 'inline-block'}}>
<div style={{width: '30px', height: '20px', backgroundColor: Color.toStyle(color), display: 'inline-block'}}></div>
{name}
</div>
})}
</div>
: ''
}
</div>;
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export interface BooleanParamComponentProps {
label: string
param: PD.Boolean
value: boolean
onChange(v: boolean): void
}
export interface BooleanParamComponentState {
value: boolean
}
export class BooleanParamComponent extends React.Component<BooleanParamComponentProps, BooleanParamComponentState> {
state = {
value: this.props.value
}
onChange(value: boolean) {
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<button onClick={e => this.onChange(!this.state.value) }>
{this.state.value ? 'Off' : 'On'}
</button>
</div>;
}
}

View File

@@ -1,43 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { ColorNames } from '../../../mol-util/color/tables';
import { Color } from '../../../mol-util/color';
export interface ColorParamComponentProps {
label: string
param: PD.Color
value: Color
onChange(v: Color): void
}
export interface ColorParamComponentState {
value: Color
}
export class ColorParamComponent extends React.Component<ColorParamComponentProps, ColorParamComponentState> {
state = {
value: this.props.value
}
onChange(value: Color) {
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<select value={this.state.value} onChange={e => this.onChange(Color(parseInt(e.target.value))) }>
{Object.keys(ColorNames).map(name => {
return <option key={name} value={(ColorNames as { [k: string]: Color})[name]}>{name}</option>
})}
</select>
</div>;
}
}

View File

@@ -1,45 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export interface MultiSelectParamComponentProps<T extends string> {
label: string
param: PD.MultiSelect<T>
value: T[]
onChange(v: T[]): void
}
export interface MultiSelectParamComponentState<T extends string> {
value: T[]
}
export class MultiSelectParamComponent<T extends string> extends React.Component<MultiSelectParamComponentProps<T>, MultiSelectParamComponentState<T>> {
state = {
value: this.props.value
}
onChange(value: T[]) {
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<select multiple value={this.state.value} onChange={e => {
const value = Array.from(e.target.options).filter(option => option.selected).map(option => option.value)
this.onChange(value as T[])
}}>
{this.props.param.options.map(v => {
const [value, label] = v
return <option key={label} value={value}>{label}</option>
})}
</select>
</div>;
}
}

View File

@@ -1,45 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export interface NumberParamComponentProps {
label: string
param: PD.Numeric
value: number
onChange(v: number): void
}
export interface NumberParamComponentState {
value: number
}
export class NumberParamComponent extends React.Component<NumberParamComponentProps, NumberParamComponentState> {
state = {
value: this.props.value
}
onChange(valueStr: string) {
const value = this.props.param.step && Number.isInteger(this.props.param.step) ? parseInt(valueStr) : parseFloat(valueStr)
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<input type='range'
value={this.state.value}
min={this.props.param.min}
max={this.props.param.max}
step={this.props.param.step}
onChange={e => this.onChange(e.currentTarget.value)}
>
</input>
</div>;
}
}

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export interface SelectParamComponentProps<T extends string> {
label: string
param: PD.Select<T>
value: T
onChange(v: T): void
}
export interface SelectParamComponentState<T extends string> {
value: T
}
export class SelectParamComponent<T extends string> extends React.Component<SelectParamComponentProps<T>, SelectParamComponentState<T>> {
state = {
value: this.props.value
}
onChange(value: T) {
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<select value={this.state.value} onChange={e => this.onChange(e.target.value as T) }>
{this.props.param.options.map(v => {
const [value, label] = v
return <option key={label} value={value}>{label}</option>
})}
</select>
</div>;
}
}

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
export interface TextParamComponentProps {
label: string
param: PD.Text
value: string
onChange(v: string): void
}
export interface TextParamComponentState {
value: string
}
export class TextParamComponent extends React.Component<TextParamComponentProps, TextParamComponentState> {
state = {
value: this.props.value
}
onChange(value: string) {
this.setState({ value })
this.props.onChange(value)
}
render() {
return <div>
<span>{this.props.label} </span>
<input type='text'
value={this.state.value}
onChange={e => this.onChange(e.currentTarget.value)}
>
</input>
</div>;
}
}

View File

@@ -1,64 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as React from 'react'
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { BooleanParamComponent } from './parameter/boolean';
import { NumberParamComponent } from './parameter/number';
import { SelectParamComponent } from './parameter/select';
import { MultiSelectParamComponent } from './parameter/multi-select';
import { TextParamComponent } from './parameter/text';
import { ColorParamComponent } from './parameter/color';
import { camelCaseToWords } from '../../mol-util/string';
interface ParametersProps<P extends PD.Params> {
params: P
values: { [k in keyof P]: P[k]['defaultValue'] }
onChange<K extends keyof P>(k: K, v: P[K]['defaultValue']): void
}
type ParametersState = {}
function getParamComponent<P extends PD.Any>(label: string, param: PD.Any, value: P['defaultValue'], onChange: (v: P['defaultValue']) => void) {
switch (param.type) {
case 'boolean':
return <BooleanParamComponent label={label} param={param} value={value} onChange={onChange} />
case 'number':
return <NumberParamComponent label={label} param={param} value={value} onChange={onChange} />
case 'select':
return <SelectParamComponent label={label} param={param} value={value} onChange={onChange} />
case 'multi-select':
return <MultiSelectParamComponent label={label} param={param} value={value} onChange={onChange} />
case 'text':
return <TextParamComponent label={label} param={param} value={value} onChange={onChange} />
case 'color':
return <ColorParamComponent label={label} param={param} value={value} onChange={onChange} />
}
return ''
}
function getLabel(name: string, param: PD.Base<any>) {
return param.label === undefined ? camelCaseToWords(name) : param.label
}
export class ParametersComponent<P extends PD.Params> extends React.Component<ParametersProps<P>, ParametersState> {
onChange(k: string, value: any) {
this.props.onChange(k, value)
}
render() {
return <div style={{ width: '100%' }}>
{ Object.keys(this.props.params).map(k => {
const param = this.props.params[k]
const value = this.props.values[k]
const label = getLabel(k, param)
return <div key={k}>
{getParamComponent(label, param, value, v => this.onChange(k, v))}
</div>
})}
</div>;
}
}

View File

@@ -1,81 +0,0 @@
// /**
// * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
// *
// * @author David Sehnal <david.sehnal@gmail.com>
// */
// import * as React from 'react'
// import { Structure, StructureSequence, Queries, StructureSelection, StructureProperties, StructureQuery } from 'mol-model/structure';
// import { EmptyLoci } from 'mol-model/loci';
// export class SequenceView extends View<SequenceViewController, {}, {}> {
// render() {
// const s = this.controller.latestState.structure;
// if (!s) return <div className='molstar-sequence-view-wrap'>No structure available.</div>;
// const seqs = s.models[0].sequence.sequences;
// return <div className='molstar-sequence-view-wrap'>
// {seqs.map((seq, i) => <EntitySequence key={i} ctx={this.controller.context} seq={seq} structure={s} /> )}
// </div>;
// }
// }
// function createQuery(entityId: string, label_seq_id: number) {
// return Queries.generators.atoms({
// entityTest: ctx => StructureProperties.entity.id(ctx.element) === entityId,
// residueTest: ctx => StructureProperties.residue.label_seq_id(ctx.element) === label_seq_id
// });
// }
// // TODO: this is really ineffective and should be done using a canvas.
// class EntitySequence extends React.Component<{ ctx: Context, seq: StructureSequence.Entity, structure: Structure }> {
// raiseInteractityEvent(seqId?: number) {
// if (typeof seqId === 'undefined') {
// InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci);
// return;
// }
// const query = createQuery(this.props.seq.entityId, seqId);
// const loci = StructureSelection.toLoci(StructureQuery.run(query, this.props.structure));
// if (loci.elements.length === 0) InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, EmptyLoci);
// else InteractivityEvents.HighlightLoci.dispatch(this.props.ctx, loci);
// }
// render() {
// const { ctx, seq } = this.props;
// const { offset, sequence } = seq.sequence;
// const elems: JSX.Element[] = [];
// for (let i = 0, _i = sequence.length; i < _i; i++) {
// elems[elems.length] = <ResidueView ctx={ctx} seqId={offset + i} letter={sequence[i]} parent={this} key={i} />;
// }
// return <div style={{ wordWrap: 'break-word' }}>
// <span style={{ fontWeight: 'bold' }}>{this.props.seq.entityId}:{offset}&nbsp;</span>
// {elems}
// </div>;
// }
// }
// class ResidueView extends React.Component<{ ctx: Context, seqId: number, letter: string, parent: EntitySequence }, { isHighlighted: boolean }> {
// state = { isHighlighted: false }
// mouseEnter = () => {
// this.setState({ isHighlighted: true });
// this.props.parent.raiseInteractityEvent(this.props.seqId);
// }
// mouseLeave = () => {
// this.setState({ isHighlighted: false });
// this.props.parent.raiseInteractityEvent();
// }
// render() {
// return <span onMouseEnter={this.mouseEnter} onMouseLeave={this.mouseLeave}
// style={{ cursor: 'pointer', backgroundColor: this.state.isHighlighted ? 'yellow' : void 0 }}>
// {this.props.letter}
// </span>;
// }
// }

View File

@@ -7,17 +7,11 @@
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { Object3D } from '../mol-gl/object3d';
import { BehaviorSubject } from 'rxjs';
import { CameraTransitionManager } from './camera/transition';
export { Camera }
// TODO: slab controls that modify near/far planes?
class Camera implements Object3D {
readonly updatedViewProjection = new BehaviorSubject<Camera>(this);
class Camera {
readonly view: Mat4 = Mat4.identity();
readonly projection: Mat4 = Mat4.identity();
readonly projectionView: Mat4 = Mat4.identity();
@@ -32,14 +26,17 @@ class Camera implements Object3D {
width: 1, height: 1
}
near = 1
far = 10000
fogNear = 5000
fogFar = 10000
zoom = 1
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
get position() { return this.state.position; }
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
get direction() { return this.state.direction; }
set direction(v: Vec3) { Vec3.copy(this.state.direction, v); }
get up() { return this.state.up; }
set up(v: Vec3) { Vec3.copy(this.state.up, v); }
@@ -51,10 +48,12 @@ class Camera implements Object3D {
private deltaDirection = Vec3.zero();
private newPosition = Vec3.zero();
updateMatrices() {
update() {
const snapshot = this.state as Camera.Snapshot;
const height = 2 * Math.tan(snapshot.fov / 2) * Vec3.distance(snapshot.position, snapshot.target);
snapshot.zoom = this.viewport.height / height;
this.zoom = this.viewport.height / height;
updateClip(this);
switch (this.state.mode) {
case 'orthographic': updateOrtho(this); break;
@@ -62,11 +61,7 @@ class Camera implements Object3D {
default: throw new Error('unknown camera mode');
}
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON.Value) || !Mat4.areEqual(this.view, this.prevView, EPSILON.Value);
Mat4.mul(this.projectionView, this.projection, this.view)
Mat4.invert(this.inverseProjectionView, this.projectionView)
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);
if (changed) {
Mat4.mul(this.projectionView, this.projection, this.view)
@@ -74,52 +69,44 @@ class Camera implements Object3D {
Mat4.copy(this.prevView, this.view);
Mat4.copy(this.prevProjection, this.projection);
this.updatedViewProjection.next(this);
}
return changed;
}
setState(snapshot: Partial<Camera.Snapshot>) {
this.transition.apply(snapshot);
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
this.transition.apply(snapshot, durationMs);
}
getSnapshot() {
const ret = Camera.createDefaultSnapshot();
Camera.copySnapshot(ret, this.state);
return ret;
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
}
getFocus(target: Vec3, radius: number): Partial<Camera.Snapshot> {
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
const fov = this.state.fov
const { width, height } = this.viewport
const aspect = width / height
const aspectFactor = (height < width ? 1 : aspect)
const currentDistance = Vec3.distance(this.state.position, target)
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
const deltaDistance = Math.abs(currentDistance - targetDistance)
Vec3.sub(this.deltaDirection, this.state.position, target)
Vec3.setMagnitude(this.deltaDirection, this.state.direction, deltaDistance)
if (currentDistance < targetDistance) Vec3.negate(this.deltaDirection, this.deltaDirection)
Vec3.add(this.newPosition, this.state.position, this.deltaDirection)
Vec3.sub(this.deltaDirection, this.target, this.position)
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
Vec3.setMagnitude(this.deltaDirection, this.deltaDirection, targetDistance)
Vec3.sub(this.newPosition, target, this.deltaDirection)
return { target, position: Vec3.clone(this.newPosition) };
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
state.target = Vec3.clone(target)
state.radius = radius
state.position = Vec3.clone(this.newPosition)
if (up) Vec3.matchDirection(state.up, up, state.up)
return state
}
focus(target: Vec3, radius: number) {
this.setState(this.getFocus(target, radius));
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radius > 0) this.setState(this.getFocus(target, radius, up, dir), durationMs);
}
// lookAt(target: Vec3) {
// cameraLookAt(this.position, this.up, this.direction, target);
// }
// translate(v: Vec3) {
// Vec3.add(this.position, this.position, v);
// cameraLookAt(this.position, this.up, this.direction, this.target);
// }
project(out: Vec4, point: Vec3) {
return cameraProject(out, point, this.viewport, this.projectionView)
}
@@ -128,10 +115,6 @@ class Camera implements Object3D {
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView)
}
dispose() {
this.updatedViewProjection.complete();
}
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(-1, -1, 1, 1)) {
this.viewport = viewport;
Camera.copySnapshot(this.state, state);
@@ -142,13 +125,6 @@ class Camera implements Object3D {
namespace Camera {
export type Mode = 'perspective' | 'orthographic'
export interface ClippingInfo {
near: number,
far: number,
fogNear: number,
fogFar: number
}
/**
* Sets an offseted view in a larger frustum. This is useful for
* - multi-window or multi-monitor/multi-machine setups
@@ -176,64 +152,48 @@ namespace Camera {
export function createDefaultSnapshot(): Snapshot {
return {
mode: 'perspective',
fov: Math.PI / 4,
position: Vec3.zero(),
direction: Vec3.create(0, 0, -1),
position: Vec3.create(0, 0, 100),
up: Vec3.create(0, 1, 0),
target: Vec3.create(0, 0, 0),
near: 1,
far: 10000,
fogNear: 1,
fogFar: 10000,
fov: Math.PI / 4,
zoom: 1,
radius: 10,
fog: 50,
};
}
export interface Snapshot {
mode: Mode,
mode: Mode
fov: number
position: Vec3,
// Normalized camera direction
direction: Vec3,
up: Vec3,
target: Vec3,
position: Vec3
up: Vec3
target: Vec3
near: number,
far: number,
fogNear: number,
fogFar: number,
fov: number,
zoom: number,
radius: number
fog: number
}
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
if (!source) return;
if (!source) return out;
if (typeof source.mode !== 'undefined') out.mode = source.mode;
if (typeof source.fov !== 'undefined') out.fov = source.fov;
if (typeof source.position !== 'undefined') Vec3.copy(out.position, source.position);
if (typeof source.direction !== 'undefined') Vec3.copy(out.direction, source.direction);
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
if (typeof source.near !== 'undefined') out.near = source.near;
if (typeof source.far !== 'undefined') out.far = source.far;
if (typeof source.fogNear !== 'undefined') out.fogNear = source.fogNear;
if (typeof source.fogFar !== 'undefined') out.fogFar = source.fogFar;
if (typeof source.radius !== 'undefined') out.radius = source.radius;
if (typeof source.fog !== 'undefined') out.fog = source.fog;
if (typeof source.fov !== 'undefined') out.fov = source.fov;
if (typeof source.zoom !== 'undefined') out.zoom = source.zoom;
return out;
}
}
const _center = Vec3.zero();
function updateOrtho(camera: Camera) {
const { viewport, state: { zoom, near, far }, viewOffset } = camera
const { viewport, zoom, near, far, viewOffset } = camera
const fullLeft = -(viewport.width - viewport.x) / 2
const fullRight = (viewport.width - viewport.x) / 2
@@ -250,7 +210,7 @@ function updateOrtho(camera: Camera) {
let top = cy + dy
let bottom = cy - dy
if (viewOffset && viewOffset.enabled) {
if (viewOffset.enabled) {
const zoomW = zoom / (viewOffset.width / viewOffset.fullWidth)
const zoomH = zoom / (viewOffset.height / viewOffset.fullHeight)
const scaleW = (fullRight - fullLeft) / viewOffset.width
@@ -265,21 +225,20 @@ function updateOrtho(camera: Camera) {
Mat4.ortho(camera.projection, left, right, top, bottom, near, far)
// build view matrix
Vec3.add(_center, camera.position, camera.direction)
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
}
function updatePers(camera: Camera) {
const aspect = camera.viewport.width / camera.viewport.height
const { state: { fov, near, far }, viewOffset } = camera
const { near, far, viewOffset } = camera
let top = near * Math.tan(0.5 * fov)
let top = near * Math.tan(0.5 * camera.state.fov)
let height = 2 * top
let width = aspect * height
let left = -0.5 * width
if (viewOffset && viewOffset.enabled) {
if (viewOffset.enabled) {
left += viewOffset.offsetX * width / viewOffset.fullWidth
top -= viewOffset.offsetY * height / viewOffset.fullHeight
width *= viewOffset.width / viewOffset.fullWidth
@@ -290,6 +249,33 @@ function updatePers(camera: Camera) {
Mat4.perspective(camera.projection, left, left + width, top, top - height, near, far)
// build view matrix
Vec3.add(_center, camera.position, camera.direction)
Mat4.lookAt(camera.view, camera.position, _center, camera.up)
Mat4.lookAt(camera.view, camera.position, camera.target, camera.up)
}
function updateClip(camera: Camera) {
const { radius, mode, fog } = camera.state
const cDist = Vec3.distance(camera.position, camera.target)
const bRadius = Math.max(1, radius)
let near = cDist - bRadius
let far = cDist + bRadius
const fogNearFactor = -(50 - fog) / 50
let fogNear = cDist - (bRadius * fogNearFactor)
let fogFar = cDist + bRadius
if (mode === 'perspective') {
// set at least to 5 to avoid slow sphere impostor rendering
near = Math.max(5, near)
far = Math.max(5, far)
} else {
near = Math.max(0, near)
far = Math.max(0, far)
}
camera.near = near;
camera.far = far;
camera.fogNear = fogNear;
camera.fogFar = fogFar;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 Mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -22,7 +22,7 @@ class CameraTransitionManager {
private current = Camera.createDefaultSnapshot();
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
if (durationMs <= 0 || to.mode !== this.camera.state.mode) {
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
this.finish(to);
return;
}
@@ -68,33 +68,21 @@ class CameraTransitionManager {
namespace CameraTransitionManager {
export type TransitionFunc = (out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot) => void
const _rot = Quat.identity();
export function defaultTransition(out: Camera.Snapshot, t: number, source: Camera.Snapshot, target: Camera.Snapshot): void {
Camera.copySnapshot(out, target);
// Rotate direction
const rot = Quat.identity();
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.direction, target.direction), t);
Vec3.transformQuat(out.direction, source.direction, rot);
// Rotate up
Quat.setIdentity(rot);
Quat.slerp(rot, rot, Quat.rotationTo(Quat.zero(), source.up, target.up), t);
Vec3.transformQuat(out.up, source.up, rot);
Quat.slerp(_rot, Quat.Identity, Quat.rotationTo(_rot, source.up, target.up), t);
Vec3.transformQuat(out.up, source.up, _rot);
// Lerp target
// Lerp target, position & radius
Vec3.lerp(out.target, source.target, target.target, t);
Vec3.lerp(out.position, source.position, target.position, t);
out.radius = lerp(source.radius, target.radius, t);
// Update position
const dist = -lerp(Vec3.distance(source.position, source.target), Vec3.distance(target.position, target.target), t);
Vec3.scale(out.position, out.direction, dist);
Vec3.add(out.position, out.position, out.target);
// Lerp other props
out.zoom = lerp(source.zoom, target.zoom, t);
// Lerp fov & fog
out.fov = lerp(source.fov, target.fov, t);
out.near = lerp(source.near, target.near, t);
out.far = lerp(source.far, target.far, t);
out.fogNear = lerp(source.fogNear, target.fogNear, t);
out.fogFar = lerp(source.fogFar, target.fogFar, t);
out.fog = lerp(source.fog, target.fog, t);
}
}

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3, Vec4, EPSILON } from '../../mol-math/linear-algebra'
import { Mat4, Vec3, Vec4 } from '../../mol-math/linear-algebra'
export { Viewport }
@@ -55,31 +55,6 @@ namespace Viewport {
//
const tmpVec3 = Vec3()
/** Modifies the direction & up vectors in place, both are normalized */
export function cameraLookAt(position: Vec3, up: Vec3, direction: Vec3, target: Vec3) {
Vec3.sub(tmpVec3, target, position)
Vec3.normalize(tmpVec3, tmpVec3)
if (!Vec3.isZero(tmpVec3)) {
// change direction vector to look at target
const d = Vec3.dot(tmpVec3, up)
if (Math.abs(d - 1) < EPSILON.Value) { // parallel
Vec3.scale(up, direction, -1)
} else if (Math.abs(d + 1) < EPSILON.Value) { // anti parallel
Vec3.copy(up, direction)
}
Vec3.copy(direction, tmpVec3)
// normalize up vector
Vec3.cross(tmpVec3, direction, up)
Vec3.normalize(tmpVec3, tmpVec3)
Vec3.cross(up, tmpVec3, direction)
Vec3.normalize(up, up)
}
}
const NEAR_RANGE = 0
const FAR_RANGE = 1

View File

@@ -17,7 +17,7 @@ import { Representation } from '../mol-repr/representation';
import Scene from '../mol-gl/scene';
import { GraphicsRenderVariant } from '../mol-gl/webgl/render-item';
import { PickingId } from '../mol-geo/geometry/picking';
import { MarkerAction } from '../mol-geo/geometry/marker-data';
import { MarkerAction } from '../mol-util/marker-action';
import { Loci, EmptyLoci, isEmptyLoci } from '../mol-model/loci';
import { Camera } from './camera';
import { ParamDefinition as PD } from '../mol-util/param-definition';
@@ -31,14 +31,13 @@ import { PixelData } from '../mol-util/image';
import { readTexture } from '../mol-gl/compute/util';
import { DrawPass } from './passes/draw';
import { PickPass } from './passes/pick';
import { Task } from '../mol-task';
import { ImagePass, ImageProps } from './passes/image';
export const Canvas3DParams = {
// TODO: FPS cap?
// maxFps: PD.Numeric(30),
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
cameraClipDistance: PD.Numeric(0, { min: 0.0, max: 50.0, step: 0.1 }, { description: 'The distance between camera and scene at which to clip regardless of near clipping plane.' }),
clip: PD.Interval([1, 100], { min: 1, max: 100, step: 1 }),
fog: PD.Interval([50, 100], { min: 1, max: 100, step: 1 }),
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
@@ -46,6 +45,7 @@ export const Canvas3DParams = {
trackball: PD.Group(TrackballControlsParams),
debug: PD.Group(DebugHelperParams)
}
export const DefaultCanvas3DParams = PD.getDefaultValues(Canvas3DParams);
export type Canvas3DProps = PD.Values<typeof Canvas3DParams>
export { Canvas3D }
@@ -53,8 +53,8 @@ export { Canvas3D }
interface Canvas3D {
readonly webgl: WebGLContext,
add: (repr: Representation.Any) => void
remove: (repr: Representation.Any) => void
add: (repr: Representation.Any) => Promise<void>
remove: (repr: Representation.Any) => Promise<void>
update: (repr?: Representation.Any, keepBoundingSphere?: boolean) => void
clear: () => void
@@ -66,6 +66,7 @@ interface Canvas3D {
getLoci: (pickingId: PickingId) => Representation.Loci
readonly didDraw: BehaviorSubject<now.Timestamp>
readonly reprCount: BehaviorSubject<number>
handleResize: () => void
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
@@ -74,6 +75,7 @@ interface Canvas3D {
downloadScreenshot: () => void
getPixelData: (variant: GraphicsRenderVariant) => PixelData
setProps: (props: Partial<Canvas3DProps>) => void
getImagePass: () => ImagePass
/** Returns a copy of the current Canvas3D instance props */
readonly props: Readonly<Canvas3DProps>
@@ -85,25 +87,27 @@ interface Canvas3D {
}
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
const DefaultRunTask = (task: Task<unknown>) => task.run()
namespace Canvas3D {
export interface HighlightEvent { current: Representation.Loci, prev: Representation.Loci, modifiers?: ModifiersKeys }
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}) {
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
const gl = getGLContext(canvas, {
alpha: false,
alpha: true,
antialias: true,
depth: true,
preserveDrawingBuffer: true
preserveDrawingBuffer: true,
premultipliedAlpha: false,
})
if (gl === null) throw new Error('Could not create a WebGL rendering context')
const input = InputObserver.fromElement(canvas)
return Canvas3D.create(gl, input, props)
return Canvas3D.create(gl, input, props, runTask)
}
export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}): Canvas3D {
const p = { ...PD.getDefaultValues(Canvas3DParams), ...props }
export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
const p = { ...DefaultCanvas3DParams, ...props }
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
const reprUpdatedSubscriptions = new Map<Representation.Any, Subscription>()
@@ -112,31 +116,31 @@ namespace Canvas3D {
const startTime = now()
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
const camera = new Camera({
near: 0.1,
far: 10000,
position: Vec3.create(0, 0, 10),
mode: p.cameraMode
})
const webgl = createContext(gl)
let width = gl.drawingBufferWidth
let height = gl.drawingBufferHeight
const scene = Scene.create(webgl)
const camera = new Camera({
position: Vec3.create(0, 0, 100),
mode: p.cameraMode,
fog: p.cameraFog
})
const controls = TrackballControls.create(input, camera, p.trackball)
const renderer = Renderer.create(webgl, camera, p.renderer)
const renderer = Renderer.create(webgl, p.renderer)
const debugHelper = new BoundingSphereHelper(webgl, scene, p.debug);
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input);
const drawPass = new DrawPass(webgl, renderer, scene, debugHelper)
const pickPass = new PickPass(webgl, renderer, scene, 0.5)
const drawPass = new DrawPass(webgl, renderer, scene, camera, debugHelper)
const pickPass = new PickPass(webgl, renderer, scene, camera, 0.5)
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
let isUpdating = false
let drawPending = false
let cameraResetRequested = false
function getLoci(pickingId: PickingId) {
let loci: Loci = EmptyLoci
@@ -144,7 +148,9 @@ namespace Canvas3D {
reprRenderObjects.forEach((_, _repr) => {
const _loci = _repr.getLoci(pickingId)
if (!isEmptyLoci(_loci)) {
if (!isEmptyLoci(loci)) console.warn('found another loci')
if (!isEmptyLoci(loci)) {
console.warn('found another loci, this should not happen')
}
loci = _loci
repr = _repr
}
@@ -168,46 +174,13 @@ namespace Canvas3D {
}
}
let currentNear = -1, currentFar = -1, currentFogNear = -1, currentFogFar = -1
function setClipping() {
const cDist = Vec3.distance(camera.state.position, camera.state.target)
const bRadius = Math.max(10, scene.boundingSphere.radius)
const nearFactor = (50 - p.clip[0]) / 50
const farFactor = -(50 - p.clip[1]) / 50
let near = cDist - (bRadius * nearFactor)
let far = cDist + (bRadius * farFactor)
const fogNearFactor = (50 - p.fog[0]) / 50
const fogFarFactor = -(50 - p.fog[1]) / 50
let fogNear = cDist - (bRadius * fogNearFactor)
let fogFar = cDist + (bRadius * fogFarFactor)
if (camera.state.mode === 'perspective') {
near = Math.max(1, p.cameraClipDistance, near)
far = Math.max(1, far)
fogNear = Math.max(1, fogNear)
fogFar = Math.max(1, fogFar)
} else if (camera.state.mode === 'orthographic') {
if (p.cameraClipDistance > 0) {
near = Math.max(p.cameraClipDistance, near)
}
}
if (near !== currentNear || far !== currentFar || fogNear !== currentFogNear || fogFar !== currentFogFar) {
camera.setState({ near, far, fogNear, fogFar })
currentNear = near, currentFar = far, currentFogNear = fogNear, currentFogFar = fogFar
}
}
function render(variant: 'pick' | 'draw', force: boolean) {
if (isUpdating) return false
if (scene.isCommiting) return false
let didRender = false
controls.update(currentTime);
// TODO: is this a good fix? Also, setClipping does not work if the user has manually set a clipping plane.
if (!camera.transition.inTransition) setClipping();
const cameraChanged = camera.updateMatrices();
controls.update(currentTime)
Viewport.set(camera.viewport, 0, 0, width, height)
const cameraChanged = camera.update()
multiSample.update(force || cameraChanged, currentTime)
if (force || cameraChanged || multiSample.enabled) {
@@ -216,9 +189,9 @@ namespace Canvas3D {
pickPass.render()
break;
case 'draw':
renderer.setViewport(0, 0, width, height);
renderer.setViewport(0, 0, width, height)
if (multiSample.enabled) {
multiSample.render()
multiSample.render(true)
} else {
drawPass.render(!postprocessing.enabled)
if (postprocessing.enabled) postprocessing.render(true)
@@ -229,7 +202,7 @@ namespace Canvas3D {
didRender = true
}
return didRender && cameraChanged;
return didRender;
}
let forceNextDraw = false;
@@ -261,26 +234,35 @@ namespace Canvas3D {
return pickPass.identify(x, y)
}
function commit(renderObjects?: readonly GraphicsRenderObject[]) {
scene.update(renderObjects, false)
return runTask(scene.commit()).then(() => {
if (cameraResetRequested && !scene.isCommiting) {
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
cameraResetRequested = false
}
if (debugHelper.isEnabled) debugHelper.update()
requestDraw(true)
reprCount.next(reprRenderObjects.size)
})
}
function add(repr: Representation.Any) {
isUpdating = true
const oldRO = reprRenderObjects.get(repr)
const newRO = new Set<GraphicsRenderObject>()
repr.renderObjects.forEach(o => newRO.add(o))
if (oldRO) {
if (!SetUtils.areEqual(newRO, oldRO)) {
for (const o of Array.from(newRO)) { if (!oldRO.has(o)) scene.add(o); }
for (const o of Array.from(newRO)) { if (!oldRO.has(o)) scene.add(o) }
for (const o of Array.from(oldRO)) { if (!newRO.has(o)) scene.remove(o) }
}
} else {
repr.renderObjects.forEach(o => scene.add(o))
}
reprRenderObjects.set(repr, newRO)
scene.update(repr.renderObjects, false)
if (debugHelper.isEnabled) debugHelper.update()
isUpdating = false
requestDraw(true)
reprCount.next(reprRenderObjects.size)
return commit(repr.renderObjects)
}
handleResize()
@@ -289,10 +271,10 @@ namespace Canvas3D {
webgl,
add: (repr: Representation.Any) => {
add(repr)
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
if (!repr.state.syncManually) add(repr)
}))
return add(repr)
},
remove: (repr: Representation.Any) => {
const updatedSubscription = reprUpdatedSubscriptions.get(repr)
@@ -301,15 +283,11 @@ namespace Canvas3D {
}
const renderObjects = reprRenderObjects.get(repr)
if (renderObjects) {
isUpdating = true
renderObjects.forEach(o => scene.remove(o))
reprRenderObjects.delete(repr)
scene.update(void 0, false)
if (debugHelper.isEnabled) debugHelper.update()
isUpdating = false
requestDraw(true)
reprCount.next(reprRenderObjects.size)
return commit()
}
return Promise.resolve()
},
update: (repr, keepSphere) => {
if (repr) {
@@ -323,6 +301,8 @@ namespace Canvas3D {
reprRenderObjects.clear()
scene.clear()
debugHelper.clear()
requestDraw(true)
reprCount.next(reprRenderObjects.size)
},
// draw,
@@ -334,8 +314,12 @@ namespace Canvas3D {
handleResize,
resetCamera: () => {
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
requestDraw(true);
if (scene.isCommiting) {
cameraResetRequested = true
} else {
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
requestDraw(true);
}
},
camera,
downloadScreenshot: () => {
@@ -351,13 +335,15 @@ namespace Canvas3D {
}
},
didDraw,
reprCount,
setProps: (props: Partial<Canvas3DProps>) => {
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
camera.setState({ mode: props.cameraMode })
}
if (props.cameraClipDistance !== undefined) p.cameraClipDistance = props.cameraClipDistance
if (props.clip !== undefined) p.clip = [props.clip[0], props.clip[1]]
if (props.fog !== undefined) p.fog = [props.fog[0], props.fog[1]]
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
camera.setState({ fog: props.cameraFog })
}
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
if (props.multiSample) multiSample.setProps(props.multiSample)
@@ -366,13 +352,15 @@ namespace Canvas3D {
if (props.debug) debugHelper.setProps(props.debug)
requestDraw(true)
},
getImagePass: (props: Partial<ImageProps> = {}) => {
return new ImagePass(webgl, renderer, scene, camera, debugHelper, props)
},
get props() {
return {
cameraMode: camera.state.mode,
cameraClipDistance: p.cameraClipDistance,
clip: p.clip,
fog: p.fog,
cameraFog: camera.state.fog,
cameraResetDurationMs: p.cameraResetDurationMs,
postprocessing: { ...postprocessing.props },
multiSample: { ...multiSample.props },
@@ -396,7 +384,6 @@ namespace Canvas3D {
input.dispose()
controls.dispose()
renderer.dispose()
camera.dispose()
interactionHelper.dispose()
}
}

View File

@@ -9,26 +9,47 @@
*/
import { Quat, Vec2, Vec3, EPSILON } from '../../mol-math/linear-algebra';
import { cameraLookAt, Viewport } from '../camera/util';
import InputObserver, { DragInput, WheelInput, ButtonsType, PinchInput } from '../../mol-util/input/input-observer';
import { Object3D } from '../../mol-gl/object3d';
import { Viewport } from '../camera/util';
import InputObserver, { DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Camera } from '../camera';
import { absMax } from '../../mol-math/misc';
import { Binding } from '../../mol-util/binding';
const B = ButtonsType
const M = ModifiersKeys
const Trigger = Binding.Trigger
export const DefaultTrackballBindings = {
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate the 3D scene by dragging using ${triggers}'),
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate the 3D scene around the z-axis by dragging using ${triggers}'),
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan the 3D scene by dragging using ${triggers}'),
dragZoom: Binding.Empty,
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus the 3D scene by dragging using ${triggers}'),
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom the 3D scene by dragging using ${triggers}'),
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom the 3D scene by scrolling using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Focus the 3D scene by scrolling using ${triggers}'),
scrollFocusZoom: Binding.Empty,
}
export const TrackballControlsParams = {
noScroll: PD.Boolean(true, { isHidden: true }),
rotateSpeed: PD.Numeric(5.0, { min: 0.1, max: 10, step: 0.1 }),
rotateSpeed: PD.Numeric(3.0, { min: 0.1, max: 10, step: 0.1 }),
zoomSpeed: PD.Numeric(6.0, { min: 0.1, max: 10, step: 0.1 }),
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
spin: PD.Boolean(false),
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
staticMoving: PD.Boolean(true, { isHidden: true }),
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
minDistance: PD.Numeric(0.01, {}, { isHidden: true }),
maxDistance: PD.Numeric(1e150, {}, { isHidden: true })
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true })
}
export type TrackballControlsProps = PD.Values<typeof TrackballControlsParams>
@@ -44,11 +65,10 @@ interface TrackballControls {
dispose: () => void
}
namespace TrackballControls {
export function create(input: InputObserver, object: Object3D & { target: Vec3 }, props: Partial<TrackballControlsProps> = {}): TrackballControls {
export function create(input: InputObserver, camera: Camera, props: Partial<TrackballControlsProps> = {}): TrackballControls {
const p = { ...PD.getDefaultValues(TrackballControlsParams), ...props }
const viewport: Viewport = { x: 0, y: 0, width: 0, height: 0 }
const target: Vec3 = object.target
const viewport = Viewport()
let disposed = false
@@ -60,91 +80,116 @@ namespace TrackballControls {
let _isInteracting = false;
// For internal use
const lastPosition = Vec3.zero()
const lastPosition = Vec3()
const _eye = Vec3.zero()
const _eye = Vec3()
const _movePrev = Vec2.zero()
const _moveCurr = Vec2.zero()
const _rotPrev = Vec2()
const _rotCurr = Vec2()
const _rotLastAxis = Vec3()
let _rotLastAngle = 0
const _lastAxis = Vec3.zero()
let _lastAngle = 0
const _zRotPrev = Vec2()
const _zRotCurr = Vec2()
let _zRotLastAngle = 0
const _zoomStart = Vec2.zero()
const _zoomEnd = Vec2.zero()
const _zoomStart = Vec2()
const _zoomEnd = Vec2()
const _panStart = Vec2.zero()
const _panEnd = Vec2.zero()
const _focusStart = Vec2()
const _focusEnd = Vec2()
const _panStart = Vec2()
const _panEnd = Vec2()
// Initial values for reseting
const target0 = Vec3.clone(target)
const position0 = Vec3.clone(object.position)
const up0 = Vec3.clone(object.up)
const target0 = Vec3.clone(camera.target)
const position0 = Vec3.clone(camera.position)
const up0 = Vec3.clone(camera.up)
const mouseOnScreenVec2 = Vec2.zero()
const mouseOnScreenVec2 = Vec2()
function getMouseOnScreen(pageX: number, pageY: number) {
Vec2.set(
return Vec2.set(
mouseOnScreenVec2,
(pageX - viewport.x) / viewport.width,
(pageY - viewport.y) / viewport.height
);
return mouseOnScreenVec2;
}
const mouseOnCircleVec2 = Vec2.zero()
const mouseOnCircleVec2 = Vec2()
function getMouseOnCircle(pageX: number, pageY: number) {
Vec2.set(
return Vec2.set(
mouseOnCircleVec2,
((pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5)),
((viewport.height + 2 * (viewport.y - pageY)) / viewport.width) // screen.width intentional
(pageX - viewport.width * 0.5 - viewport.x) / (viewport.width * 0.5),
(viewport.height + 2 * (viewport.y - pageY)) / viewport.width // screen.width intentional
);
return mouseOnCircleVec2;
}
const rotAxis = Vec3.zero()
const rotQuat = Quat.zero()
const rotEyeDir = Vec3.zero()
const rotObjUpDir = Vec3.zero()
const rotObjSideDir = Vec3.zero()
const rotMoveDir = Vec3.zero()
const rotAxis = Vec3()
const rotQuat = Quat()
const rotEyeDir = Vec3()
const rotObjUpDir = Vec3()
const rotObjSideDir = Vec3()
const rotMoveDir = Vec3()
function rotateCamera() {
Vec3.set(rotMoveDir, _moveCurr[0] - _movePrev[0], _moveCurr[1] - _movePrev[1], 0);
let angle = Vec3.magnitude(rotMoveDir);
const dx = _rotCurr[0] - _rotPrev[0]
const dy = _rotCurr[1] - _rotPrev[1]
Vec3.set(rotMoveDir, dx, dy, 0);
const angle = Vec3.magnitude(rotMoveDir) * p.rotateSpeed;
if (angle) {
Vec3.copy(_eye, object.position)
Vec3.sub(_eye, _eye, target)
Vec3.sub(_eye, camera.position, camera.target)
Vec3.normalize(rotEyeDir, Vec3.copy(rotEyeDir, _eye))
Vec3.normalize(rotObjUpDir, Vec3.copy(rotObjUpDir, object.up))
Vec3.normalize(rotEyeDir, _eye)
Vec3.normalize(rotObjUpDir, camera.up)
Vec3.normalize(rotObjSideDir, Vec3.cross(rotObjSideDir, rotObjUpDir, rotEyeDir))
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, _moveCurr[1] - _movePrev[1])
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, _moveCurr[0] - _movePrev[0])
Vec3.add(rotMoveDir, Vec3.copy(rotMoveDir, rotObjUpDir), rotObjSideDir)
Vec3.setMagnitude(rotObjUpDir, rotObjUpDir, dy)
Vec3.setMagnitude(rotObjSideDir, rotObjSideDir, dx)
Vec3.add(rotMoveDir, rotObjUpDir, rotObjSideDir)
Vec3.normalize(rotAxis, Vec3.cross(rotAxis, rotMoveDir, _eye))
angle *= p.rotateSpeed;
Quat.setAxisAngle(rotQuat, rotAxis, angle)
Vec3.transformQuat(_eye, _eye, rotQuat)
Vec3.transformQuat(object.up, object.up, rotQuat)
Vec3.transformQuat(camera.up, camera.up, rotQuat)
Vec3.copy(_lastAxis, rotAxis)
_lastAngle = angle;
} else if (!p.staticMoving && _lastAngle) {
_lastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
Vec3.sub(_eye, Vec3.copy(_eye, object.position), target)
Quat.setAxisAngle(rotQuat, _lastAxis, _lastAngle)
Vec3.copy(_rotLastAxis, rotAxis)
_rotLastAngle = angle;
} else if (!p.staticMoving && _rotLastAngle) {
_rotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(rotQuat, _rotLastAxis, _rotLastAngle)
Vec3.transformQuat(_eye, _eye, rotQuat)
Vec3.transformQuat(object.up, object.up, rotQuat)
Vec3.transformQuat(camera.up, camera.up, rotQuat)
}
Vec2.copy(_movePrev, _moveCurr)
Vec2.copy(_rotPrev, _rotCurr)
}
const zRotQuat = Quat()
function zRotateCamera() {
const dx = _zRotCurr[0] - _zRotPrev[0]
const dy = _zRotCurr[1] - _zRotPrev[1]
const angle = p.rotateSpeed * (-dx + dy) * -0.05
if (angle) {
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(zRotQuat, _eye, angle)
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
_zRotLastAngle = angle;
} else if (!p.staticMoving && _zRotLastAngle) {
_zRotLastAngle *= Math.sqrt(1.0 - p.dynamicDampingFactor);
Vec3.sub(_eye, camera.position, camera.target)
Quat.setAxisAngle(zRotQuat, _eye, _zRotLastAngle)
Vec3.transformQuat(camera.up, camera.up, zRotQuat)
}
Vec2.copy(_zRotPrev, _zRotCurr)
}
function zoomCamera() {
@@ -160,9 +205,23 @@ namespace TrackballControls {
}
}
const panMouseChange = Vec2.zero()
const panObjUp = Vec3.zero()
const panOffset = Vec3.zero()
function focusCamera() {
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
if (factor !== 0.0) {
const radius = Math.max(1, camera.state.radius + 10 * factor)
camera.setState({ radius })
}
if (p.staticMoving) {
Vec2.copy(_focusStart, _focusEnd)
} else {
_focusStart[1] += (_focusEnd[1] - _focusStart[1]) * p.dynamicDampingFactor
}
}
const panMouseChange = Vec2()
const panObjUp = Vec3()
const panOffset = Vec3()
function panCamera() {
Vec2.sub(panMouseChange, Vec2.copy(panMouseChange, _panEnd), _panStart)
@@ -170,14 +229,14 @@ namespace TrackballControls {
if (Vec2.squaredMagnitude(panMouseChange)) {
Vec2.scale(panMouseChange, panMouseChange, Vec3.magnitude(_eye) * p.panSpeed)
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), object.up)
Vec3.cross(panOffset, Vec3.copy(panOffset, _eye), camera.up)
Vec3.setMagnitude(panOffset, panOffset, panMouseChange[0])
Vec3.setMagnitude(panObjUp, object.up, panMouseChange[1])
Vec3.setMagnitude(panObjUp, camera.up, panMouseChange[1])
Vec3.add(panOffset, panOffset, panObjUp)
Vec3.add(object.position, object.position, panOffset)
Vec3.add(target, target, panOffset)
Vec3.add(camera.position, camera.position, panOffset)
Vec3.add(camera.target, camera.target, panOffset)
if (p.staticMoving) {
Vec2.copy(_panStart, _panEnd)
@@ -193,14 +252,16 @@ namespace TrackballControls {
function checkDistances() {
if (Vec3.squaredMagnitude(_eye) > p.maxDistance * p.maxDistance) {
Vec3.setMagnitude(_eye, _eye, p.maxDistance)
Vec3.add(object.position, target, _eye)
Vec3.add(camera.position, camera.target, _eye)
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_focusStart, _focusEnd)
}
if (Vec3.squaredMagnitude(_eye) < p.minDistance * p.minDistance) {
Vec3.setMagnitude(_eye, _eye, p.minDistance)
Vec3.add(object.position, target, _eye)
Vec3.add(camera.position, camera.target, _eye)
Vec2.copy(_zoomStart, _zoomEnd)
Vec2.copy(_focusStart, _focusEnd)
}
}
@@ -210,20 +271,19 @@ namespace TrackballControls {
if (lastUpdated === t) return;
if (p.spin) spin(t - lastUpdated);
Vec3.sub(_eye, object.position, target)
Vec3.sub(_eye, camera.position, camera.target)
rotateCamera()
zRotateCamera()
zoomCamera()
focusCamera()
panCamera()
Vec3.add(object.position, target, _eye)
Vec3.add(camera.position, camera.target, _eye)
checkDistances()
cameraLookAt(object.position, object.up, object.direction, target)
if (Vec3.squaredDistance(lastPosition, object.position) > EPSILON.Value) {
Vec3.copy(lastPosition, object.position)
if (Vec3.squaredDistance(lastPosition, camera.position) > EPSILON) {
Vec3.copy(lastPosition, camera.position)
}
lastUpdated = t;
@@ -231,54 +291,82 @@ namespace TrackballControls {
/** Reset object's vectors and the target vector to their initial values */
function reset() {
Vec3.copy(target, target0)
Vec3.copy(object.position, position0)
Vec3.copy(object.up, up0)
Vec3.copy(camera.target, target0)
Vec3.copy(camera.position, position0)
Vec3.copy(camera.up, up0)
Vec3.sub(_eye, object.position, target)
cameraLookAt(object.position, object.up, object.direction, target)
Vec3.copy(lastPosition, object.position)
Vec3.sub(_eye, camera.position, camera.target)
Vec3.copy(lastPosition, camera.position)
}
// listeners
function onDrag({ pageX, pageY, buttons, isStart }: DragInput) {
function onDrag({ pageX, pageY, buttons, modifiers, isStart }: DragInput) {
_isInteracting = true;
const dragRotate = Binding.match(p.bindings.dragRotate, buttons, modifiers)
const dragRotateZ = Binding.match(p.bindings.dragRotateZ, buttons, modifiers)
const dragPan = Binding.match(p.bindings.dragPan, buttons, modifiers)
const dragZoom = Binding.match(p.bindings.dragZoom, buttons, modifiers)
const dragFocus = Binding.match(p.bindings.dragFocus, buttons, modifiers)
const dragFocusZoom = Binding.match(p.bindings.dragFocusZoom, buttons, modifiers)
getMouseOnCircle(pageX, pageY)
getMouseOnScreen(pageX, pageY)
if (isStart) {
if (buttons === ButtonsType.Flag.Primary) {
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
Vec2.copy(_movePrev, _moveCurr)
} else if (buttons === ButtonsType.Flag.Auxilary) {
Vec2.copy(_zoomStart, getMouseOnScreen(pageX, pageY))
if (dragRotate) {
Vec2.copy(_rotCurr, mouseOnCircleVec2)
Vec2.copy(_rotPrev, _rotCurr)
}
if (dragRotateZ) {
Vec2.copy(_zRotCurr, mouseOnCircleVec2)
Vec2.copy(_zRotPrev, _zRotCurr)
}
if (dragZoom || dragFocusZoom) {
Vec2.copy(_zoomStart, mouseOnScreenVec2)
Vec2.copy(_zoomEnd, _zoomStart)
} else if (buttons === ButtonsType.Flag.Secondary) {
Vec2.copy(_panStart, getMouseOnScreen(pageX, pageY))
}
if (dragFocus) {
Vec2.copy(_focusStart, mouseOnScreenVec2)
Vec2.copy(_focusEnd, _focusStart)
}
if (dragPan) {
Vec2.copy(_panStart, mouseOnScreenVec2)
Vec2.copy(_panEnd, _panStart)
}
}
if (buttons === ButtonsType.Flag.Primary) {
Vec2.copy(_movePrev, _moveCurr)
Vec2.copy(_moveCurr, getMouseOnCircle(pageX, pageY))
} else if (buttons === ButtonsType.Flag.Auxilary) {
Vec2.copy(_zoomEnd, getMouseOnScreen(pageX, pageY))
} else if (buttons === ButtonsType.Flag.Secondary) {
Vec2.copy(_panEnd, getMouseOnScreen(pageX, pageY))
if (dragRotate) Vec2.copy(_rotCurr, mouseOnCircleVec2)
if (dragRotateZ) Vec2.copy(_zRotCurr, mouseOnCircleVec2)
if (dragZoom || dragFocusZoom) Vec2.copy(_zoomEnd, mouseOnScreenVec2)
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
if (dragFocusZoom) {
const dist = Vec3.distance(camera.state.position, camera.state.target);
camera.setState({ radius: dist / 5 })
}
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
}
function onInteractionEnd() {
_isInteracting = false;
}
function onWheel({ dy }: WheelInput) {
_zoomStart[1] -= dy * 0.0001
function onWheel({ dx, dy, dz, buttons, modifiers }: WheelInput) {
const delta = absMax(dx, dy, dz)
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_zoomEnd[1] += delta * 0.0001
}
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
_focusEnd[1] += delta * 0.0001
}
}
function onPinch({ fraction }: PinchInput) {
_isInteracting = true;
_zoomStart[1] -= (fraction - 1) * 0.1
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_isInteracting = true;
_zoomEnd[1] += (fraction - 1) * 0.1
}
}
function dispose() {
@@ -295,7 +383,7 @@ namespace TrackballControls {
function spin(deltaT: number) {
const frameSpeed = (p.spinSpeed || 0) / 1000;
_spinSpeed[0] = 60 * Math.min(Math.abs(deltaT), 1000 / 8) / 1000 * frameSpeed;
if (!_isInteracting) Vec2.add(_moveCurr, _movePrev, _spinSpeed);
if (!_isInteracting) Vec2.add(_rotCurr, _rotPrev, _spinSpeed);
}
// force an update at start

View File

@@ -13,7 +13,7 @@ import Scene from '../../mol-gl/scene';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { Sphere3D } from '../../mol-math/geometry';
import { Color } from '../../mol-util/color';
import { ColorNames } from '../../mol-util/color/tables';
import { ColorNames } from '../../mol-util/color/names';
import { TransformData } from '../../mol-geo/geometry/transform-data';
import { sphereVertexCount } from '../../mol-geo/primitive/sphere';
import { ValueCell } from '../../mol-util';
@@ -81,7 +81,8 @@ export class BoundingSphereHelper {
}
})
this.scene.update(void 0, false);
this.scene.update(void 0, false)
this.scene.syncCommit()
}
syncVisibility() {

View File

@@ -11,13 +11,15 @@ import InputObserver, { ModifiersKeys, ButtonsType } from '../../mol-util/input/
import { RxEventHelper } from '../../mol-util/rx-event-helper';
type Canvas3D = import('../canvas3d').Canvas3D
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
export class Canvas3dInteractionHelper {
private ev = RxEventHelper.create();
readonly events = {
highlight: this.ev<import('../canvas3d').Canvas3D.HighlightEvent>(),
click: this.ev<import('../canvas3d').Canvas3D.ClickEvent>(),
hover: this.ev<HoverEvent>(),
click: this.ev<ClickEvent>(),
};
private cX = -1;
@@ -52,14 +54,14 @@ export class Canvas3dInteractionHelper {
return;
}
// only highlight the latest
if (!this.inside || this.currentIdentifyT !== t) {
return;
}
const loci = this.getLoci(this.id);
// only broadcast the latest hover
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
this.events.highlight.next({ current: loci, prev: this.prevLoci, modifiers: this.modifiers });
this.events.hover.next({ current: loci, buttons: this.buttons, modifiers: this.modifiers });
this.prevLoci = loci;
}
}
@@ -75,14 +77,14 @@ export class Canvas3dInteractionHelper {
leave() {
this.inside = false;
if (this.prevLoci.loci !== EmptyLoci) {
const prev = this.prevLoci;
this.prevLoci = Representation.Loci.Empty;
this.events.highlight.next({ current: this.prevLoci, prev });
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
}
}
move(x: number, y: number, modifiers: ModifiersKeys) {
move(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
this.inside = true;
this.buttons = buttons;
this.modifiers = modifiers;
this.cX = x;
this.cY = y;
@@ -99,7 +101,7 @@ export class Canvas3dInteractionHelper {
modify(modifiers: ModifiersKeys) {
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
this.modifiers = modifiers;
this.events.highlight.next({ current: this.prevLoci, prev: this.prevLoci, modifiers: this.modifiers });
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
}
dispose() {
@@ -108,8 +110,8 @@ export class Canvas3dInteractionHelper {
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
input.move.subscribe(({x, y, inside, buttons, modifiers }) => {
if (!inside || buttons) { return; }
this.move(x, y, modifiers);
if (!inside) return;
this.move(x, y, buttons, modifiers);
});
input.leave.subscribe(() => {

View File

@@ -10,6 +10,7 @@ import Renderer from '../../mol-gl/renderer';
import Scene from '../../mol-gl/scene';
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
import { createTexture, Texture } from '../../mol-gl/webgl/texture';
import { Camera } from '../camera';
export class DrawPass {
colorTarget: RenderTarget
@@ -18,11 +19,11 @@ export class DrawPass {
private depthTarget: RenderTarget | null
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private debugHelper: BoundingSphereHelper) {
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
const { gl, extensions } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.colorTarget = createRenderTarget(webgl, width, height)
this.packedDepth = !extensions.depthTexture
this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
@@ -42,29 +43,28 @@ export class DrawPass {
}
render(toDrawingBuffer: boolean) {
const { webgl, renderer, scene, debugHelper, colorTarget, depthTarget } = this
const { gl } = webgl
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
} else {
colorTarget.bind()
}
renderer.setViewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight)
renderer.render(scene, 'color', true)
renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
renderer.render(scene, camera, 'color', true)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, 'color', false)
renderer.render(debugHelper.scene, camera, 'color', false)
}
// do a depth pass if not rendering to drawing buffer and
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (!toDrawingBuffer && depthTarget) {
depthTarget.bind()
renderer.render(scene, 'depth', true)
renderer.render(scene, camera, 'depth', true)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, 'depth', false)
renderer.render(debugHelper.scene, camera, 'depth', false)
}
}
}

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import Renderer from '../../mol-gl/renderer';
import Scene from '../../mol-gl/scene';
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { DrawPass } from './draw'
import { PostprocessingPass, PostprocessingParams } from './postprocessing'
import { MultiSamplePass, MultiSampleParams } from './multi-sample'
import { Camera } from '../camera';
import { Viewport } from '../camera/util';
export const ImageParams = {
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
}
export type ImageProps = PD.Values<typeof ImageParams>
export class ImagePass {
private _width = 1024
private _height = 768
private _camera = new Camera()
private _colorTarget: RenderTarget
get colorTarget() { return this._colorTarget }
readonly drawPass: DrawPass
private readonly postprocessing: PostprocessingPass
private readonly multiSample: MultiSamplePass
get width() { return this._width }
get height() { return this._height }
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
const p = { ...PD.getDefaultValues(ImageParams), ...props }
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
this.setSize(this._width, this._height)
}
setSize(width: number, height: number) {
this._width = width
this._height = height
this.drawPass.setSize(width, height)
this.postprocessing.setSize(width, height)
this.multiSample.setSize(width, height)
}
setProps(props: Partial<ImageProps> = {}) {
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
if (props.multiSample) this.multiSample.setProps(props.multiSample)
}
render() {
Camera.copySnapshot(this._camera.state, this.camera.state)
Viewport.set(this._camera.viewport, 0, 0, this._width, this._height)
this._camera.update()
this.renderer.setViewport(0, 0, this._width, this._height);
if (this.multiSample.enabled) {
this.multiSample.render(false)
this._colorTarget = this.multiSample.colorTarget
} else {
this.drawPass.render(false)
if (this.postprocessing.enabled) {
this.postprocessing.render(false)
this._colorTarget = this.postprocessing.target
} else {
this._colorTarget = this.drawPass.colorTarget
}
}
}
getImageData(width: number, height: number) {
this.setSize(width, height)
this.render()
const pd = this.colorTarget.getPixelData()
return new ImageData(new Uint8ClampedArray(pd.array), pd.width, pd.height)
}
}

View File

@@ -54,6 +54,7 @@ export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
export class MultiSamplePass {
props: MultiSampleProps
colorTarget: RenderTarget
private composeTarget: RenderTarget
private holdTarget: RenderTarget
@@ -65,6 +66,7 @@ export class MultiSamplePass {
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
const { gl } = webgl
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
@@ -92,6 +94,7 @@ export class MultiSamplePass {
}
setSize(width: number, height: number) {
this.colorTarget.setSize(width, height)
this.composeTarget.setSize(width, height)
this.holdTarget.setSize(width, height)
ValueCell.update(this.compose.values.uTexSize, Vec2.set(this.compose.values.uTexSize.ref.value, width, height))
@@ -102,15 +105,15 @@ export class MultiSamplePass {
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
}
render() {
render(toDrawingBuffer: boolean) {
if (this.props.mode === 'temporal') {
this.renderTemporalMultiSample()
this.renderTemporalMultiSample(toDrawingBuffer)
} else {
this.renderMultiSample()
this.renderMultiSample(toDrawingBuffer)
}
}
private renderMultiSample() {
private renderMultiSample(toDrawingBuffer: boolean) {
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
const { gl, state } = webgl
@@ -135,7 +138,7 @@ export class MultiSamplePass {
for (let i = 0; i < offsetList.length; ++i) {
const offset = offsetList[i]
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
camera.updateMatrices()
camera.update()
// the theory is that equal weights for each sample lead to an accumulation of rounding
// errors. The following equation varies the sampleWeight per sample so that it is uniformly
@@ -168,16 +171,20 @@ export class MultiSamplePass {
ValueCell.update(compose.values.tColor, composeTarget.texture)
compose.update()
webgl.unbindFramebuffer()
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
} else {
this.colorTarget.bind()
}
gl.viewport(0, 0, width, height)
state.disable(gl.BLEND)
compose.render()
camera.viewOffset.enabled = false
camera.updateMatrices()
camera.update()
}
private renderTemporalMultiSample() {
private renderTemporalMultiSample(toDrawingBuffer: boolean) {
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
const { gl, state } = webgl
@@ -223,7 +230,7 @@ export class MultiSamplePass {
for (let i = 0; i < numSamplesPerFrame; ++i) {
const offset = offsetList[this.sampleIndex]
Camera.setViewOffset(camera.viewOffset, width, height, offset[0], offset[1], width, height)
camera.updateMatrices()
camera.update()
// render scene and optionally postprocess
drawPass.render(false)
@@ -252,7 +259,11 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, composeTarget.texture)
compose.update()
webgl.unbindFramebuffer()
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
} else {
this.colorTarget.bind()
}
gl.viewport(0, 0, width, height)
state.disable(gl.BLEND)
compose.render()
@@ -261,7 +272,11 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, 1.0 - accumulationWeight)
ValueCell.update(compose.values.tColor, holdTarget.texture)
compose.update()
webgl.unbindFramebuffer()
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
} else {
this.colorTarget.bind()
}
gl.viewport(0, 0, width, height)
if (accumulationWeight === 0) state.disable(gl.BLEND)
else state.enable(gl.BLEND)
@@ -269,7 +284,7 @@ export class MultiSamplePass {
}
camera.viewOffset.enabled = false
camera.updateMatrices()
camera.update()
if (this.sampleIndex >= offsetList.length) this.sampleIndex = -1
}
}

View File

@@ -10,20 +10,24 @@ import Renderer from '../../mol-gl/renderer';
import Scene from '../../mol-gl/scene';
import { PickingId } from '../../mol-geo/geometry/picking';
import { decodeFloatRGB } from '../../mol-util/float-packing';
const readBuffer = new Uint8Array(4)
import { Camera } from '../camera';
export class PickPass {
pickDirty = true
objectPickTarget: RenderTarget
instancePickTarget: RenderTarget
groupPickTarget: RenderTarget
private objectBuffer: Uint8Array
private instanceBuffer: Uint8Array
private groupBuffer: Uint8Array
private pickScale: number
private pickWidth: number
private pickHeight: number
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private pickBaseScale: number) {
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private pickBaseScale: number) {
const { gl } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
@@ -31,35 +35,73 @@ export class PickPass {
this.pickScale = pickBaseScale / webgl.pixelRatio
this.pickWidth = Math.round(width * this.pickScale)
this.pickHeight = Math.round(height * this.pickScale)
this.objectPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
this.instancePickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
this.groupPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
this.setupBuffers()
}
private setupBuffers() {
const bufferSize = this.pickWidth * this.pickHeight * 4
if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
this.objectBuffer = new Uint8Array(bufferSize)
this.instanceBuffer = new Uint8Array(bufferSize)
this.groupBuffer = new Uint8Array(bufferSize)
}
}
setSize(width: number, height: number) {
this.pickScale = this.pickBaseScale / this.webgl.pixelRatio
this.pickWidth = Math.round(width * this.pickScale)
this.pickHeight = Math.round(height * this.pickScale)
this.objectPickTarget.setSize(this.pickWidth, this.pickHeight)
this.instancePickTarget.setSize(this.pickWidth, this.pickHeight)
this.groupPickTarget.setSize(this.pickWidth, this.pickHeight)
this.setupBuffers()
}
render() {
const { renderer, scene } = this
const { renderer, scene, camera } = this
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
this.objectPickTarget.bind();
renderer.render(scene, 'pickObject', true);
renderer.render(scene, camera, 'pickObject', true);
this.instancePickTarget.bind();
renderer.render(scene, 'pickInstance', true);
renderer.render(scene, camera, 'pickInstance', true);
this.groupPickTarget.bind();
renderer.render(scene, 'pickGroup', true);
renderer.render(scene, camera, 'pickGroup', true);
this.pickDirty = false
}
private syncBuffers() {
const { webgl } = this
this.objectPickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.objectBuffer)
this.instancePickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.instanceBuffer)
this.groupPickTarget.bind()
webgl.readPixels(0, 0, this.pickWidth, this.pickHeight, this.groupBuffer)
}
private getId(x: number, y: number, buffer: Uint8Array) {
const idx = (y * this.pickWidth + x) * 4
return decodeFloatRGB(buffer[idx], buffer[idx + 1], buffer[idx + 2])
}
identify(x: number, y: number): PickingId | undefined {
const { webgl, pickScale } = this
const { gl } = webgl
if (this.pickDirty) this.render()
if (this.pickDirty) {
this.render()
this.syncBuffers()
}
x *= webgl.pixelRatio
y *= webgl.pixelRatio
@@ -68,19 +110,13 @@ export class PickPass {
const xp = Math.round(x * pickScale)
const yp = Math.round(y * pickScale)
this.objectPickTarget.bind()
webgl.readPixels(xp, yp, 1, 1, readBuffer)
const objectId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
const objectId = this.getId(xp, yp, this.objectBuffer)
if (objectId === -1) return
this.instancePickTarget.bind()
webgl.readPixels(xp, yp, 1, 1, readBuffer)
const instanceId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
const instanceId = this.getId(xp, yp, this.instanceBuffer)
if (instanceId === -1) return
this.groupPickTarget.bind()
webgl.readPixels(xp, yp, 1, 1, readBuffer)
const groupId = decodeFloatRGB(readBuffer[0], readBuffer[1], readBuffer[2])
const groupId = this.getId(xp, yp, this.groupBuffer)
if (groupId === -1) return
return { objectId, instanceId, groupId }

View File

@@ -51,7 +51,7 @@ export const PostprocessingParams = {
occlusionEnable: PD.Boolean(false),
occlusionKernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
occlusionBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
occlusionRadius: PD.Numeric(64, { min: 0, max: 256, step: 1 }),
occlusionRadius: PD.Numeric(32, { min: 0, max: 256, step: 1 }),
outlineEnable: PD.Boolean(false),
outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
@@ -160,10 +160,10 @@ export class PostprocessingPass {
}
render(toDrawingBuffer: boolean) {
ValueCell.update(this.renderable.values.uFar, this.camera.state.far)
ValueCell.update(this.renderable.values.uNear, this.camera.state.near)
ValueCell.update(this.renderable.values.uFogFar, this.camera.state.fogFar)
ValueCell.update(this.renderable.values.uFogNear, this.camera.state.fogNear)
ValueCell.update(this.renderable.values.uFar, this.camera.far)
ValueCell.update(this.renderable.values.uNear, this.camera.near)
ValueCell.update(this.renderable.values.uFogFar, this.camera.fogFar)
ValueCell.update(this.renderable.values.uFogNear, this.camera.fogNear)
ValueCell.update(this.renderable.values.dOrthographic, this.camera.state.mode === 'orthographic' ? 1 : 0)
const { gl, state } = this.webgl

View File

@@ -4,16 +4,57 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
/** resize canvas to container element */
/** Set canvas size taking `devicePixelRatio` into account */
export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height: number) {
canvas.width = Math.round(window.devicePixelRatio * width)
canvas.height = Math.round(window.devicePixelRatio * height)
Object.assign(canvas.style, { width: `${width}px`, height: `${height}px` })
}
/** Resize canvas to container element taking `devicePixelRatio` into account */
export function resizeCanvas (canvas: HTMLCanvasElement, container: Element) {
let w = window.innerWidth
let h = window.innerHeight
let width = window.innerWidth
let height = window.innerHeight
if (container !== document.body) {
let bounds = container.getBoundingClientRect()
w = bounds.right - bounds.left
h = bounds.bottom - bounds.top
width = bounds.right - bounds.left
height = bounds.bottom - bounds.top
}
canvas.width = window.devicePixelRatio * w
canvas.height = window.devicePixelRatio * h
Object.assign(canvas.style, { width: `${w}px`, height: `${h}px` })
setCanvasSize(canvas, width, height)
}
function _canvasToBlob(canvas: HTMLCanvasElement, callback: BlobCallback, type?: string, quality?: any) {
const bin = atob(canvas.toDataURL(type, quality).split(',')[1])
const len = bin.length
const len32 = len >> 2
const a8 = new Uint8Array(len)
const a32 = new Uint32Array(a8.buffer, 0, len32)
let j = 0
for (let i = 0; i < len32; ++i) {
a32[i] = bin.charCodeAt(j++) |
bin.charCodeAt(j++) << 8 |
bin.charCodeAt(j++) << 16 |
bin.charCodeAt(j++) << 24
}
let tailLength = len & 3;
while (tailLength--) a8[j] = bin.charCodeAt(j++)
callback(new Blob([a8], { type: type || 'image/png' }));
}
export async function canvasToBlob(canvas: HTMLCanvasElement, type?: string, quality?: any): Promise<Blob> {
return new Promise((resolve, reject) => {
const callback = (blob: Blob | null) => {
if (blob) resolve(blob)
else reject('no blob returned')
}
if (!HTMLCanvasElement.prototype.toBlob) {
_canvasToBlob(canvas, callback, type, quality)
} else {
canvas.toBlob(callback, type, quality)
}
})
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -7,6 +7,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 { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../mol-io/reader/common/text/number-parser';
interface Column<T> {
readonly schema: Column.Schema,
@@ -71,6 +73,7 @@ namespace Column {
rowCount: number,
schema: T,
valueKind?: (row: number) => ValueKind,
areValuesEqual?: (rowA: number, rowB: number) => boolean
}
export interface ArraySpec<T extends Schema> {
@@ -128,6 +131,37 @@ namespace Column {
return arrayColumn({ array, schema: Schema.str });
}
export function ofIntTokens(tokens: Tokens) {
const { count, data, indices } = tokens
return lambdaColumn({
value: (row: number) => fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0,
rowCount: count,
schema: Schema.int,
});
}
export function ofFloatTokens(tokens: Tokens) {
const { count, data, indices } = tokens
return lambdaColumn({
value: (row: number) => fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0,
rowCount: count,
schema: Schema.float,
});
}
export function ofStringTokens(tokens: Tokens) {
const { count, data, indices } = tokens
return lambdaColumn({
value: (row: number) => {
const ret = data.substring(indices[2 * row], indices[2 * row + 1]);
if (ret === '.' || ret === '?') return '';
return ret;
},
rowCount: count,
schema: Schema.str,
});
}
export function window<T>(column: Column<T>, start: number, end: number) {
return windowColumn(column, start, end);
}
@@ -214,7 +248,7 @@ function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schem
}
}
function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqual, rowCount, schema }: Column.LambdaSpec<T>): Column<T['T']> {
return {
schema: schema,
__array: void 0,
@@ -227,7 +261,7 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, rowCount, sch
for (let i = 0, _i = array.length; i < _i; i++) array[i] = value(i + start);
return array;
},
areValuesEqual: (rowA, rowB) => value(rowA) === value(rowB)
areValuesEqual: areValuesEqual ? areValuesEqual : (rowA, rowB) => value(rowA) === value(rowB)
}
}
@@ -268,7 +302,7 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
}
}
function windowColumn<T>(column: Column<T>, start: number, end: number) {
function windowColumn<T>(column: Column<T>, start: number, end: number): Column<T> {
if (!column.isDefined) return Column.Undefined(end - start, column.schema);
if (start === 0 && end === column.rowCount) return column;
if (!!column.__array && ColumnHelpers.isTypedArray(column.__array)) return windowTyped(column, start, end);
@@ -309,9 +343,9 @@ function isIdentity(map: ArrayLike<number>, rowCount: number) {
}
function columnView<T>(c: Column<T>, map: ArrayLike<number>, checkIdentity: boolean): Column<T> {
if (!c.isDefined) return c;
if (!c.isDefined || c.rowCount === 0) return c;
if (checkIdentity && isIdentity(map, c.rowCount)) return c;
if (!!c.__array) return arrayView(c, map);
if (!!c.__array && typeof c.value(0) === typeof c.__array[0]) return arrayView(c, map);
return viewFull(c, map);
}

View File

@@ -218,6 +218,16 @@ namespace Table {
return ret;
}
export function toArrays<S extends Schema>(table: Table<S>) {
const arrays: { [k: string]: ArrayLike<any> } = {}
const { _columns } = table;
for (let i = 0; i < _columns.length; i++) {
const c = _columns[i]
arrays[c] = table[c].toArray();
}
return arrays as { [k in keyof S]: ArrayLike<S[k]['T']> }
}
export function formatToString<S extends Schema>(table: Table<S>) {
const sb = StringBuilder.create();

View File

@@ -20,6 +20,10 @@ namespace UniqueArray {
array[array.length] = value;
return true;
}
export function has<K, T>({ keys }: UniqueArray<K, T>, key: K) {
return keys.has(key);
}
}
export { UniqueArray }

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -81,6 +81,13 @@ describe('ordered set', () => {
expect(OrderedSet.isSubset(arr136, OrderedSet.ofSortedArray([12, 13, 16]))).toBe(false);
});
it('isSubsetIS', () => {
expect(OrderedSet.isSubset(
Interval.ofRange(1271, 1295),
OrderedSet.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
)).toBe(true);
});
it('access/membership', () => {
expect(OrderedSet.has(empty, 10)).toBe(false);
expect(OrderedSet.indexOf(empty, 10)).toBe(-1);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -65,6 +65,90 @@ describe('sortedArray', () => {
test('intersectionSize', SortedArray.intersectionSize(a1234, a2468), 2);
// console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
// console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
it('union1', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 836, 837, 838, 839, 840, 841, 842, 843]),
SortedArray.ofSortedArray([835])
),
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
)
})
it('union2', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([830, 832, 833]),
SortedArray.ofSortedArray([831])
),
SortedArray.ofSortedArray([830, 831, 832, 833])
)
})
it('union3ab', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835]),
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843])
),
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
)
})
it('union3ba', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([836, 837, 838, 839, 840, 841, 842, 843]),
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835])
),
SortedArray.ofSortedArray([830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843])
)
})
it('union4', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([1, 3, 5, 7, 9]),
SortedArray.ofSortedArray([2, 4, 6, 8])
),
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6, 7, 8, 9])
)
})
it('union5', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([2, 3, 4, 20, 21, 22]),
SortedArray.ofSortedArray([10, 11, 12])
),
SortedArray.ofSortedArray([2, 3, 4, 10, 11, 12, 20, 21, 22])
)
})
it('union6', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819]),
SortedArray.ofSortedArray([1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758])
),
SortedArray.ofSortedArray([768, 769, 770, 771, 772, 773, 774, 775, 776, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819])
)
})
it('union7', () => {
compareArrays(
SortedArray.union(
SortedArray.ofSortedArray([3766, 3767, 3768, 3770, 3773, 3780, 3783, 3787, 3797]),
SortedArray.ofSortedArray([3769, 3790, 3794])
),
SortedArray.ofSortedArray([3766, 3767, 3768, 3769, 3770, 3773, 3780, 3783, 3787, 3790, 3794, 3797])
)
})
it('isSubset', () => {
expect(SortedArray.isSubset(
SortedArray.ofSortedArray([1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295]),
SortedArray.ofSortedArray([1271, 1272, 1274, 1275, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294])
)).toBe(true);
})
});

View File

@@ -17,7 +17,7 @@ describe('rangesArray', () => {
it(`iterator, ${name}`, () => {
const rangesIt = SortedRanges.transientSegments(ranges, set)
const { index, start, end } = expectedValues
let i = 0
while (rangesIt.hasNext) {
const segment = rangesIt.move()
@@ -41,7 +41,7 @@ describe('rangesArray', () => {
testIterator('two ranges',
SortedRanges.ofSortedRanges([1, 2, 3, 4]),
OrderedSet.ofBounds(1, 4),
OrderedSet.ofBounds(1, 5),
{ index: [0, 1], start: [0, 2], end: [2, 4] }
)
testIterator('first range',
@@ -62,7 +62,7 @@ describe('rangesArray', () => {
testIterator('set in second range and beyond',
SortedRanges.ofSortedRanges([1, 2, 3, 4]),
SortedArray.ofSortedArray([3, 10]),
{ index: [1], start: [0], end: [2] }
{ index: [1], start: [0], end: [1] }
)
testIterator('length 1 range',
SortedRanges.ofSortedRanges([1, 1, 3, 4]),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -9,6 +9,7 @@ import Tuple from '../tuple'
export const Empty = Tuple.Zero;
export function ofRange(min: number, max: number) { return max < min ? Tuple.create(min, min) : Tuple.create(min, max + 1); }
export function ofBounds(start: number, end: number) { return end <= start ? Tuple.create(start, start) : Tuple.create(start, end); }
export function ofLength(length: number) { return length < 0 ? Tuple.create(0, 0) : Tuple.create(0, length); }
export const is = Tuple.is;
export const start = Tuple.fst;
@@ -17,6 +18,7 @@ export const min = Tuple.fst;
export function max(i: Tuple) { return Tuple.snd(i) - 1; }
export function size(i: Tuple) { return Tuple.snd(i) - Tuple.fst(i); }
export const hashCode = Tuple.hashCode;
export const toString = Tuple.toString;
export function has(int: Tuple, v: number) { return Tuple.fst(int) <= v && v < Tuple.snd(int); }
/** Returns the index of `x` in `set` or -1 if not found. */

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -36,6 +36,8 @@ export function end(set: OrderedSetImpl) { return I.is(set) ? I.end(set) : S.end
export function hashCode(set: OrderedSetImpl) { return I.is(set) ? I.hashCode(set) : S.hashCode(set); }
// TODO: possibly add more hash functions to allow for multilevel hashing.
export function toString(set: OrderedSetImpl) { return I.is(set) ? I.toString(set) : S.toString(set); }
export function areEqual(a: OrderedSetImpl, b: OrderedSetImpl) {
if (I.is(a)) {
if (I.is(b)) return I.areEqual(a, b);
@@ -124,7 +126,7 @@ function isSubsetIS(a: I, b: S) {
const minA = I.min(a), maxA = I.max(a);
if (maxA - minA + 1 === 0) return false;
const minB = S.min(b), maxB = S.max(b);
return minB >= minA && maxA <= maxB;
return minB >= minA && maxB <= maxA;
}
function areRangesIntersecting(a: OrderedSetImpl, b: OrderedSetImpl) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -35,6 +35,11 @@ export function hashCode(xs: Nums) {
if (s > 2) return hash4(s, xs[0], xs[s - 1], xs[s >> 1]);
return hash3(s, xs[0], xs[s - 1]);
}
export function toString(xs: Nums) {
const s = xs.length;
if (s > 5) return `[${xs[0]}, ${xs[1]}, ..., ${xs[s - 1]}], length ${s}`;
return `[${(xs as number[]).join(', ')}]`;
}
/** Returns the index of `x` in `set` or -1 if not found. */
export function indexOf(xs: Nums, v: number) {
@@ -42,8 +47,10 @@ export function indexOf(xs: Nums, v: number) {
return l === 0 ? -1 : xs[0] <= v && v <= xs[l - 1] ? binarySearchRange(xs, v, 0, l) : -1;
}
export function indexOfInInterval(xs: Nums, v: number, bounds: Interval) {
return indexOfInRange(xs, v, Interval.start(bounds), Interval.end(bounds))
}
export function indexOfInRange(xs: Nums, v: number, s: number, e: number) {
const l = xs.length;
const s = Interval.start(bounds), e = Interval.end(bounds);
return l === 0 || e <= s ? -1 : xs[s] <= v && v <= xs[e - 1] ? binarySearchRange(xs, v, s, e) : -1;
}
export function has(xs: Nums, v: number) { return indexOf(xs, v) >= 0; }
@@ -60,6 +67,11 @@ export function areEqual(a: Nums, b: Nums) {
return true;
}
/**
* Returns 0 if `v` is smaller or equal the first element of `xs`
* Returns length of `xs` if `v` is bigger than the last element of `xs`
* Otherwise returns the first index where the value of `xs` is equal or bigger than `v`
*/
export function findPredecessorIndex(xs: Nums, v: number) {
const len = xs.length;
if (v <= xs[0]) return 0;
@@ -159,28 +171,30 @@ export function isSubset(a: Nums, b: Nums) {
return equal === lenB;
}
export function union(a: Nums, b: Nums) {
export function union(a: Nums, b: Nums): Nums {
if (a === b) return a;
const lenA = a.length, lenB = b.length;
if (lenA === 0) return b;
if (lenB === 0) return a;
if (a[0] > b[0]) return union(b, a);
const { startI, startJ, endI, endJ } = getSuitableIntersectionRange(a, b);
const commonCount = getCommonCount(a, b, startI, startJ, endI, endJ);
const lenA = a.length, lenB = b.length;
// A === B || B is subset of A ==> A
if ((commonCount === lenA && commonCount === lenB) || commonCount === lenB) return a;
// A is subset of B ===> B
if (commonCount === lenA) return b;
const indices = new Int32Array(lenA + lenB - commonCount);
let offset = 0;
let i = 0, j = 0, offset = 0;
// insert the "prefixes"
for (let k = 0; k < startI; k++) indices[offset++] = a[k];
for (let k = 0; k < startJ; k++) indices[offset++] = b[k];
for (i = 0; i < startI; i++) indices[offset++] = a[i];
while (j < endJ && a[startI] > b[j]) indices[offset++] = b[j++];
// insert the common part
let i = startI;
let j = startJ;
while (i < endI && j < endJ) {
const x = a[i], y = b[j];
if (x < y) { indices[offset++] = x; i++; }
@@ -188,6 +202,10 @@ export function union(a: Nums, b: Nums) {
else { indices[offset++] = x; i++; j++; }
}
// insert the remaining common part
for (; i < endI; i++) indices[offset++] = a[i];
for (; j < endJ; j++) indices[offset++] = b[j];
// insert the "tail"
for (; i < lenA; i++) indices[offset++] = a[i];
for (; j < lenB; j++) indices[offset++] = b[j];

View File

@@ -14,6 +14,8 @@ namespace Interval {
export const ofRange: <T extends number = number>(min: T, max: T) => Interval<T> = Impl.ofRange as any;
/** Create interval from bounds [start, end), i.e. [start, end - 1] */
export const ofBounds: <T extends number = number>(start: T, end: T) => Interval<T> = Impl.ofBounds as any;
/** Create interval from length [0, length), i.e. [0, length - 1] */
export const ofLength: <T extends number = number>(length: T) => Interval<T> = Impl.ofLength as any;
export const is: <T extends number = number>(v: any) => v is Interval<T> = Impl.is as any;
/** Test if a value is within the bounds of the interval */
@@ -34,6 +36,8 @@ namespace Interval {
export const size: <T extends number = number>(interval: Interval<T>) => number = Impl.size as any;
/** Hash code describing the interval */
export const hashCode: <T extends number = number>(interval: Interval<T>) => number = Impl.hashCode as any;
/** String representation of the interval */
export const toString: <T extends number = number>(interval: Interval<T>) => string = Impl.toString as any;
/** Test if two intervals are identical */
export const areEqual: <T extends number = number>(a: Interval<T>, b: Interval<T>) => boolean = Impl.areEqual as any;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -8,7 +8,6 @@ import * as Base from './impl/ordered-set'
import Interval from './interval'
import SortedArray from './sorted-array';
/** test */
namespace OrderedSet {
export const Empty: OrderedSet = Base.Empty as any;
export const ofSingleton: <T extends number = number>(value: T) => OrderedSet<T> = Base.ofSingleton as any;
@@ -22,12 +21,14 @@ namespace OrderedSet {
export const has: <T extends number = number>(set: OrderedSet<T>, x: T) => boolean = Base.has as any;
/** Returns the index of `x` in `set` or -1 if not found. */
export const indexOf: <T extends number = number>(set: OrderedSet<T>, x: T) => number = Base.indexOf as any;
/** Returns the value in `set` at index `i`. */
export const getAt: <T extends number = number>(set: OrderedSet<T>, i: number) => T = Base.getAt as any;
export const min: <T extends number = number>(set: OrderedSet<T>) => T = Base.min as any;
export const max: <T extends number = number>(set: OrderedSet<T>) => T = Base.max as any;
export const start: <T extends number = number>(set: OrderedSet<T>) => T = Base.start as any;
export const end: <T extends number = number>(set: OrderedSet<T>) => T = Base.end as any;
/** Number of elements in the OrderedSet */
export const size: <T extends number = number>(set: OrderedSet<T>) => number = Base.size as any;
export const hashCode: <T extends number = number>(set: OrderedSet<T>) => number = Base.hashCode as any;
@@ -38,8 +39,14 @@ namespace OrderedSet {
export const union: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.union as any;
export const intersect: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.intersect as any;
/** Returns elements of `a` that are not in `b`, i.e `a` - `b` */
export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any;
/**
* Returns 0 if `x` is smaller or equal the first element of `set`
* Returns length of `set` if `x` is bigger than the last element of `set`
* Otherwise returns the first index where the value of `set` is equal or bigger than `x`
*/
export const findPredecessorIndex: <T extends number = number>(set: OrderedSet<T>, x: number) => number = Base.findPredecessorIndex as any;
export const findPredecessorIndexInInterval: <T extends number = number>(set: OrderedSet<T>, x: T, range: Interval) => number = Base.findPredecessorIndexInInterval as any;
export const findRange: <T extends number = number>(set: OrderedSet<T>, min: T, max: T) => Interval = Base.findRange as any;
@@ -62,10 +69,12 @@ namespace OrderedSet {
OrderedSet.forEach(set, v => array.push(v))
return array
}
export function toString<T extends number = number>(set: OrderedSet<T>): string {
return Base.toString(set)
}
}
/** Represents bla */
type OrderedSet<T extends number = number> = SortedArray<T> | Interval<T>
export default OrderedSet

View File

@@ -18,7 +18,7 @@ namespace Segmentation {
export const getSegment: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, value: T) => number = Impl.getSegment as any;
export const projectValue: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any;
// Segment iterator that mutates a single segment object to mark all the segments.
/** Segment iterator that mutates a single segment object to mark all the segments. */
export const transientSegments: <T extends number = number, I extends number = number>(segs: Segmentation<T, I>, set: OrderedSet<T>, segment?: Segment) => Impl.SegmentIterator<I> = Impl.segments as any;
export type SegmentIterator<I extends number = number> = Impl.SegmentIterator<I>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -12,9 +12,9 @@ namespace SortedArray {
export const ofUnsortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofUnsortedArray as any;
export const ofSingleton: <T extends number = number>(v: number) => SortedArray<T> = Impl.ofSingleton as any;
export const ofSortedArray: <T extends number = number>(xs: ArrayLike<number>) => SortedArray<T> = Impl.ofSortedArray as any;
// create sorted array [min, max] (it DOES contain the max value)
/** create sorted array [min, max] (it DOES contain the max value) */
export const ofRange: <T extends number = number>(min: T, max: T) => SortedArray<T> = Impl.ofRange as any;
// create sorted array [min, max) (it does NOT contain the max value)
/** create sorted array [min, max) (it does NOT contain the max value) */
export const ofBounds: <T extends number = number>(min: T, max: T) => SortedArray<T> = (min, max) => Impl.ofRange(min, max - 1) as any;
export const is: <T extends number = number>(v: any) => v is SortedArray<T> = Impl.is as any;
@@ -22,15 +22,17 @@ namespace SortedArray {
/** Returns the index of `x` in `set` or -1 if not found. */
export const indexOf: <T extends number = number>(array: SortedArray<T>, x: T) => number = Impl.indexOf as any;
export const indexOfInInterval: <T extends number = number>(array: SortedArray<T>, x: number, bounds: Interval) => number = Impl.indexOfInInterval as any;
export const indexOfInRange: <T extends number = number>(array: SortedArray<T>, x: number, start: number, end: number) => number = Impl.indexOfInRange as any;
// array[0]
/** Returns `array[0]` */
export const start: <T extends number = number>(array: SortedArray<T>) => T = Impl.start as any;
// array[array.length - 1] + 1
/** Returns `array[array.length - 1] + 1` */
export const end: <T extends number = number>(array: SortedArray<T>) => T = Impl.end as any;
export const min: <T extends number = number>(array: SortedArray<T>) => T = Impl.min as any;
export const max: <T extends number = number>(array: SortedArray<T>) => T = Impl.max as any;
export const size: <T extends number = number>(array: SortedArray<T>) => number = Impl.size as any;
export const hashCode: <T extends number = number>(array: SortedArray<T>) => number = Impl.hashCode as any;
export const toString: <T extends number = number>(array: SortedArray<T>) => string = Impl.toString as any;
export const areEqual: <T extends number = number>(a: SortedArray<T>, b: SortedArray<T>) => boolean = Impl.areEqual as any;
export const areIntersecting: <T extends number = number>(a: SortedArray<T>, b: SortedArray<T>) => boolean = Impl.areIntersecting as any;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -23,6 +23,64 @@ namespace SortedRanges {
}
return size
}
export function count<T extends number = number>(ranges: SortedRanges<T>) { return ranges.length / 2 }
export function startAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
return ranges[index * 2]
}
export function endAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
return ranges[index * 2 + 1] + 1
}
export function minAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
return ranges[index * 2]
}
export function maxAt<T extends number = number>(ranges: SortedRanges<T>, index: number) {
return ranges[index * 2 + 1]
}
export function areEqual<T extends number = number>(a: SortedRanges<T>, b: SortedRanges<T>) {
if (a.length !== b.length) return false
for (let i = 0, il = a.length; i < il; ++i) {
if (a[i] !== b[i]) return false
}
return true
}
export function forEach<T extends number = number>(ranges: SortedRanges<T>, f: (value: T, i: number) => void) {
let k = 0
for (let i = 0, il = ranges.length; i < il; i += 2) {
for (let j = ranges[i], jl = ranges[i + 1]; j <= jl; ++j) {
f(j, k);
++k
}
}
}
/** Returns if a value of `set` is included in `ranges` */
export function has<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
return firstIntersectionIndex(ranges, set) !== -1
}
/** Returns if a value of `set` is included in `ranges` from given index */
export function hasFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number) {
return firstIntersectionIndexFrom(ranges, set, from) !== -1
}
export function firstIntersectionIndex<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>): number {
return firstIntersectionIndexFrom(ranges, set, 0)
}
export function firstIntersectionIndexFrom<T extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>, from: number): number {
if (minAt(ranges, from) > OrderedSet.max(set) || max(ranges) < OrderedSet.min(set)) return -1
for (let i = from, il = count(ranges); i < il; ++i) {
const interval = Interval.ofRange(minAt(ranges, i), maxAt(ranges, i))
if (OrderedSet.areIntersecting(interval, set)) return i
}
return -1
}
export function transientSegments<T extends number = number, I extends number = number>(ranges: SortedRanges<T>, set: OrderedSet<T>) {
return new Iterator<T, I>(ranges, set)
@@ -32,53 +90,27 @@ namespace SortedRanges {
private value: Segmentation.Segment<I> = { index: 0 as I, start: 0 as T, end: 0 as T }
private curIndex = 0
private maxIndex = 0
private interval: Interval<T>
private curMin: T = 0 as T
hasNext: boolean = false;
updateInterval() {
this.interval = Interval.ofRange(this.ranges[this.curIndex], this.ranges[this.curIndex + 1])
}
updateValue() {
this.value.index = this.curIndex / 2 as I
this.value.start = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex])
this.value.end = OrderedSet.findPredecessorIndex(this.set, this.ranges[this.curIndex + 1]) + 1
private updateValue() {
this.value.index = this.curIndex as I
this.value.start = OrderedSet.findPredecessorIndex(this.set, startAt(this.ranges, this.curIndex))
this.value.end = OrderedSet.findPredecessorIndex(this.set, endAt(this.ranges, this.curIndex))
}
move() {
if (this.hasNext) {
this.updateValue()
while (this.curIndex <= this.maxIndex) {
this.curIndex += 2
this.curMin = Interval.end(this.interval)
this.updateInterval()
if (Interval.min(this.interval) >= this.curMin && OrderedSet.areIntersecting(this.interval, this.set)) break
}
this.hasNext = this.curIndex <= this.maxIndex
this.curIndex = firstIntersectionIndexFrom(this.ranges, this.set, this.curIndex + 1)
this.hasNext = this.curIndex !== -1
}
return this.value;
}
getRangeIndex(value: number) {
const index = SortedArray.findPredecessorIndex(this.ranges, value)
return (index % 2 === 1) ? index - 1 : index
}
constructor(private ranges: SortedRanges<T>, private set: OrderedSet<T>) {
// TODO cleanup, refactor to make it clearer
const min = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.min(set))
const max = SortedArray.findPredecessorIndex(this.ranges, OrderedSet.max(set) + 1)
if (ranges.length && min !== max) {
this.curIndex = this.getRangeIndex(OrderedSet.min(set))
this.maxIndex = Math.min(ranges.length - 2, this.getRangeIndex(OrderedSet.max(set)))
this.curMin = this.ranges[this.curIndex]
this.updateInterval()
}
this.hasNext = ranges.length > 0 && min !== max && this.curIndex <= this.maxIndex
this.curIndex = firstIntersectionIndex(ranges, set)
this.hasNext = this.curIndex !== -1
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -73,6 +73,11 @@ namespace IntTuple {
_float64[0] = t as any;
return hash2(_int32[0], _int32[1]);
}
export function toString(t: IntTuple) {
_float64[0] = t as any;
return `(${_int32[0]}, ${_int32[1]})`;
}
}
export default IntTuple

View File

@@ -73,7 +73,7 @@ export function sortedCantorPairing(a: number, b: number) {
/**
* 32 bit FNV-1a hash, see http://isthe.com/chongo/tech/comp/fnv/
*/
export function hashFnv32a(array: number[]) {
export function hashFnv32a(array: ArrayLike<number>) {
let hval = 0x811c9dc5;
for (let i = 0, il = array.length; i < il; ++i) {
hval ^= array[i];

View File

@@ -13,7 +13,7 @@ import { Color } from '../../mol-util/color';
import { Vec3 } from '../../mol-math/linear-algebra';
import { TransformData, createIdentityTransform } from './transform-data';
import { Theme } from '../../mol-theme/theme';
import { ColorNames } from '../../mol-util/color/tables';
import { ColorNames } from '../../mol-util/color/names';
import { NullLocation } from '../../mol-model/location';
import { UniformColorTheme } from '../../mol-theme/color/uniform';
import { UniformSizeTheme } from '../../mol-theme/size/uniform';

View File

@@ -21,7 +21,7 @@ import { transformPositionArray } from '../../../mol-geo/util';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { Theme } from '../../../mol-theme/theme';
import { RenderableState } from '../../../mol-gl/renderable';
import { ColorListOptions, ColorListName } from '../../../mol-util/color/scale';
import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
@@ -81,7 +81,7 @@ export namespace DirectVolume {
Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
]),
list: PD.ColorScale<ColorListName>('RedYellowBlue', ColorListOptions),
list: PD.ColorList<ColorListName>('red-yellow-blue', ColorListOptions),
}
export type Params = typeof Params

View File

@@ -9,7 +9,7 @@ import { spline } from '../../../mol-math/interpolate';
import { ColorScale, Color } from '../../../mol-util/color';
import { ValueCell } from '../../../mol-util';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { ColorListName } from '../../../mol-util/color/scale';
import { ColorListName } from '../../../mol-util/color/lists';
export interface ControlPoint { x: number, alpha: number }

View File

@@ -177,8 +177,8 @@ export namespace Lines {
}
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount)
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount)
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount, 0, 4)
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount, 0, 4)
return {
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)

View File

@@ -13,57 +13,6 @@ export type MarkerData = {
uMarkerTexDim: ValueCell<Vec2>
}
export enum MarkerAction {
Highlight,
RemoveHighlight,
Select,
Deselect,
Toggle,
Clear
}
export function applyMarkerAction(array: Uint8Array, start: number, end: number, action: MarkerAction) {
let changed = false
for (let i = start; i < end; ++i) {
let v = array[i]
switch (action) {
case MarkerAction.Highlight:
if (v % 2 === 0) {
v += 1
}
break
case MarkerAction.RemoveHighlight:
if (v % 2 !== 0) {
v -= 1
}
break
case MarkerAction.Select:
if (v < 2) v += 2
// v += 2
break
case MarkerAction.Deselect:
// if (v >= 2) {
// v -= 2
// }
v = v % 2
break
case MarkerAction.Toggle:
if (v >= 2) {
v -= 2
} else {
v += 2
}
break
case MarkerAction.Clear:
v = 0
break
}
changed = array[i] !== v || changed
array[i] = v
}
return changed
}
export function createMarkers(count: number, markerData?: MarkerData): MarkerData {
const markers = createTextureImage(Math.max(1, count), 1, Uint8Array, markerData && markerData.tMarker.ref.value.array)
if (markerData) {

View File

@@ -1,13 +1,15 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
import { MeshBuilder } from '../mesh-builder';
import { Primitive } from '../../../primitive/primitive';
import { Primitive, transformPrimitive } from '../../../primitive/primitive';
import { Cylinder, CylinderProps } from '../../../primitive/cylinder';
import { Prism } from '../../../primitive/prism';
import { polygon } from '../../../primitive/polygon';
const cylinderMap = new Map<string, Primitive>()
const up = Vec3.create(0, 1, 0)
@@ -24,8 +26,7 @@ function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number) {
Vec3.add(tmpCylinderCenter, start, tmpCylinderMatDir)
// ensure the direction used to create the rotation is always pointing in the same
// direction so the triangles of adjacent cylinder will line up
Vec3.copy(tmpUp, up)
if (Vec3.dot(tmpCylinderMatDir, tmpUp) < 0) Vec3.scale(tmpUp, tmpUp, -1)
Vec3.matchDirection(tmpUp, up, tmpCylinderMatDir)
Vec3.makeRotation(m, tmpUp, tmpCylinderMatDir)
return Mat4.setTranslation(m, tmpCylinderCenter)
}
@@ -34,7 +35,12 @@ function getCylinder(props: CylinderProps) {
const key = JSON.stringify(props)
let cylinder = cylinderMap.get(key)
if (cylinder === undefined) {
cylinder = Cylinder(props)
if (props.radialSegments && props.radialSegments <= 4) {
const box = Prism(polygon(4, true, props.radiusTop), props)
cylinder = transformPrimitive(box, Mat4.rotX90)
} else {
cylinder = Cylinder(props)
}
cylinderMap.set(key, cylinder)
}
return cylinder

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
import { MeshBuilder } from '../mesh-builder';
import { getSphere } from './sphere';
const tmpEllipsoidMat = Mat4.identity()
const tmpVec = Vec3()
function setEllipsoidMat(m: Mat4, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3) {
Vec3.add(tmpVec, center, dirMajor)
Mat4.targetTo(m, center, tmpVec, dirMinor)
Mat4.setTranslation(m, center)
return Mat4.scale(m, m, radiusScale)
}
export function addEllipsoid(state: MeshBuilder.State, center: Vec3, dirMajor: Vec3, dirMinor: Vec3, radiusScale: Vec3, detail: number) {
MeshBuilder.addPrimitive(state, setEllipsoidMat(tmpEllipsoidMat, center, dirMajor, dirMinor, radiusScale), getSphere(detail))
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Vec3 } from '../../../../mol-math/linear-algebra';
import { ChunkedArray } from '../../../../mol-data/util';
import { MeshBuilder } from '../mesh-builder';
const tA = Vec3.zero()
const tB = Vec3.zero()
const tV = Vec3.zero()
const horizontalVector = Vec3.zero()
const verticalVector = Vec3.zero()
const normalOffset = Vec3.zero()
const positionVector = Vec3.zero()
const normalVector = Vec3.zero()
const torsionVector = Vec3.zero()
/** set arrowHeight = 0 for no arrow */
export function addRibbon(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number) {
const { currentGroup, vertices, normals, indices, groups } = state
let vertexCount = vertices.elementCount
let offsetLength = 0
if (arrowHeight > 0) {
Vec3.fromArray(tA, controlPoints, 0)
Vec3.fromArray(tB, controlPoints, linearSegments * 3)
offsetLength = arrowHeight / Vec3.magnitude(Vec3.sub(tV, tB, tA))
}
for (let i = 0; i <= linearSegments; ++i) {
const width = widthValues[i]
const height = heightValues[i]
const actualHeight = arrowHeight === 0 ? height : arrowHeight * (1 - i / linearSegments);
const i3 = i * 3
Vec3.fromArray(verticalVector, normalVectors, i3)
Vec3.scale(verticalVector, verticalVector, actualHeight);
Vec3.fromArray(horizontalVector, binormalVectors, i3)
Vec3.scale(horizontalVector, horizontalVector, width);
if (arrowHeight > 0) {
Vec3.fromArray(tA, normalVectors, i3)
Vec3.fromArray(tB, binormalVectors, i3)
Vec3.scale(normalOffset, Vec3.cross(normalOffset, tA, tB), offsetLength)
}
Vec3.fromArray(positionVector, controlPoints, i3)
Vec3.fromArray(normalVector, normalVectors, i3)
Vec3.fromArray(torsionVector, binormalVectors, i3)
Vec3.add(tA, positionVector, verticalVector)
Vec3.negate(tB, torsionVector)
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
Vec3.sub(tA, positionVector, verticalVector)
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
Vec3.add(tA, positionVector, verticalVector)
Vec3.copy(tB, torsionVector)
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
Vec3.sub(tA, positionVector, verticalVector)
ChunkedArray.add3(vertices, tA[0], tA[1], tA[2])
ChunkedArray.add3(normals, tB[0], tB[1], tB[2])
}
for (let i = 0; i < linearSegments; ++i) {
ChunkedArray.add3(
indices,
vertexCount + i * 4,
vertexCount + (i + 1) * 4 + 1,
vertexCount + i * 4 + 1
);
ChunkedArray.add3(
indices,
vertexCount + i * 4,
vertexCount + (i + 1) * 4,
vertexCount + (i + 1) * 4 + 1
);
ChunkedArray.add3(
indices,
vertexCount + i * 4 + 2 + 1,
vertexCount + (i + 1) * 4 + 2 + 1,
vertexCount + i * 4 + 2
);
ChunkedArray.add3(
indices,
vertexCount + i * 4 + 2,
vertexCount + (i + 1) * 4 + 2 + 1,
vertexCount + (i + 1) * 4 + 2
);
}
const addedVertexCount = (linearSegments + 1) * 4
for (let i = 0, il = addedVertexCount; i < il; ++i) ChunkedArray.add(groups, currentGroup)
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -71,7 +71,7 @@ function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLi
}
/** set arrowHeight = 0 for no arrow */
export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, width: number, height: number, arrowHeight: number, startCap: boolean, endCap: boolean) {
export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number, startCap: boolean, endCap: boolean) {
const { currentGroup, vertices, normals, indices, groups } = state
let vertexCount = vertices.elementCount
@@ -84,6 +84,9 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb
}
for (let i = 0; i <= linearSegments; ++i) {
const width = widthValues[i]
const height = heightValues[i]
const actualHeight = arrowHeight === 0 ? height : arrowHeight * (1 - i / linearSegments);
const i3 = i * 3
@@ -141,32 +144,55 @@ export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<numb
}
for (let i = 0; i < linearSegments; ++i) {
for (let j = 0; j < 4; j++) {
// the triangles are arranged such that opposing triangles of the sheet align
// which prevents triangle intersection within tight curves
for (let j = 0; j < 2; j++) {
ChunkedArray.add3(
indices,
vertexCount + i * 8 + 2 * j,
vertexCount + (i + 1) * 8 + 2 * j + 1,
vertexCount + i * 8 + 2 * j + 1
vertexCount + i * 8 + 2 * j, // a
vertexCount + (i + 1) * 8 + 2 * j + 1, // c
vertexCount + i * 8 + 2 * j + 1 // b
);
ChunkedArray.add3(
indices,
vertexCount + i * 8 + 2 * j,
vertexCount + (i + 1) * 8 + 2 * j,
vertexCount + (i + 1) * 8 + 2 * j + 1
vertexCount + i * 8 + 2 * j, // a
vertexCount + (i + 1) * 8 + 2 * j, // d
vertexCount + (i + 1) * 8 + 2 * j + 1 // c
);
}
for (let j = 2; j < 4; j++) {
ChunkedArray.add3(
indices,
vertexCount + i * 8 + 2 * j, // a
vertexCount + (i + 1) * 8 + 2 * j, // d
vertexCount + i * 8 + 2 * j + 1, // b
);
ChunkedArray.add3(
indices,
vertexCount + (i + 1) * 8 + 2 * j, // d
vertexCount + (i + 1) * 8 + 2 * j + 1, // c
vertexCount + i * 8 + 2 * j + 1, // b
);
}
}
if (startCap) {
const width = widthValues[0]
const height = heightValues[0]
const h = arrowHeight === 0 ? height : arrowHeight
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, h, h)
} else if (arrowHeight > 0) {
const width = widthValues[0]
const height = heightValues[0]
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, arrowHeight, -height)
addCap(0, state, controlPoints, normalVectors, binormalVectors, width, -arrowHeight, height)
}
if (endCap && arrowHeight === 0) {
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, height, height)
const width = widthValues[linearSegments]
const height = heightValues[linearSegments]
// use negative height to flip the direction the cap's triangles are facing
addCap(linearSegments * 3, state, controlPoints, normalVectors, binormalVectors, width, -height, -height)
}
const addedVertexCount = (linearSegments + 1) * 8 +

View File

@@ -16,7 +16,7 @@ function setSphereMat(m: Mat4, center: Vec3, radius: number) {
return Mat4.scaleUniformly(m, Mat4.fromTranslation(m, center), radius)
}
function getSphere(detail: number) {
export function getSphere(detail: number) {
let sphere = sphereMap.get(detail)
if (sphere === undefined) {
sphere = Sphere(detail)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -50,7 +50,15 @@ export function addTube(state: MeshBuilder.State, controlPoints: ArrayLike<numbe
const t = 2 * Math.PI * j / radialSegments;
add3AndScale2(surfacePoint, u, v, controlPoint, h * Math.cos(t), w * Math.sin(t))
add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t))
if (radialSegments === 2) {
// add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t))
Vec3.copy(normalVector, v)
console.log(i, t)
Vec3.normalize(normalVector, normalVector)
if (t !== 0 || i % 2 === 0) Vec3.negate(normalVector, normalVector)
} else {
add2AndScale2(normalVector, u, v, w * Math.cos(t), h * Math.sin(t))
}
Vec3.normalize(normalVector, normalVector)
ChunkedArray.add3(vertices, surfacePoint[0], surfacePoint[1], surfacePoint[2]);

View File

@@ -101,9 +101,9 @@ export namespace MeshBuilder {
}
}
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number) {
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number, radialSegments: number) {
const { vertices: va, edges: ea } = cage
const cylinderProps = { radiusTop: radius, radiusBottom: radius }
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments }
for (let i = 0, il = ea.length; i < il; i += 2) {
Vec3.fromArray(tmpVecA, va, ea[i] * 3)
Vec3.fromArray(tmpVecB, va, ea[i + 1] * 3)

View File

@@ -24,7 +24,8 @@ export function applyOverpaintColor(array: Uint8Array, start: number, end: numbe
}
export function clearOverpaint(array: Uint8Array, start: number, end: number) {
array.fill(0, start, end)
array.fill(0, start * 4, end * 4)
return true
}
export function createOverpaint(count: number, overpaintData?: OverpaintData): OverpaintData {

View File

@@ -89,7 +89,7 @@ export namespace Spheres {
const padding = getMaxSize(size)
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
transform.aTransform.ref.value, instanceCount, padding
transform.aTransform.ref.value, instanceCount, padding, 4
)
return {
@@ -130,7 +130,7 @@ export namespace Spheres {
const padding = getMaxSize(values)
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
values.aPosition.ref.value, spheres.sphereCount * 4,
values.aTransform.ref.value, values.instanceCount.ref.value, padding
values.aTransform.ref.value, values.instanceCount.ref.value, padding, 4
)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)

View File

@@ -13,7 +13,7 @@ import { Theme } from '../../../mol-theme/theme';
import { createColors } from '../color-data';
import { createSizes, getMaxSize } from '../size-data';
import { createMarkers } from '../marker-data';
import { ColorNames } from '../../../mol-util/color/tables';
import { ColorNames } from '../../../mol-util/color/names';
import { Sphere3D } from '../../../mol-math/geometry';
import { calculateBoundingSphere, TextureImage, createTextureImage } from '../../../mol-gl/renderable/util';
import { TextValues } from '../../../mol-gl/renderable/text';

View File

@@ -4,6 +4,9 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Mat4, Vec3 } from '../../mol-math/linear-algebra'
import { NumberArray } from '../../mol-util/type-helpers'
export interface Cage {
readonly vertices: ArrayLike<number>
readonly edges: ArrayLike<number>
@@ -11,4 +14,24 @@ export interface Cage {
export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>): Cage {
return { vertices, edges }
}
export function copyCage(cage: Cage): Cage {
return {
vertices: new Float32Array(cage.vertices),
edges: new Uint32Array(cage.edges)
}
}
const tmpV = Vec3.zero()
/** Transform primitive in-place */
export function transformCage(cage: Cage, t: Mat4) {
const { vertices } = cage
for (let i = 0, il = vertices.length; i < il; i += 3) {
// position
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
Vec3.toArray(tmpV, vertices as NumberArray, i)
}
return cage
}

View File

@@ -8,16 +8,18 @@
* Create 3d points for a polygon:
* 3 for a triangle, 4 for a rectangle, 5 for a pentagon, 6 for a hexagon...
*/
export function polygon(sideCount: number, shift: boolean) {
export function polygon(sideCount: number, shift: boolean, radius = -1) {
const points = new Float32Array(sideCount * 3)
const radius = sideCount <= 4 ? Math.sqrt(2) / 2 : 0.6
const r = radius === -1
? (sideCount <= 4 ? Math.sqrt(2) / 2 : 0.6)
: radius
const offset = shift ? 1 : 0
for (let i = 0, il = sideCount; i < il; ++i) {
const c = (i * 2 + offset) / sideCount * Math.PI
points[i * 3] = Math.cos(c) * radius
points[i * 3 + 1] = Math.sin(c) * radius
points[i * 3] = Math.cos(c) * r
points[i * 3 + 1] = Math.sin(c) * r
points[i * 3 + 2] = 0
}
return points

View File

@@ -1,10 +1,12 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../mol-math/linear-algebra';
import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra';
import { getNormalMatrix } from '../util';
import { NumberArray } from '../../mol-util/type-helpers';
export interface Primitive {
vertices: ArrayLike<number>
@@ -28,6 +30,14 @@ export function createPrimitive(vertices: ArrayLike<number>, indices: ArrayLike<
return builder.getPrimitive()
}
export function copyPrimitive(primitive: Primitive): Primitive {
return {
vertices: new Float32Array(primitive.vertices),
normals: new Float32Array(primitive.normals),
indices: new Uint32Array(primitive.indices)
}
}
export interface PrimitiveBuilder {
add(a: Vec3, b: Vec3, c: Vec3): void
getPrimitive(): Primitive
@@ -56,4 +66,22 @@ export function PrimitiveBuilder(triangleCount: number): PrimitiveBuilder {
},
getPrimitive: () => ({ vertices, normals, indices })
}
}
const tmpV = Vec3.zero()
const tmpMat3 = Mat3.zero()
/** Transform primitive in-place */
export function transformPrimitive(primitive: Primitive, t: Mat4) {
const { vertices, normals } = primitive
const n = getNormalMatrix(tmpMat3, t)
for (let i = 0, il = vertices.length; i < il; i += 3) {
// position
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
Vec3.toArray(tmpV, vertices as NumberArray, i)
// normal
Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, normals, i), n)
Vec3.toArray(tmpV, normals as NumberArray, i)
}
return primitive
}

View File

@@ -9,26 +9,39 @@ import { Primitive, PrimitiveBuilder } from './primitive';
import { polygon } from './polygon'
import { Cage } from './cage';
const on = Vec3.create(0, 0, -0.5), op = Vec3.create(0, 0, 0.5)
const a = Vec3.zero(), b = Vec3.zero(), c = Vec3.zero(), d = Vec3.zero()
const on = Vec3(), op = Vec3()
const a = Vec3(), b = Vec3(), c = Vec3(), d = Vec3()
export const DefaultPrismProps = {
height: 1,
topCap: true,
bottomCap: true,
}
export type PrismProps = Partial<typeof DefaultPrismProps>
/**
* Create a prism with a base of 4 or more points
*/
export function Prism(points: ArrayLike<number>): Primitive {
export function Prism(points: ArrayLike<number>, props?: PrismProps): Primitive {
const sideCount = points.length / 3
if (sideCount < 4) throw new Error('need at least 4 points to build a prism')
const { height, topCap, bottomCap } = { ...DefaultPrismProps, ...props };
const count = 4 * sideCount
const builder = PrimitiveBuilder(count)
const halfHeight = height * 0.5
Vec3.set(on, 0, 0, -halfHeight)
Vec3.set(op, 0, 0, halfHeight)
// create sides
for (let i = 0; i < sideCount; ++i) {
const ni = (i + 1) % sideCount
Vec3.set(a, points[i * 3], points[i * 3 + 1], -0.5)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -0.5)
Vec3.set(c, points[ni * 3], points[ni * 3 + 1], 0.5)
Vec3.set(d, points[i * 3], points[i * 3 + 1], 0.5)
Vec3.set(a, points[i * 3], points[i * 3 + 1], -halfHeight)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -halfHeight)
Vec3.set(c, points[ni * 3], points[ni * 3 + 1], halfHeight)
Vec3.set(d, points[i * 3], points[i * 3 + 1], halfHeight)
builder.add(a, b, c)
builder.add(c, d, a)
}
@@ -36,12 +49,16 @@ export function Prism(points: ArrayLike<number>): Primitive {
// create bases
for (let i = 0; i < sideCount; ++i) {
const ni = (i + 1) % sideCount
Vec3.set(a, points[i * 3], points[i * 3 + 1], -0.5)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -0.5)
builder.add(on, b, a)
Vec3.set(a, points[i * 3], points[i * 3 + 1], 0.5)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], 0.5)
builder.add(a, b, op)
if (topCap) {
Vec3.set(a, points[i * 3], points[i * 3 + 1], -halfHeight)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], -halfHeight)
builder.add(on, b, a)
}
if (bottomCap) {
Vec3.set(a, points[i * 3], points[i * 3 + 1], halfHeight)
Vec3.set(b, points[ni * 3], points[ni * 3 + 1], halfHeight)
builder.add(a, b, op)
}
}
return builder.getPrimitive()
@@ -61,29 +78,42 @@ export function PentagonalPrism() {
let hexagonalPrism: Primitive
export function HexagonalPrism() {
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, true))
if (!hexagonalPrism) hexagonalPrism = Prism(polygon(6, false))
return hexagonalPrism
}
let shiftedHexagonalPrism: Primitive
export function ShiftedHexagonalPrism() {
if (!shiftedHexagonalPrism) shiftedHexagonalPrism = Prism(polygon(6, true))
return shiftedHexagonalPrism
}
let heptagonalPrism: Primitive
export function HeptagonalPrism() {
if (!heptagonalPrism) heptagonalPrism = Prism(polygon(7, false))
return heptagonalPrism
}
//
/**
* Create a prism cage
*/
export function PrismCage(points: ArrayLike<number>): Cage {
export function PrismCage(points: ArrayLike<number>, height = 1): Cage {
const sideCount = points.length / 3
// const count = 4 * sideCount
const vertices: number[] = []
const edges: number[] = []
const halfHeight = height * 0.5
let offset = 0
// vertices and side edges
for (let i = 0; i < sideCount; ++i) {
vertices.push(
points[i * 3], points[i * 3 + 1], -0.5,
points[i * 3], points[i * 3 + 1], 0.5
points[i * 3], points[i * 3 + 1], -halfHeight,
points[i * 3], points[i * 3 + 1], halfHeight
)
edges.push(offset, offset + 1)
offset += 2
@@ -115,6 +145,6 @@ export function PentagonalPrismCage() {
let hexagonalPrismCage: Cage
export function HexagonalPrismCage() {
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, true))
if (!hexagonalPrismCage) hexagonalPrismCage = PrismCage(polygon(6, false))
return hexagonalPrismCage
}

View File

@@ -54,6 +54,12 @@ export function Pyramid(points: ArrayLike<number>): Primitive {
return builder.getPrimitive()
}
let triangularPyramid: Primitive
export function TriangularPyramid() {
if (!triangularPyramid) triangularPyramid = Pyramid(polygon(3, true))
return triangularPyramid
}
let octagonalPyramid: Primitive
export function OctagonalPyramid() {
if (!octagonalPyramid) octagonalPyramid = Pyramid(polygon(8, true))

View File

@@ -28,11 +28,9 @@ import { createEmptyTransparency } from '../../mol-geo/geometry/transparency-dat
function createRenderer(gl: WebGLRenderingContext) {
const ctx = createContext(gl)
const camera = new Camera({
near: 0.01,
far: 10000,
position: Vec3.create(0, 0, 50)
})
const renderer = Renderer.create(ctx, camera)
const renderer = Renderer.create(ctx)
return { ctx, camera, renderer }
}
@@ -117,7 +115,7 @@ describe('renderer', () => {
expect(ctx.gl.getParameter(ctx.gl.VIEWPORT)[3]).toBe(48)
})
it('points', () => {
it('points', async () => {
const [ width, height ] = [ 32, 32 ]
const gl = createGl(width, height, { preserveDrawingBuffer: true })
const { ctx } = createRenderer(gl)
@@ -126,6 +124,7 @@ describe('renderer', () => {
const points = createPoints()
scene.add(points)
await scene.commit().run()
expect(ctx.stats.bufferCount).toBe(4);
expect(ctx.stats.textureCount).toBe(5);
expect(ctx.stats.vaoCount).toBe(5);
@@ -133,6 +132,7 @@ describe('renderer', () => {
expect(ctx.shaderCache.count).toBe(10);
scene.remove(points)
await scene.commit().run()
expect(ctx.stats.bufferCount).toBe(0);
expect(ctx.stats.textureCount).toBe(0);
expect(ctx.stats.vaoCount).toBe(0);

View File

@@ -159,6 +159,7 @@ export const GlobalUniformSchema = {
uPixelRatio: UniformSpec('f'),
uViewportHeight: UniformSpec('f'),
uViewport: UniformSpec('v4'),
uViewOffset: UniformSpec('v2'),
uCameraPosition: UniformSpec('v3'),
uNear: UniformSpec('f'),
@@ -167,7 +168,9 @@ export const GlobalUniformSchema = {
uFogFar: UniformSpec('f'),
uFogColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('i'),
uPickingAlphaThreshold: UniformSpec('f'),
uInteriorDarkening: UniformSpec('f'),
}
export type GlobalUniformSchema = typeof GlobalUniformSchema
export type GlobalUniformValues = Values<GlobalUniformSchema> // { [k in keyof GlobalUniformSchema]: ValueCell<any> }

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -80,14 +80,15 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
const v = Vec3.zero()
const boundaryHelper = new BoundaryHelper()
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number): Sphere3D {
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
const step = stepFactor * 3
boundaryHelper.reset(0)
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
Vec3.fromArray(v, position, i)
boundaryHelper.boundaryStep(v, 0)
}
boundaryHelper.finishBoundaryStep()
for (let i = 0, _i = positionCount * 3; i < _i; i += 3) {
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
Vec3.fromArray(v, position, i)
boundaryHelper.extendStep(v, 0)
}
@@ -109,8 +110,8 @@ export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere
return boundaryHelper.getSphere()
}
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount)
export function calculateBoundingSphere(position: Float32Array, positionCount: number, transform: Float32Array, transformCount: number, padding = 0, stepFactor = 1): { boundingSphere: Sphere3D, invariantBoundingSphere: Sphere3D } {
const invariantBoundingSphere = calculateInvariantBoundingSphere(position, positionCount, stepFactor)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform, transformCount)
Sphere3D.expand(boundingSphere, boundingSphere, padding)
Sphere3D.expand(invariantBoundingSphere, invariantBoundingSphere, padding)

View File

@@ -9,7 +9,7 @@ import { Camera } from '../mol-canvas3d/camera';
import Scene from './scene';
import { WebGLContext } from './webgl/context';
import { Mat4, Vec3, Vec4 } from '../mol-math/linear-algebra';
import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
import { Renderable } from './renderable';
import { Color } from '../mol-util/color';
import { ValueCell } from '../mol-util';
@@ -38,53 +38,59 @@ interface Renderer {
readonly props: Readonly<RendererProps>
clear: () => void
render: (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => void
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
setProps: (props: Partial<RendererProps>) => void
setViewport: (x: number, y: number, width: number, height: number) => void
dispose: () => void
}
export const RendererParams = {
backgroundColor: PD.Color(Color(0x000000)),
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
roughness: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
}
export type RendererProps = PD.Values<typeof RendererParams>
namespace Renderer {
export function create(ctx: WebGLContext, camera: Camera, props: Partial<RendererProps> = {}): Renderer {
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats } = ctx
const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
const viewport = Viewport()
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
const view = Mat4.clone(camera.view)
const invView = Mat4.invert(Mat4.identity(), view)
const modelView = Mat4.clone(camera.view)
const invModelView = Mat4.invert(Mat4.identity(), modelView)
const invProjection = Mat4.invert(Mat4.identity(), camera.projection)
const modelViewProjection = Mat4.mul(Mat4.identity(), modelView, camera.projection)
const invModelViewProjection = Mat4.invert(Mat4.identity(), modelViewProjection)
const view = Mat4()
const invView = Mat4()
const modelView = Mat4()
const invModelView = Mat4()
const invProjection = Mat4()
const modelViewProjection = Mat4()
const invModelViewProjection = Mat4()
const viewOffset = Vec2()
const globalUniforms: GlobalUniformValues = {
uModel: ValueCell.create(Mat4.identity()),
uView: ValueCell.create(camera.view),
uView: ValueCell.create(view),
uInvView: ValueCell.create(invView),
uModelView: ValueCell.create(modelView),
uInvModelView: ValueCell.create(invModelView),
uInvProjection: ValueCell.create(invProjection),
uProjection: ValueCell.create(Mat4.clone(camera.projection)),
uProjection: ValueCell.create(Mat4()),
uModelViewProjection: ValueCell.create(modelViewProjection),
uInvModelViewProjection: ValueCell.create(invModelViewProjection),
uIsOrtho: ValueCell.create(camera.state.mode === 'orthographic' ? 1 : 0),
uIsOrtho: ValueCell.create(1),
uViewOffset: ValueCell.create(viewOffset),
uPixelRatio: ValueCell.create(ctx.pixelRatio),
uViewportHeight: ValueCell.create(viewport.height),
uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
@@ -96,14 +102,16 @@ namespace Renderer {
uRoughness: ValueCell.create(p.roughness),
uReflectivity: ValueCell.create(p.reflectivity),
uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)),
uNear: ValueCell.create(camera.state.near),
uFar: ValueCell.create(camera.state.far),
uFogNear: ValueCell.create(camera.state.fogNear),
uFogFar: ValueCell.create(camera.state.fogFar),
uCameraPosition: ValueCell.create(Vec3()),
uNear: ValueCell.create(1),
uFar: ValueCell.create(10000),
uFogNear: ValueCell.create(1),
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(bgColor),
uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
uInteriorDarkening: ValueCell.create(p.interiorDarkening),
}
const globalUniformList = Object.entries(globalUniforms)
@@ -153,7 +161,7 @@ namespace Renderer {
}
}
const render = (scene: Scene, variant: GraphicsRenderVariant, clear: boolean) => {
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
ValueCell.update(globalUniforms.uModel, scene.view)
ValueCell.update(globalUniforms.uView, camera.view)
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
@@ -165,12 +173,13 @@ namespace Renderer {
ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection))
ValueCell.update(globalUniforms.uIsOrtho, camera.state.mode === 'orthographic' ? 1 : 0)
ValueCell.update(globalUniforms.uViewOffset, camera.viewOffset.enabled ? Vec2.set(viewOffset, camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2.set(viewOffset, 0, 0))
ValueCell.update(globalUniforms.uCameraPosition, camera.state.position)
ValueCell.update(globalUniforms.uFar, camera.state.far)
ValueCell.update(globalUniforms.uNear, camera.state.near)
ValueCell.update(globalUniforms.uFogFar, camera.state.fogFar)
ValueCell.update(globalUniforms.uFogNear, camera.state.fogNear)
ValueCell.update(globalUniforms.uFar, camera.far)
ValueCell.update(globalUniforms.uNear, camera.near)
ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
globalUniformsNeedUpdate = true
state.currentRenderItemId = -1
@@ -185,7 +194,7 @@ namespace Renderer {
if (clear) {
if (variant === 'color') {
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
} else {
state.clearColor(1, 1, 1, 1)
}
@@ -198,7 +207,7 @@ namespace Renderer {
if (r.state.opaque) renderObject(r, variant)
}
state.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE)
state.enable(gl.BLEND)
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i]
@@ -218,7 +227,7 @@ namespace Renderer {
clear: () => {
state.depthMask(true)
state.colorMask(true, true, true, true)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1.0)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
},
render,
@@ -228,11 +237,19 @@ namespace Renderer {
p.pickingAlphaThreshold = props.pickingAlphaThreshold
ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold)
}
if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
p.interiorDarkening = props.interiorDarkening
ValueCell.update(globalUniforms.uInteriorDarkening, p.interiorDarkening)
}
if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
p.backgroundColor = props.backgroundColor
Color.toVec3Normalized(bgColor, p.backgroundColor)
ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
}
if (props.transparentBackground !== undefined && props.transparentBackground !== p.transparentBackground) {
p.transparentBackground = props.transparentBackground
ValueCell.update(globalUniforms.uTransparentBackground, p.transparentBackground ? 1 : 0)
}
if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
p.lightIntensity = props.lightIntensity
ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -12,6 +12,8 @@ import { Object3D } from './object3d';
import { Sphere3D } from '../mol-math/geometry';
import { Vec3 } from '../mol-math/linear-algebra';
import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
import { RuntimeContext, Task } from '../mol-task';
import { AsyncQueue } from '../mol-util/async-queue';
const boundaryHelper = new BoundaryHelper();
function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D {
@@ -40,17 +42,11 @@ function renderableSort(a: Renderable<RenderableValues & BaseValues>, b: Rendera
const drawProgramIdB = b.getProgram('color').id
const materialIdA = a.materialId
const materialIdB = b.materialId
const zA = a.values.boundingSphere.ref.value.center[2]
const zB = b.values.boundingSphere.ref.value.center[2]
if (drawProgramIdA !== drawProgramIdB) {
return drawProgramIdA - drawProgramIdB // sort by program id to minimize gl state changes
} else if (materialIdA !== materialIdB) {
return materialIdA - materialIdB // sort by material id to minimize gl state changes
} else if (zA !== zB) {
return a.state.opaque
? zA - zB // when opaque, draw closer elements first to minimize overdraw
: zB - zA // when transparent, draw elements last to maximize partial visibility
} else {
return a.id - b.id;
}
@@ -60,10 +56,13 @@ interface Scene extends Object3D {
readonly count: number
readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>>
readonly boundingSphere: Sphere3D
readonly isCommiting: boolean
update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean) => void
add: (o: GraphicsRenderObject) => Renderable<any>
add: (o: GraphicsRenderObject) => void // Renderable<any>
remove: (o: GraphicsRenderObject) => void
syncCommit: () => void
commit: () => Task<void>
has: (o: GraphicsRenderObject) => boolean
clear: () => void
forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void
@@ -74,15 +73,63 @@ namespace Scene {
const renderableMap = new Map<GraphicsRenderObject, Renderable<RenderableValues & BaseValues>>()
const renderables: Renderable<RenderableValues & BaseValues>[] = []
const boundingSphere = Sphere3D.zero()
let boundingSphereDirty = true
const object3d = Object3D.create()
const add = (o: GraphicsRenderObject) => {
if (!renderableMap.has(o)) {
const renderable = createRenderable(ctx, o)
renderables.push(renderable)
renderableMap.set(o, renderable)
boundingSphereDirty = true
return renderable;
} else {
console.warn(`RenderObject with id '${o.id}' already present`)
return renderableMap.get(o)!
}
}
const remove = (o: GraphicsRenderObject) => {
const renderable = renderableMap.get(o)
if (renderable) {
renderable.dispose()
renderables.splice(renderables.indexOf(renderable), 1)
renderableMap.delete(o)
boundingSphereDirty = true
}
}
const commitQueue = new AsyncQueue<any>();
const toAdd: GraphicsRenderObject[] = []
const toRemove: GraphicsRenderObject[] = []
type CommitParams = { toAdd: GraphicsRenderObject[], toRemove: GraphicsRenderObject[] }
const step = 100
const handle = async (ctx: RuntimeContext, arr: GraphicsRenderObject[], fn: (o: GraphicsRenderObject) => void, message: string) => {
for (let i = 0, il = arr.length; i < il; i += step) {
if (ctx.shouldUpdate) await ctx.update({ message, current: i, max: il })
for (let j = i, jl = Math.min(i + step, il); j < jl; ++j) {
fn(arr[j])
}
}
}
const commit = async (ctx: RuntimeContext, p: CommitParams) => {
await handle(ctx, p.toRemove, remove, 'Removing GraphicsRenderObjects')
await handle(ctx, p.toAdd, add, 'Adding GraphicsRenderObjects')
if (ctx.shouldUpdate) await ctx.update({ message: 'Sorting GraphicsRenderObjects' })
renderables.sort(renderableSort)
}
return {
get view () { return object3d.view },
get position () { return object3d.position },
get direction () { return object3d.direction },
get up () { return object3d.up },
get isCommiting () { return commitQueue.length > 0 },
update(objects, keepBoundingSphere) {
Object3D.update(object3d)
@@ -100,27 +147,35 @@ namespace Scene {
if (!keepBoundingSphere) boundingSphereDirty = true
},
add: (o: GraphicsRenderObject) => {
if (!renderableMap.has(o)) {
const renderable = createRenderable(ctx, o)
renderables.push(renderable)
renderables.sort(renderableSort)
renderableMap.set(o, renderable)
boundingSphereDirty = true
return renderable;
} else {
console.warn(`RenderObject with id '${o.id}' already present`)
return renderableMap.get(o)!
}
toAdd.push(o)
},
remove: (o: GraphicsRenderObject) => {
const renderable = renderableMap.get(o)
if (renderable) {
renderable.dispose()
renderables.splice(renderables.indexOf(renderable), 1)
renderables.sort(renderableSort)
renderableMap.delete(o)
boundingSphereDirty = true
}
toRemove.push(o)
},
syncCommit: () => {
for (let i = 0, il = toRemove.length; i < il; ++i) remove(toRemove[i])
toRemove.length = 0
for (let i = 0, il = toAdd.length; i < il; ++i) add(toAdd[i])
toAdd.length = 0
renderables.sort(renderableSort)
},
commit: () => {
const params = { toAdd: [ ...toAdd ], toRemove: [ ...toRemove ] }
toAdd.length = 0
toRemove.length = 0
return Task.create('Commiting GraphicsRenderObjects', async ctx => {
const removed = await commitQueue.enqueue(params);
if (!removed) return;
try {
await commit(ctx, params);
} finally {
commitQueue.handled(params);
}
}, () => {
commitQueue.remove(params);
})
},
has: (o: GraphicsRenderObject) => {
return renderableMap.has(o)

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