Compare commits

...

370 Commits

Author SHA1 Message Date
Alexander Rose
9d31f1ba07 0.5.0-dev.2 2020-02-19 16:13:55 -08:00
David Sehnal
d722d17a02 mol-gl: partial scene commit support 2020-02-19 22:19:17 +01:00
Alexander Rose
522d929f5a handle UNK as amino acid 2020-02-19 10:32:03 -08:00
David Sehnal
42037074cb fix unitTransforms visual state updating 2020-02-19 16:49:14 +01:00
David Sehnal
1e3f17efdf disable selection/highlight when animating/updating 2020-02-19 16:10:19 +01:00
David Sehnal
7389e0075d mol-canvas/gl: refactored scene add/remove object sync 2020-02-19 15:20:38 +01:00
David Sehnal
a080114690 added Structure property to StructureElement and Bond locations 2020-02-19 13:31:42 +01:00
Alexander Rose
1293848d9b refactored ModelFormat
- convert to mmCIF only when needed
- added gro, psf, 3dg formats
2020-02-18 17:11:31 -08:00
Alexander Rose
16b2d9e873 add .ofStringAliasArray and .ofStringListArray to Column 2020-02-18 17:08:03 -08:00
Alexander Rose
7a64334261 split mmcif parser into basic part and properties
- basic part is for hierarchy and conformation
- properties is for any additional info

first commit of refactoring with the aim to make the format parsing more modular and be clear about what data is actually needed for the basic part and for properties
2020-02-18 14:20:47 -08:00
Alexander Rose
009b20c95a uuid docs 2020-02-18 14:17:17 -08:00
Alexander Rose
296bea1b88 added AtomicIndex.findEntity 2020-02-18 14:16:46 -08:00
Alexander Rose
e0775607cc added Table.ofPartialColumns 2020-02-18 14:05:36 -08:00
Alexander Rose
a1fb4b8bf3 support loading multiple files at once
- OpenFiles state action
- file-list param definition
2020-02-14 10:47:05 -08:00
Alexander Rose
29ae78c193 assembly symmetry: use https 2020-02-14 09:34:50 -08:00
Alexander Rose
46cfb42cce seperated autoAttach from property params in custom property transform 2020-02-13 15:09:35 -08:00
Alexander Rose
94ef9f4dbe struct_conn refactoring
- explicitely between two partners
- use symmetry in intra-unit compute (important, before there where wrong hbonds in e.g. 1XJ9)
2020-02-13 11:03:42 -08:00
David Sehnal
b864e992ef mol-io: CIF triple quote support 2020-02-13 16:31:11 +01:00
Alexander Rose
57ed9a4226 updated cif schemas 2020-02-12 18:50:52 -08:00
Alexander Rose
b2c93cbeda add option to ignore issues types in geo quality coloring 2020-02-12 12:29:44 -08:00
Alexander Rose
60540dadee SetUtils: added intersectionSize & differenceSize 2020-02-12 12:28:34 -08:00
David Sehnal
c0236650f0 mol-model: struct conn bonds fix 2020-02-12 19:58:13 +01:00
David Sehnal
fb3017cfff mol-model-props: support outlier type coloring in RCSB validation theme 2020-02-12 16:06:48 +01:00
Alexander Rose
929c91a48c fixed & improved carbohydrate handling
- marking behavior like for polymer visual (single atom triggers full marking)
- fixed carbohydrate links induced by intra-unit bonds
- carbohydrate element is now defined by an altId ring index (instead of the anomeric carbon element index)
2020-02-11 19:09:03 -08:00
Alexander Rose
bfd9595c1c avoid adding bonds multiple times 2020-02-11 19:01:50 -08:00
Alexander Rose
c091f9a8ae typo 2020-02-11 19:00:49 -08:00
David Sehnal
d5b71b302b mol-model: check alt loc in carb fused rings 2020-02-11 14:54:53 +01:00
David Sehnal
67103232be mol-data: fixed valueKind for typed array based Column views 2020-02-11 14:02:46 +01:00
David Sehnal
f471fc3d2b mol-model: support alt loc in ring computation 2020-02-11 13:39:14 +01:00
David Sehnal
2a0783d005 fix residue label; CIF export tweak 2020-02-11 12:11:10 +01:00
Alexander Rose
7234ad261b linting and unit test fixes 2020-02-10 18:54:30 -08:00
Alexander Rose
001d1fb8d3 updated packages 2020-02-10 18:02:00 -08:00
Alexander Rose
35a56bf37c Merge branch 'master' of https://github.com/molstar/molstar 2020-02-10 17:55:40 -08:00
Alexander Rose
9715ff061e ccp4/mrc handling improvements
- read mode 6 (uint16)
- correctly normalize mrc origin offset
- use heuristic to determine endianess if not given
- handle erroneous spacegroup
- calculate data stats if not available
- add offset param
2020-02-10 17:55:24 -08:00
Alexander Rose
9866db9ced remove label on mouseleave in state tree 2020-02-10 14:43:57 -08:00
Alexander Rose
fc2288a583 label tweaks 2020-02-10 14:21:47 -08:00
Alexander Rose
11d1bbe222 cleanup, removed console.log 2020-02-10 14:18:56 -08:00
Alexander Rose
bc07256d8e improved shallowEqual
- old shallowEqual is now shallowEqualObjects
- new shallowEqual supports objects, primitives and arrays
2020-02-10 14:17:54 -08:00
Alexander Rose
3826bdb0c3 validation-report: support symm-clashes 2020-02-10 14:16:41 -08:00
Alexander Rose
9609dddd47 assembly symmetry: improved error and transform handling 2020-02-10 10:32:44 -08:00
David Sehnal
ad7b318da0 mol-plugin: state saving tweak 2020-02-09 18:01:58 +01:00
David Sehnal
66c76ea6b5 mol-plugin: remember state server URL 2020-02-09 17:45:56 +01:00
David Sehnal
29e47c1e90 plugin-state server swagger ui 2020-02-09 17:39:58 +01:00
David Sehnal
e08a074a1c mol-plugin: config 2020-02-08 17:02:44 +01:00
David Sehnal
d7ebb30e05 servers/plugin-state 2020-02-08 16:26:29 +01:00
Alexander Rose
8330068434 assembly symmetry: cage support and improved labels 2020-02-07 18:20:29 -08:00
Alexander Rose
42dea4a2eb renamed copyCage to cloneCage 2020-02-07 18:19:30 -08:00
Alexander Rose
4de0ae6628 fix tetrahedron primitive to be a one... 2020-02-07 18:18:54 -08:00
Alexander Rose
5a714af309 use skipTypename for rcsb graphql codegen 2020-02-07 18:18:17 -08:00
Alexander Rose
20f73fdae7 assembly symmetry: add axes order symbols and coloring 2020-02-06 13:07:15 -08:00
Alexander Rose
aca91cf18f added bundleLabel, improved measurement labels 2020-02-06 10:54:41 -08:00
Alexander Rose
974a7d7520 shape loci bounding sphere for points and text geo 2020-02-06 09:29:07 -08:00
Alexander Rose
f9d7545f7d add avg./sum indication to labels 2020-02-06 09:14:36 -08:00
Alexander Rose
0e135c4645 label improvements for various properties
- asa
- rci
- geometry quality
- desnity fit
- interactions
2020-02-05 18:27:04 -08:00
Alexander Rose
961f847765 updated rcsb graphql schema and url 2020-02-05 14:22:16 -08:00
Alexander Rose
b83fed4701 refactored DataLoci, DataLocation and improved props
- moved nci theme & repr to props
- better validaiton clash loci handling
2020-02-05 12:30:13 -08:00
Alexander Rose
651fd2aca7 fix validation report clashes between unrelated units 2020-02-05 12:27:29 -08:00
Alexander Rose
2dd863e711 add CentroidHelper utils .fromProvider and .fromPairProvider 2020-02-05 12:26:55 -08:00
Alexander Rose
bac5ea7ea6 better asa label, moved asa theme to props 2020-02-04 17:48:29 -08:00
Alexander Rose
312db7eec3 added Bond.getBoundingSphere (moved from model/loci) 2020-02-04 16:51:34 -08:00
Alexander Rose
31850b3a71 Shape.Loci improvements including bounding-sphere calculation 2020-02-04 16:31:09 -08:00
Alexander Rose
ee35e39611 fix .expandBySphere and .expand in Sphere3D 2020-02-04 16:28:58 -08:00
Alexander Rose
872130d65c added GroupMapping to mesh, lines, spheres geometries 2020-02-04 16:28:34 -08:00
Alexander Rose
b903554f52 geo util cleanup and consilidation 2020-02-04 14:21:20 -08:00
Alexander Rose
c3fba20780 ply improvements: use vertex group field name in label, fix parser and spec 2020-02-04 11:28:30 -08:00
Alexander Rose
870d2da8ed wip, geometry refactoring
- create helper function
- bounding-sphere handling
- cleanup
2020-02-03 19:34:28 -08:00
Alexander Rose
22d17f6c8e ply parser: handle list elements with appended properties 2020-02-03 19:31:36 -08:00
Alexander Rose
6bbf12a660 repr marker and label fixes 2020-02-03 18:14:53 -08:00
Alexander Rose
ab4ae742db add Mat4.isRotationAndTranslation 2020-02-03 17:46:46 -08:00
Alexander Rose
7725071ae2 repr state handling fixes and support for allowed marker actions 2020-02-03 17:45:33 -08:00
Alexander Rose
c7ab6ebec7 wip, rcsb validation report clashes 2020-02-03 09:35:32 -08:00
Alexander Rose
d56abadf4c support props in default theme of repr provider 2020-02-03 09:34:29 -08:00
David Sehnal
7eb2952ec5 mol-state: StateActionManager.remove 2020-02-03 15:41:45 +01:00
Alexander Rose
21cf2d5437 wip, rcsb validation report 2020-01-31 18:53:31 -08:00
Alexander Rose
5af0c448c6 generic data Location 2020-01-31 18:33:07 -08:00
Alexander Rose
2ca9392c5a Column.ValueKind docs 2020-01-31 18:30:07 -08:00
Alexander Rose
a5a6f2dcc4 emit StructureElement.Location in BondIterator.fromStructure
- simpler to handle in themes
2020-01-31 18:29:53 -08:00
Alexander Rose
91f5207d68 formating 2020-01-31 17:30:59 -08:00
Alexander Rose
c89848dec0 fix AtomicIndex.findChainAuth
- needs to consider that auth_asym_id can map to multiple label_asym_ids
2020-01-31 17:29:48 -08:00
Alexander Rose
116d3ec319 added InterUnitGraph.Builder 2020-01-31 15:56:36 -08:00
Alexander Rose
20266a229b added Structure.eachUnitPair 2020-01-31 12:06:38 -08:00
Alexander Rose
0a26e84f8f added LinkCylinderStyle.Disk 2020-01-30 19:33:02 -08:00
Alexander Rose
93bfa7a575 refactored link/bond utils for visuals 2020-01-30 18:35:20 -08:00
Alexander Rose
c09991573c refactored custom-model-property
- analogous to custom-structure-property
2020-01-29 18:55:08 -08:00
Alexander Rose
556b7b26d4 handle update in StructureFromModel 2020-01-29 18:53:33 -08:00
Alexander Rose
a011600766 handle more edge cases in getNumberType 2020-01-29 18:51:28 -08:00
Alexander Rose
2e5439c385 treat scientific numbers as string in getFieldType 2020-01-29 10:22:36 -08:00
Alexander Rose
3d6ae08437 wip, custom property refactoring 2020-01-28 17:16:01 -08:00
Alexander Rose
03228f7952 renamed CustomStructureProperty.getValue to .get 2020-01-28 16:23:49 -08:00
Alexander Rose
e46a8c4369 better handle scientific number type in getCifFieldType 2020-01-28 16:13:51 -08:00
Alexander Rose
01ef92a795 cleanup assembly-symmetry 2020-01-28 13:04:59 -08:00
Alexander Rose
61a5d18be6 async zip/gzip decoding 2020-01-28 11:47:26 -08:00
Alexander Rose
9c2c48bd59 use TextDecoder when available 2020-01-28 11:44:09 -08:00
Alexander Rose
df6d49b2ac zip utils cleanup 2020-01-27 22:41:03 -08:00
Alexander Rose
2476bf76b5 Merge branch 'master' of https://github.com/molstar/molstar 2020-01-27 22:33:00 -08:00
Alexander Rose
1c4a397249 support loading of zip and gz files
- only zip files with a single entry
2020-01-27 22:32:40 -08:00
Alexander Rose
4930018a55 added lightweight zip utils
- deflate/inflate
- zip files
- gz files
2020-01-27 22:30:48 -08:00
Alexander Rose
205f4c31d6 tweaked geometry and render-object types 2020-01-27 14:41:24 -08:00
Alexander Rose
89d3c87919 refactored data-source to leverage built-in json/xml parsing 2020-01-27 14:40:45 -08:00
Alexander Rose
d0a861d39c take aromatic rings into account for hydrogen bond calculation 2020-01-27 11:13:35 -08:00
Alexander Rose
9f7b96c727 fix use of invariant positions when calculation interactions 2020-01-25 14:29:48 -08:00
Alexander Rose
e1d2a8b41d ignore non-src files in vscode eslint plugin 2020-01-25 13:20:43 -08:00
Alexander Rose
e71bf9c288 use cpx2 instead of unmaintained cpx package 2020-01-24 18:30:31 -08:00
Alexander Rose
693ea3a40e refactored CustomStructureProperties & updated RCSB assembly symmetry
- CustomStructureProperties.attach recieves runtime and fetch context
- RCSBAsseblySymmetry uses updated data API
2020-01-24 18:23:20 -08:00
Alexander Rose
921d23e73f add NonNullableArray type helper 2020-01-24 18:13:20 -08:00
Alexander Rose
709944c859 typed DataLoci 2020-01-24 18:12:43 -08:00
Alexander Rose
9341c19bd5 updated graphql codegen script 2020-01-24 18:12:10 -08:00
Alexander Rose
1cee84d9af fix formating 2020-01-23 10:14:03 -08:00
Alexander Rose
bea5fe5474 updated packages 2020-01-23 10:13:53 -08:00
Alexander Rose
c473f2b284 Merge branch 'master' of https://github.com/molstar/molstar 2020-01-23 09:31:20 -08:00
Alexander Rose
d5c163ac48 handle context loss, add webgl resources 2020-01-23 09:31:05 -08:00
Alexander Rose
85dcef1b2e more browser backwards compat, polyfills 2020-01-23 08:57:53 -08:00
Alexander Rose
8c1acc6758 fix floatLogFactor in shader code 2020-01-22 17:55:18 -08:00
Alexander Rose
fbb7f0a6a1 fix glsl300 frag shader prefix 2020-01-22 17:54:56 -08:00
David Sehnal
3b7c8963df mol-theme: include <1 occupancy in default label 2020-01-21 12:51:34 +01:00
David Sehnal
d58f4a73b6 mol-model: fix for links with <1 occupancy atoms 2020-01-21 12:42:44 +01:00
Alexander Rose
09cfd85603 tweaked shader const 2020-01-16 11:38:09 -05:00
Alexander Rose
846a301122 added polyfills 2020-01-16 11:37:18 -05:00
Alexander Rose
6a29925733 fix if statements in WebGLState 2020-01-15 15:21:41 -05:00
Alexander Rose
5633350b63 formating tweaks 2020-01-15 15:12:55 -05:00
Alexander Rose
5d5ffcdb36 trajectory from topology and coordinates 2020-01-15 15:00:26 -05:00
Alexander Rose
a819835984 add AtomicConformation.xyzDefined & improve getAtomicRanges 2020-01-15 14:58:03 -05:00
Alexander Rose
227721bfd3 fix sequence creation from hierarchy 2020-01-15 14:14:37 -05:00
Alexander Rose
31e2cc5f07 add index-pair bond provider 2020-01-15 14:14:02 -05:00
Alexander Rose
3f02cf0561 take .isDefined into account in CifField.ofColumn 2020-01-15 13:51:54 -05:00
Alexander Rose
a57a9f0386 fix psf parser 2020-01-15 13:50:13 -05:00
Alexander Rose
11a6df6e19 add basic psf reader 2020-01-13 16:32:42 -05:00
Alexander Rose
8d5e1feac9 updated packages 2020-01-13 09:12:59 -05:00
Alexander Rose
e19e3d7380 use eslint instead of tslint 2020-01-13 09:02:47 -05:00
Alexander Rose
78c7664be3 very basic ccp4 spec test 2020-01-12 13:38:06 -08:00
Alexander Rose
39b29fe0ea formating & code style 2020-01-12 13:23:37 -08:00
Alexander Rose
5636edc1d1 plugin, trajectory from model and coordinates 2020-01-12 13:22:50 -08:00
Alexander Rose
7dbdc75cad added dcd format parser 2020-01-12 13:20:55 -08:00
Alexander Rose
fd92c916b7 added coordinates support to model 2020-01-12 13:01:41 -08:00
Alexander Rose
f5e880839e force cast from unknown for state dependencies 2020-01-12 11:56:07 -08:00
Alexander Rose
6e70172c45 fixed addUnitPositiveCharges 2020-01-10 17:46:27 -08:00
Alexander Rose
22563bf671 consider overlapping secondary structure elements from mmCIF 2020-01-10 17:33:28 -08:00
Alexander Rose
cad95403aa added option to toggle far clipping 2020-01-10 17:03:08 -08:00
Alexander Rose
d375595e08 support interactions loci in getBoundingSphere 2020-01-10 16:08:25 -08:00
Alexander Rose
b16d13cc35 interactions, links to contacts renaming 2020-01-10 16:07:52 -08:00
Alexander Rose
f03471ee39 tweaked interaction params 2020-01-10 15:33:58 -08:00
Alexander Rose
6f1f65487d string, kebab case 2020-01-10 15:33:31 -08:00
Alexander Rose
b863aaedb3 general support for aromatic rings
- containing annotated aromatic bonds
- being flat with certain elements
2020-01-10 15:33:06 -08:00
Alexander Rose
b77994d01f improved bond handling
- filter (include/exclude) type in repr
- cleaned-up bond types and names
2020-01-10 15:30:29 -08:00
Alexander Rose
30e01c07f7 add interactions repr to surroundings behavior 2020-01-09 17:34:29 -08:00
Alexander Rose
1e018b82df interactions repr: use size theme 2020-01-09 17:33:58 -08:00
Alexander Rose
49e6966037 fixe & improved interaction refiners 2020-01-09 17:04:23 -08:00
Alexander Rose
85b1df46cd improved checkLineOfSight 2020-01-09 17:03:43 -08:00
Alexander Rose
6908a2cd2b copy structure.lookup.findUnitIndices results
needed so structure.lookup.find can be safely called within
2020-01-09 17:02:52 -08:00
Alexander Rose
4379d818ab fix interactions/valence-model label behavior 2020-01-09 17:00:35 -08:00
Alexander Rose
4434dfb79a fix .firstChainLoc assignment in handleUnitChainsSimple 2020-01-09 16:59:49 -08:00
Alexander Rose
9621f67e84 helper to copy lookup results 2020-01-09 16:59:12 -08:00
Alexander Rose
21a0684353 wip, interactions refiners
- saltBridgeRefiner
- piStackingRefiner
- metalCoordinationRefiner
2020-01-08 17:54:10 -08:00
Alexander Rose
3601955433 wip, interactions, various fixes
- limit bond-related calculations to covalent bonds
- wrong charge feature assignment
2020-01-08 17:53:12 -08:00
Alexander Rose
d91483c485 implemented areFeaturesWithinDistanceSq 2020-01-07 16:57:44 -08:00
Alexander Rose
8b3d07906f wip, refactored refineInteractions, added weakHydrogenBondsRefiner 2020-01-07 16:46:21 -08:00
Alexander Rose
5f8a4b6be4 better types for IntAdjacencyGraph and GridLookup3D 2020-01-07 16:45:21 -08:00
Alexander Rose
f8ff919787 wip, interactions, fix hydrogen bond distance test 2020-01-06 17:55:40 -08:00
Alexander Rose
6dfe975fc7 wip, interactions refinement
- more links to contacts renaming
- line-of-sight check
- hydrophobic contact refinement
2020-01-06 17:40:55 -08:00
Alexander Rose
2650f8d3dd wip, renamed links to contacts for interactions 2020-01-06 10:06:57 -08:00
Alexander Rose
f172b6ceaa wip, interactions
- improved performance by searching for links in only the subset of relevant Features
2020-01-03 17:22:52 -08:00
Alexander Rose
8086af1bf7 impoved SetUtils
- use ReadonlySet when possible
- added .add
2020-01-03 17:21:18 -08:00
Alexander Rose
5813840e17 wip, interactions
- metal coordination
- Features.Provider() ctor
- Features.Provider, multiple types
2020-01-03 15:34:02 -08:00
Alexander Rose
e2d595394a wip, interactions
- hydrophobic
- allow computing of a subset of types
2020-01-03 12:53:27 -08:00
Alexander Rose
9b24e6fb1f add rings selection to UI 2020-01-02 14:57:51 -08:00
Alexander Rose
0f33144935 wip, various nci fixes and tweaks 2020-01-02 14:57:29 -08:00
Alexander Rose
6d384166d5 fix, rings must have at least three elements 2020-01-02 14:55:06 -08:00
Alexander Rose
8b766dc242 tweak, use Box3D.size 2020-01-02 14:54:47 -08:00
Alexander Rose
1fa64c836c wip, charged interactions 2019-12-20 17:30:10 -08:00
Alexander Rose
af700c1481 wip, refactored interaction computation 2019-12-20 11:33:29 -08:00
Alexander Rose
7dcf16cd97 wip, interactions, halogen bonds 2019-12-19 17:30:24 -08:00
Alexander Rose
f86b46076c wip, interactions & valence-model label providers 2019-12-19 16:29:55 -08:00
Alexander Rose
6fe58d7b73 wip, various interactions computation fixes 2019-12-19 14:53:20 -08:00
Alexander Rose
8c2a4b5cae fix unitTransforms reset with complex representation 2019-12-18 16:50:32 -08:00
Alexander Rose
b2c31f5166 fix unit instance marking 2019-12-18 16:14:50 -08:00
Alexander Rose
0f4d0ff986 wip, interactions, features in invariant space 2019-12-18 15:56:04 -08:00
Alexander Rose
2b73f9cafd improved hbond param handling 2019-12-18 15:55:07 -08:00
Alexander Rose
a86438a265 wip, interactions & hydrogen bonds 2019-12-18 14:40:22 -08:00
Alexander Rose
c0b5102d31 bonds tweaks 2019-12-18 14:39:48 -08:00
Alexander Rose
feab3a38f9 add unit kind helpers to Structure 2019-12-18 14:38:01 -08:00
Alexander Rose
b1fb9c5c47 renamed 'structure-element-index' to 'unit-element-index' 2019-12-17 10:17:42 -08:00
Alexander Rose
9fb65f46a1 wip, bond compute todos 2019-12-16 17:20:08 -08:00
Alexander Rose
7a2e85b856 always use root topology for valence model 2019-12-16 17:19:40 -08:00
Alexander Rose
f85e3e76fd add CustomStructureProperty.type determining inheritance 2019-12-16 15:48:24 -08:00
Alexander Rose
c6ef02d0a6 factored out Unit BaseProperties 2019-12-16 15:45:00 -08:00
Alexander Rose
81a6e3cf4e package updates 2019-12-16 11:46:05 -08:00
Alexander Rose
48a75927f6 renamed 'link' to 'bond' as appropriate 2019-12-16 11:38:46 -08:00
Alexander Rose
067a85ef88 avoid console.log when not in debug-mode 2019-12-16 10:23:52 -08:00
Alexander Rose
c4757313e6 fix typo 2019-12-16 10:23:24 -08:00
Alexander Rose
88d8998bd2 generalized InterUnitBonds to InterUnitGraph 2019-12-13 18:31:32 -08:00
Alexander Rose
97e6884487 wip, fix interactions marking 2019-12-13 18:13:50 -08:00
Alexander Rose
8fd56dbc38 wip, interactions 2019-12-13 16:59:47 -08:00
Alexander Rose
f9c97ad5cb fix regression, only calc dssp for non-archival files by default 2019-12-13 16:58:41 -08:00
Alexander Rose
f893aba522 make custom structure properties auto-attachable 2019-12-13 11:49:31 -08:00
Alexander Rose
f2740aaee2 secondary structure query/selection improvements
- secondaryStructureFlag names for query
- helix and beta selection query helper
2019-12-13 09:22:39 -08:00
Alexander Rose
fb15cc135a wip, handle custom custom structure props as on-demand dependencies 2019-12-12 18:26:15 -08:00
Alexander Rose
d9579914b4 improved accessible-surface-area computation 2019-12-11 15:56:33 -08:00
Alexander Rose
5a98bfd8ef added StructureElement.Location.areEqual 2019-12-11 15:22:49 -08:00
Alexander Rose
149e6ebf84 cleanup 2019-12-11 15:22:26 -08:00
Alexander Rose
74c8e512a7 add Structure.atomicResidueCount & SerialMapping.getSerialIndex 2019-12-11 15:22:08 -08:00
Alexander Rose
52bf9b87fa Merge branch 'master' of https://github.com/molstar/molstar 2019-12-11 08:55:22 -08:00
David Sehnal
ac8d71215e mol-server: print out current config 2019-12-11 15:15:10 +01:00
David Sehnal
c7cfedb874 model-server: support post params 2019-12-11 15:01:51 +01:00
Alexander Rose
faae40c9e8 better naming in SerialMapping 2019-12-10 13:37:52 -08:00
Alexander Rose
288912ccea add Task.empty 2019-12-10 13:35:16 -08:00
Alexander Rose
26e47c3e33 Theme namespace fixes 2019-12-10 13:35:02 -08:00
Alexander Rose
15a8ea6598 Merge branch 'master' of https://github.com/molstar/molstar 2019-12-10 08:33:32 -08:00
Alexander Rose
aeda6d3312 Theme namespace 2019-12-10 08:33:05 -08:00
David Sehnal
79f430efb3 added PD.array/objectToOptions 2019-12-10 12:51:49 +01:00
Alexander Rose
4fe70de2ff InterUnitLink improvements 2019-12-09 17:15:35 -08:00
Alexander Rose
56fd6d6a5b add InterUnitBonds.hasBond 2019-12-09 17:14:25 -08:00
Sebastian Bittrich
b5076edc8d accessible surface area calculation improvements 2019-12-09 17:12:09 -08:00
Alexander Rose
4fbb9e232c removed unused code 2019-12-09 17:08:12 -08:00
Alexander Rose
b6324d9cee bond-order table for standard residues 2019-12-09 17:07:54 -08:00
Alexander Rose
f668f725e8 improved CombinationIterator 2019-12-09 17:06:47 -08:00
Alexander Rose
e45041f2f2 simplified Vec3.angle 2019-12-09 17:06:33 -08:00
Alexander Rose
d285076acb Structure.bondCount 2019-12-09 17:06:12 -08:00
Alexander Rose
9047aae87c Link.ElementLinkIterator 2019-12-09 17:05:33 -08:00
Alexander Rose
44b00edcb6 StructureGroup as first argument for UnitsVisualBuilder.createLocationIterator 2019-12-09 17:04:57 -08:00
Alexander Rose
a476c2a167 rename Structure.links to .interUnitBonds 2019-12-09 17:02:32 -08:00
Alexander Rose
1b2b168624 add orientation ellipsoid to load cellpack presets 2019-12-09 09:11:18 -08:00
Alexander Rose
ce238bcd1d fix, removed unused property 2019-12-09 09:06:31 -08:00
Alexander Rose
c119a82d83 simplified fog shader handling 2019-12-08 23:06:36 -08:00
Alexander Rose
4b97686a26 consolidated some shader uniforms as global 2019-12-08 22:56:12 -08:00
Alexander Rose
c13350b098 set transparent background per render call & screenshot improvements 2019-12-08 18:41:47 -08:00
Alexander Rose
c9c6df4861 Merge branch 'master' of https://github.com/molstar/molstar 2019-12-08 17:33:01 -08:00
David Sehnal
2e73681aae Merge branch 'master' of https://github.com/molstar/molstar 2019-12-08 14:55:42 +01:00
David Sehnal
42b4c25ca3 mol-plugin: screenshot UI improvements 2019-12-08 14:55:34 +01:00
Alexander Rose
5a5939408f support for interior-color (mesh, spheres) 2019-12-08 00:23:18 -08:00
Alexander Rose
7041bac8ca add ignore-light support to spheres shader 2019-12-08 00:22:29 -08:00
Alexander Rose
f8ab02fdab add doubleSided to quality props 2019-12-07 19:43:05 -08:00
David Sehnal
b75ba4b4aa mol-plugin: screenshot controls 2019-12-07 16:42:47 +01:00
David Sehnal
baa80d08dd mol-plugin: remove passRepresentation from Highlight command 2019-12-07 12:47:05 +01:00
David Sehnal
144d40cd38 moved skin to mol-plugin-ui 2019-12-07 12:40:06 +01:00
David Sehnal
c6b4610adf mol-plugin: fix highlight label style 2019-12-07 12:32:13 +01:00
Alexander Rose
f2c5cd978b removed superseded label transfroms and representations 2019-12-06 16:58:29 -08:00
Alexander Rose
63823698c7 take fog into account for picking visibility 2019-12-06 16:55:18 -08:00
Alexander Rose
f6ca679a57 improved highlighting
- highlight SO.Molecule.Structure.Selections
- cleaned-up LociHighlightManager
2019-12-06 16:12:00 -08:00
Alexander Rose
142c46c127 improved measurement representation labels 2019-12-06 15:18:53 -08:00
Alexander Rose
1bd59523b2 Representation.getLoci() returns 'whole' loci for repr 2019-12-06 15:18:26 -08:00
David Sehnal
ef67925858 added mol-plugin-ui 2019-12-06 23:25:26 +01:00
David Sehnal
5dae376bcb mol-plugin: fix non-structure visual highlighting 2019-12-06 22:55:30 +01:00
David Sehnal
14ed325579 removed console.log 2019-12-06 22:31:48 +01:00
David Sehnal
b007409521 Merge branch 'master' of https://github.com/molstar/molstar 2019-12-06 22:17:07 +01:00
David Sehnal
5830252df7 mol-plugin: optimized sequence marking 2019-12-06 22:16:23 +01:00
David Sehnal
02ad9587fc mol-repr: optimized highlight marking for structure visuals 2019-12-06 21:16:36 +01:00
Alexander Rose
4596336c13 mmcif schema updates, mostly regarding carbohydrates 2019-12-06 12:14:45 -08:00
Alexander Rose
2a413256ed added element/residue/chain instances loci granularity and helpers 2019-12-06 11:46:54 -08:00
David Sehnal
8b9178249d mol-plugin: added Structure.Selections groupId 2019-12-06 19:30:42 +01:00
David Sehnal
1d0ba36d7c mol-repr: show residue/nucleotide segments selected when intersecting, not just subset 2019-12-06 18:02:58 +01:00
David Sehnal
3a8211937f mol-plugin: better current object handling 2019-12-06 17:44:21 +01:00
David Sehnal
0d576f7011 mol-plugin: state tree label use button instead of link 2019-12-06 15:28:31 +01:00
David Sehnal
d90e81b0e5 mol-plugin: wip state manager 2019-12-06 14:54:51 +01:00
David Sehnal
89be1767e9 Merge branch 'master' of https://github.com/molstar/molstar 2019-12-06 13:58:36 +01:00
Alexander Rose
aee6921140 added entity & model loci granularity and helpers 2019-12-05 16:49:21 -08:00
Alexander Rose
a55572d52f show structure orientation ellipsoid for more applicable units 2019-12-05 16:00:33 -08:00
Alexander Rose
525deafd83 added ellipsoid visual to orientation shape-repr 2019-12-05 15:37:53 -08:00
Alexander Rose
8064b969be added structure label-representation 2019-12-05 15:12:50 -08:00
Alexander Rose
6d6e12bbd0 added scale argument to TextBuilder.add 2019-12-05 15:12:19 -08:00
Alexander Rose
825ae48000 added Text geo support for structure repr/visual 2019-12-05 15:11:30 -08:00
Alexander Rose
764aef3181 added backbone helper-selection 2019-12-05 10:44:09 -08:00
Alexander Rose
d226abadc1 no text picking by default 2019-12-05 10:40:04 -08:00
Alexander Rose
ccd1fa3a0f better renderer depthMask handling 2019-12-05 10:38:22 -08:00
Alexander Rose
65a692e3bf OrientationEllipsoidMeshVisual.eachUnit 2019-12-04 11:28:25 -08:00
David Sehnal
2daac955f4 mol-model: Loci.Bundle 2019-12-04 20:10:54 +01:00
Alexander Rose
162797e43a fix ModelStructureRepresentation.getParams for structures without assebmlies 2019-12-04 11:09:56 -08:00
Alexander Rose
784b29805a added structure OrientationRepresentation 2019-12-04 11:00:57 -08:00
Alexander Rose
67b60cff60 added Unit.principalAxes 2019-12-04 11:00:12 -08:00
Alexander Rose
28443de11f mesh-builder: addAxes, addOrientedBox 2019-12-04 10:58:44 -08:00
Alexander Rose
59cf60c2ba removed StructureOrientation3D
- use StructureSelectionsOrientation3D
2019-12-04 09:39:52 -08:00
Alexander Rose
8e14a1e2ec updated packages 2019-12-03 17:47:00 -08:00
Alexander Rose
7dd2e5ec98 bind label & orientation to ui button 2019-12-03 17:07:13 -08:00
Alexander Rose
bd3e052130 assign position in lines shader so fog works 2019-12-03 16:46:27 -08:00
Alexander Rose
6fbe7efcab added ignoreLight param for mesh geo 2019-12-03 16:31:08 -08:00
Alexander Rose
572a574bf3 improved measurements repr, including added dihedral repr 2019-12-03 16:30:42 -08:00
Alexander Rose
347a0a43ea Loci.Pair./Triple/.Quad type 2019-12-03 16:14:44 -08:00
Alexander Rose
28abd00a6e wip, ShapeBuilder argument for ShapeRepresentation 2019-12-03 16:14:14 -08:00
Alexander Rose
06354d6d1d math lib tweaks 2019-12-03 16:13:24 -08:00
Alexander Rose
2b31d54e18 always trigger highlight on hover
no matter what keys are pressed
2019-12-02 10:20:31 -08:00
Alexander Rose
58644b3d5c wip, measurements representations 2019-12-02 08:18:54 -08:00
Alexander Rose
4c732b3b2d added Loci.getPrincipalAxes 2019-12-02 08:17:44 -08:00
Alexander Rose
36d3a0728c added MeshBuilder.addPrimitiveFlipped 2019-12-02 08:17:28 -08:00
Alexander Rose
b0c696e401 wip, add dashed in lines builder 2019-12-02 08:16:58 -08:00
Alexander Rose
2a28b5421f wip, circle primitive 2019-12-02 08:16:16 -08:00
Alexander Rose
269176e0a6 lociLabel options 2019-12-02 08:15:36 -08:00
Alexander Rose
cd23a68268 moved toUpperCase to string.ts 2019-12-02 08:13:49 -08:00
David Sehnal
48e81c8b10 packages 2019-11-30 22:07:44 +01:00
David Sehnal
649c294929 mol-plugin: fixes 2019-11-29 18:55:49 +01:00
David Sehnal
ab0dcb0de9 mol-state: builder fixes, StateObjectSelector 2019-11-29 18:18:25 +01:00
David Sehnal
cb2fc8f95b mol-state: StateObjectSelector 2019-11-29 17:42:07 +01:00
David Sehnal
5deec4391d mol-plugin: fix state action list being mutated by accident 2019-11-28 23:39:28 +01:00
David Sehnal
1873e506e0 mol-plugin: state tree button tweaks 2019-11-28 23:33:00 +01:00
David Sehnal
11e660a81a mol-plugin: indicate unobserved changed in state tree, better home screen 2019-11-28 23:27:48 +01:00
David Sehnal
635ba2a7e3 mol-plugin: fix collapsed left panel when controls are hidden 2019-11-28 22:55:42 +01:00
David Sehnal
1a2fb55d03 mol-plugin: add remote state list to the "home" panel 2019-11-28 22:54:17 +01:00
David Sehnal
a5785e526e mol-plugin: basic background settings change 2019-11-28 19:08:34 +01:00
David Sehnal
af9d50f5ec mol-plugin: make Log take less space 2019-11-28 18:58:19 +01:00
David Sehnal
ac06b9c018 mol-plugin: configure remote states in plugin spec (wip) 2019-11-28 18:05:36 +01:00
David Sehnal
36d8df1442 mol-plugin: UI tweaks 2019-11-28 18:01:03 +01:00
David Sehnal
a312c4ef1d mol-plugin: collapse left panel 2019-11-28 17:41:37 +01:00
David Sehnal
0c6b80d370 mol-plugin: UI improvements (wip) 2019-11-28 16:58:43 +01:00
David Sehnal
16f9129acc mol-plugin: only show transform back button where appropriate 2019-11-28 15:27:40 +01:00
David Sehnal
1af9554cc7 mol-plugin: sequence view tweak 2019-11-28 14:50:04 +01:00
David Sehnal
cc8999cbc9 mol-plugin: only expand DownloadStructure for root actions 2019-11-28 14:40:43 +01:00
David Sehnal
c7b4099758 mol-plugin: viewport controls tweaks 2019-11-28 14:33:29 +01:00
David Sehnal
f060fc228f mol-plugin: Help panel 2019-11-28 14:12:44 +01:00
David Sehnal
8d3c091d2e mol-plugin: simple settings 2019-11-28 13:45:57 +01:00
David Sehnal
cabf7c1613 mol-plugin: Automatic default 3d representation 2019-11-27 19:08:17 +01:00
David Sehnal
b44149bbaf mol-plugin: wip StructureRepresentationManager 2019-11-27 18:32:25 +01:00
David Sehnal
18befade7f mol-plugin: Transform and Action tweaks 2019-11-27 16:36:45 +01:00
David Sehnal
8fd8eb703b mol-plugin: CreateComplex -> Create3DRepresentationPreset 2019-11-27 16:08:09 +01:00
David Sehnal
fa0c29c1d1 mol-plugin: DownloadStructure now uses StructureFromModel 2019-11-27 13:52:19 +01:00
David Sehnal
65ed5c98bf mol-plugin: refactor StructureFromModel 2019-11-27 13:38:21 +01:00
David Sehnal
f0ecf74aad mol-script: bind rings generator 2019-11-27 12:32:15 +01:00
Alexander Rose
c56ed0af28 0.5.0-dev.1 2019-11-26 16:15:18 -08:00
Alexander Rose
ddb8231c00 focus tweaks, export structure boundary functions 2019-11-26 16:09:45 -08:00
Alexander Rose
bc24e8f236 fix query name 2019-11-26 16:08:44 -08:00
Alexander Rose
2edb31e7ea fix structure orientation translation-matrix 2019-11-26 16:06:12 -08:00
Alexander Rose
ad619d9b8d mouse binding fixes 2019-11-26 11:19:02 -08:00
Alexander Rose
61f738ce3d Merge branch 'master' of https://github.com/molstar/molstar 2019-11-26 10:59:03 -08:00
Alexander Rose
73ae95ed06 improved selection of residue ranges
- support brushing in sequence widget
- introduce reference-loci in selection manager
2019-11-26 10:48:59 -08:00
Alexander Rose
6d4c1aa3ea better names for LociSelectManager methods 2019-11-26 10:47:44 -08:00
Alexander Rose
34f230a882 reverted SelectLoci bindings 2019-11-26 10:47:22 -08:00
Alexander Rose
a68d81e495 wip, add button to input observer 2019-11-26 10:46:49 -08:00
David Sehnal
9687c38e06 mol-plugin: CSS fix active control hover 2019-11-26 16:32:08 +01:00
David Sehnal
f69670c368 mol-plugin: action select control 2019-11-26 16:19:17 +01:00
David Sehnal
a0b537ef64 mol-plugin: cleaner object update UI 2019-11-26 15:04:33 +01:00
David Sehnal
e8663b5bfc mol-plugin: fix plugin crash when WebGL is not available 2019-11-26 13:31:42 +01:00
David Sehnal
9f12fbde70 mol-plugin: param help tweaks 2019-11-26 13:07:04 +01:00
David Sehnal
940b16ebd2 mol-plugin: DownloadStructure param tweaks 2019-11-26 13:01:06 +01:00
David Sehnal
dc5aa0c30d mol-util: Fix invalid default options in ParamDefinition 2019-11-26 12:50:44 +01:00
Alexander Rose
b70b9c44fc fix ajaxGet: handle case where download had finished before callback were registered 2019-11-25 16:26:21 -08:00
David Sehnal
88a4cf1aa4 mol-plugin: SequenceView tweaks 2019-11-25 13:21:06 +01:00
David Sehnal
49eefa131f tweaks 2019-11-25 13:14:13 +01:00
David Sehnal
9f4a60572f mol-plugin: improved SequenceView 2019-11-25 13:03:01 +01:00
David Sehnal
8f9579bcaf mol-plugin: interactivity binding tweaks 2019-11-25 00:15:08 +01:00
David Sehnal
845dd4115d mol-model: changed assembly operator naming 2019-11-24 23:57:47 +01:00
David Sehnal
3e2a1227ce mol-plugin: improved range selection 2019-11-24 23:46:29 +01:00
David Sehnal
dcfec014a5 mol-plugin: Fix interactivity bindings so that only one action is triggered. 2019-11-24 23:19:32 +01:00
David Sehnal
f5598ba93e tweak 2019-11-24 22:31:43 +01:00
David Sehnal
a5fadb6e8a mol-model: remap uses OrderedSet.indexedIntersect 2019-11-24 22:26:23 +01:00
David Sehnal
738ba2d6fd mol-data: OrderedSet.indexedIntersect 2019-11-24 22:20:14 +01:00
David Sehnal
ebcc45d1fd mol-plugin: added Download Screenshot icon 2019-11-24 16:04:37 +01:00
David Sehnal
272a911d35 mol-plugin: refactored selection transforms 2019-11-24 15:36:52 +01:00
Alexander Rose
a725e577be refactored principal-axes calculation 2019-11-22 18:28:16 -08:00
Alexander Rose
a36ead9ef1 add Axes3D 2019-11-22 18:27:30 -08:00
Alexander Rose
f516072d12 extract getStructureQuality helper 2019-11-22 18:26:36 -08:00
Alexander Rose
a54b36b057 add Mat3.directionTransform 2019-11-22 18:26:05 -08:00
Alexander Rose
a18484a5dd fix memory leak, reprUpdatedSubscriptions not removed 2019-11-22 18:24:32 -08:00
David Sehnal
e685c0a342 revert name change 2019-11-22 18:27:38 +01:00
David Sehnal
a3b1d11bb3 mol-plugin: measurement tweak and todo 2019-11-22 18:25:28 +01:00
David Sehnal
7a7643c6bc mol-plugin: css fix 2019-11-22 17:56:16 +01:00
David Sehnal
3643bd04f1 mol-state: fix dependent transform delete 2019-11-22 17:49:14 +01:00
David Sehnal
65c2faaced [wip] mol-plugin: measurements 2019-11-22 17:28:21 +01:00
David Sehnal
32a91ca98c mol-plugin: wip measurements 2019-11-22 16:40:20 +01:00
David Sehnal
9f6e98a5d0 mol-plugin: support latest loci in selection manager 2019-11-22 16:17:27 +01:00
David Sehnal
0964a9fd50 mol-plugin: fix selectToggle, bind unselect to secondary button 2019-11-22 15:05:17 +01:00
David Sehnal
a0b865cd07 mol-state: dependent transforms 2019-11-22 14:36:20 +01:00
David Sehnal
a5f73b5c20 [wip] mol-state: dependent transforms 2019-11-22 13:39:15 +01: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
698 changed files with 37942 additions and 16077 deletions

65
.eslintrc.json Normal file
View File

@@ -0,0 +1,65 @@
{
"env": {
"browser": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/ban-types": "warn",
"@typescript-eslint/class-name-casing": "off",
"@typescript-eslint/indent": [
"warn",
4
],
"@typescript-eslint/member-delimiter-style": [
"off",
{
"multiline": {
"delimiter": "none",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
"@typescript-eslint/prefer-namespace-keyword": "warn",
"@typescript-eslint/quotes": [
"warn",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"@typescript-eslint/semi": [
"off",
null
],
"@typescript-eslint/type-annotation-spacing": "warn",
"arrow-parens": [
"off",
"as-needed"
],
"comma-dangle": "off",
"eqeqeq": [
"warn",
"smart"
],
"import/order": "off",
"no-eval": "warn",
"no-new-wrappers": "warn",
"no-trailing-spaces": "warn",
"no-unsafe-finally": "warn",
"no-var": "warn",
"spaced-comment": "warn"
}
}

View File

@@ -4,8 +4,8 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"dbaeumer.vscode-eslint",
"firsttris.vscode-jest-runner",
"ms-vscode.vscode-typescript-tslint-plugin",
"msjsdiag.debugger-for-chrome",
"slevesque.shader",
"stpn.vscode-graphql",

View File

@@ -6,4 +6,7 @@
"*.vert.ts": "glsl",
"*.gql.ts": "graphql"
},
"eslint.options": {
"ignorePattern": ["webpack.config.js", "scripts/*"],
}
}

View File

@@ -31,12 +31,14 @@ The core of Mol* currently consists of these modules (see under `src/`):
- `mol-state` State representation tree with state saving and automatic updates.
- `mol-app` Components for builduing UIs.
- `mol-plugin` Allow to define modular Mol* plugin instances utilizing `mol-state` and `mol-canvas3d`.
- `mol-plugin-ui` React based user interface for the Mol* plugin. Some components of the UI are usable outside the main plugin and can be integrated to 3rd party solutions.
- `mol-util` Useful things that do not fit elsewhere.
Moreover, the project contains the imlementation of `servers`, including
- `servers/model` A tool for accessing coordinate and annotation data of molecular structures.
- `servers/volume` A tool for accessing volumetric experimental data related to molecular structures.
- `servers/plugin-state` A basic server to store Mol* Plugin states.
The project also contains performance tests (`perf-tests`), `examples`, and basic proof of concept `apps` (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
@@ -89,10 +91,11 @@ Install CIFTools `npm install ciftools -g`
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
**GraphQL schemas**
node data/rcsb-graphql/codegen.js
./node_modules/.bin/graphql-codegen -c ./data/rcsb-graphql/codegen.yml
### Other scripts
**Create chem comp bond table**

View File

@@ -1,60 +0,0 @@
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

@@ -1,22 +0,0 @@
const { generate } = require('graphql-code-generator')
const path = require('path')
const basePath = path.join(__dirname, '..', '..', 'src', 'mol-model-props', 'rcsb', 'graphql')
generate({
schema: 'http://rest-staging.rcsb.org/graphql',
documents: {
[path.join(basePath, 'symmetry.gql.ts')]: {
loader: path.join(__dirname, 'loader.js')
},
},
generates: {
[path.join(basePath, 'types.ts')]: {
plugins: ['time', 'typescript-common', 'typescript-client']
}
},
overwrite: true,
config: path.join(__dirname, 'codegen.json')
}, true).then(
() => console.log('done')
).catch(e => console.error(e))

View File

@@ -1,6 +0,0 @@
{
"flattenTypes": true,
"generatorConfig": {
"immutableTypes": true
}
}

View File

@@ -0,0 +1,12 @@
schema: https://data-beta.rcsb.org/graphql
documents: './src/mol-model-props/rcsb/graphql/symmetry.gql.ts'
generates:
'./src/mol-model-props/rcsb/graphql/types.d.ts':
plugins:
- add: '/* eslint-disable */'
- time
- typescript
- typescript-operations
config:
immutableTypes: true
skipTypename: true

View File

@@ -1,14 +0,0 @@
const { parse } = require('graphql');
const { readFileSync } = require('fs');
module.exports = function(docString, config) {
const str = readFileSync(docString, { encoding: 'utf-8' }).trim()
.replace(/^export default `/, '')
.replace(/`$/, '')
return [
{
filePath: docString,
content: parse(str)
}
];
};

15466
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.4.2",
"version": "0.5.0-dev.2",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -11,7 +11,7 @@
"url": "https://github.com/molstar/molstar/issues"
},
"scripts": {
"lint": "tslint src/**/*.ts",
"lint": "eslint src/**/*.ts",
"test": "npm run lint && jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"build-tsc": "tsc",
@@ -24,7 +24,8 @@
"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",
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
"plugin-state": "node lib/servers/plugin-state/index.js",
"preversion": "npm run test",
"postversion": "git push && git push --tags",
"prepublishOnly": "npm run test && npm run build"
@@ -63,55 +64,66 @@
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^1.12.2",
"@graphql-codegen/cli": "^1.12.2",
"@graphql-codegen/time": "^1.12.2",
"@graphql-codegen/typescript": "^1.12.2",
"@graphql-codegen/typescript-graphql-files-modules": "^1.12.2",
"@graphql-codegen/typescript-graphql-request": "^1.12.2",
"@graphql-codegen/typescript-operations": "^1.12.2",
"@types/cors": "^2.8.6",
"@typescript-eslint/eslint-plugin": "^2.19.2",
"@typescript-eslint/eslint-plugin-tslint": "^2.19.2",
"@typescript-eslint/parser": "^2.19.2",
"benchmark": "^2.1.4",
"circular-dependency-plugin": "^5.2.0",
"concurrently": "^5.0.0",
"cpx": "^1.5.0",
"css-loader": "^3.2.0",
"concurrently": "^5.1.0",
"cpx2": "^2.0.0",
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^4.2.0",
"file-loader": "^5.0.2",
"fs-extra": "^8.1.0",
"graphql-code-generator": "^0.18.2",
"graphql-codegen-time": "^0.18.2",
"graphql-codegen-typescript-template": "^0.18.2",
"http-server": "^0.11.1",
"jest": "^24.9.0",
"http-server": "^0.12.1",
"jest": "^25.1.0",
"jest-raw-loader": "^1.0.1",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"raw-loader": "^3.1.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"pascal-case": "^3.1.1",
"raw-loader": "^4.0.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",
"sass-loader": "^8.0.2",
"simple-git": "^1.131.0",
"style-loader": "^1.1.3",
"ts-jest": "^25.2.0",
"typescript": "^3.7.5",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@types/argparse": "^1.0.36",
"@types/argparse": "^1.0.38",
"@types/benchmark": "^1.0.31",
"@types/compression": "1.0.1",
"@types/compression": "1.7.0",
"@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",
"@types/jest": "^25.1.2",
"@types/node": "^13.7.0",
"@types/node-fetch": "^2.5.4",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/swagger-ui-dist": "3.0.5",
"argparse": "^1.0.10",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"graphql": "^14.5.8",
"graphql": "^14.6.0",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"rxjs": "^6.5.3",
"swagger-ui-dist": "^3.24.3",
"util.promisify": "^1.0.0",
"rxjs": "^6.5.4",
"swagger-ui-dist": "^3.25.0",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.0"
}
}

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
@@ -9,9 +10,8 @@ import { Model, ElementIndex } from '../../mol-model/structure';
import { Color } from '../../mol-util/color';
export const StripedResidues = CustomElementProperty.create<number>({
isStatic: true,
label: 'Residue Stripes',
name: 'basic-wrapper-residue-striping',
display: 'Residue Stripes',
getData(model: Model) {
const map = new Map<ElementIndex, number>();
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
@@ -24,7 +24,7 @@ export const StripedResidues = CustomElementProperty.create<number>({
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
defaultColor: Color(0x777777)
},
format(e) {
getLabel(e) {
return e === 0 ? 'Odd stripe' : 'Even stripe'
}
})

View File

@@ -4,9 +4,9 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginUIComponent } from '../../mol-plugin/ui/base';
import { PluginUIComponent } from '../../mol-plugin-ui/base';
import * as React from 'react';
import { TransformUpdaterControl } from '../../mol-plugin/ui/state/update-transform';
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
export class BasicWrapperControls extends PluginUIComponent {

View File

@@ -29,7 +29,7 @@ export namespace StateHelper {
}
export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
return b.apply(StateTransforms.Model.StructureFromModel, { tags: 'structure' })
return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
};
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {

View File

@@ -21,7 +21,7 @@ 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')
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
@@ -41,12 +41,15 @@ class BasicWrapper {
// left: 'none',
// right: BasicWrapperControls
}
},
components: {
remoteState: 'none'
}
});
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.Descriptor.name, StripedResidues.colorTheme!);
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
@@ -60,7 +63,7 @@ class BasicWrapper {
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.CustomModelProperties, { properties: [StripedResidues.Descriptor.name] }, { ref: 'props', state: { isGhost: false } })
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
}
@@ -108,11 +111,13 @@ class BasicWrapper {
}
setBackground(color: number) {
const renderer = this.plugin.canvas3d.props.renderer;
const renderer = this.plugin.canvas3d!.props.renderer;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
@@ -136,7 +141,7 @@ class BasicWrapper {
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
const tree = state.build();
const colorTheme = { name: StripedResidues.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.Descriptor.name).defaultValues };
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
for (const v of visuals) {
tree.to(v).update(old => ({ ...old, colorTheme }));

View File

@@ -50,16 +50,16 @@ export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionT
export const StaticSuperpositionTestData: SuperpositionTestInput = [
{ pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() },
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows(
[[0.406, 0.879, 0.248, -200.633],
[0.693, -0.473, 0.544, 73.403],
[0.596, -0.049, -0.802, -14.209],
[0, 0, 0, 1]] )},
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows(
[[-0.053, -0.077, 0.996, -45.633],
[-0.312, 0.949, 0.057, -12.255],
[-0.949, -0.307, -0.074, 53.562],
[0, 0, 0, 1]] )}
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
[0.406, 0.879, 0.248, -200.633],
[0.693, -0.473, 0.544, 73.403],
[0.596, -0.049, -0.802, -14.209],
[0, 0, 0, 1]] )},
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
[-0.053, -0.077, 0.996, -45.633],
[-0.312, 0.949, 0.057, -12.255],
[-0.949, -0.307, -0.074, 53.562],
[0, 0, 0, 1]] )}
];
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {

View File

@@ -226,8 +226,8 @@ const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'
const PVCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/aa-variants-v1.cif'
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
addHelp: true,
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
});
parser.addArgument('out', {
help: 'Generated file output path.'

View File

@@ -13,7 +13,7 @@ import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/tran
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')
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
@@ -97,15 +97,15 @@ class LightingDemo {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
...props,
multiSample: {
...this.plugin.canvas3d.props.multiSample,
...this.plugin.canvas3d!.props.multiSample,
...props.multiSample
},
renderer: {
...this.plugin.canvas3d.props.renderer,
...this.plugin.canvas3d!.props.renderer,
...props.renderer
},
postprocessing: {
...this.plugin.canvas3d.props.postprocessing,
...this.plugin.canvas3d!.props.postprocessing,
...props.postprocessing
},
}});

View File

@@ -22,6 +22,7 @@ function paramInfo(param: PD.Any, offset: number): string {
case 'color-list': return `One of ${oToS(param.options)}`;
case 'vec3': return `3D vector [x, y, z]`;
case 'file': return `JavaScript File Handle`;
case 'file-list': return `JavaScript FileList Handle`;
case 'select': return `One of ${oToS(param.options)}`;
case 'text': return 'String';
case 'interval': return `Interval [min, max]`;
@@ -38,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string {
}
}
function oToS(options: [string, string][]) {
function oToS(options: readonly (readonly [string, string])[]) {
return options.map(o => `'${o[0]}'`).join(', ');
}

View File

@@ -16,6 +16,8 @@ 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';
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
async function downloadFromPdb(pdb: string) {
@@ -50,8 +52,10 @@ export function residueLabel(model: Model, rI: number) {
export function printSecStructure(model: Model) {
console.log('\nSecondary Structure\n=============');
const { residues } = model.atomicHierarchy;
const { key, elements } = model.properties.secondaryStructure;
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
if (!secondaryStructure) return
const { key, elements } = secondaryStructure
const count = residues._rowCount;
let rI = 0;
while (rI < count) {
@@ -65,14 +69,14 @@ export function printSecStructure(model: Model) {
}
}
export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
export function printBonds(structure: Structure, showIntra: boolean, showInter: boolean) {
if (showIntra) {
console.log('\nIntra Unit Links\n=============');
console.log('\nIntra Unit Bonds\n=============');
for (const unit of structure.units) {
if (!Unit.isAtomic(unit)) continue;
const elements = unit.elements;
const { a, b, edgeCount } = unit.links;
const { a, b, edgeCount } = unit.bonds;
const { model } = unit;
if (!edgeCount) continue;
@@ -86,20 +90,20 @@ export function printLinks(structure: Structure, showIntra: boolean, showInter:
}
if (showInter) {
console.log('\nInter Unit Links\n=============');
const links = structure.links;
console.log('\nInter Unit Bonds\n=============');
const bonds = structure.interUnitBonds;
for (const unit of structure.units) {
if (!Unit.isAtomic(unit)) continue;
for (const pairLinks of links.getLinkedUnits(unit)) {
if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
for (const pairBonds of bonds.getConnectedUnits(unit)) {
if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
const { unitA, unitB } = pairLinks;
console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
const { unitA, unitB } = pairBonds;
console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
for (const aI of pairLinks.linkedElementIndices) {
for (const link of pairLinks.getBonds(aI)) {
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
for (const aI of pairBonds.connectedIndices) {
for (const bond of pairBonds.getEdges(aI)) {
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[bond.indexB])}`);
}
}
}
@@ -148,7 +152,7 @@ export function printRings(structure: Structure) {
export function printUnits(structure: Structure) {
console.log('\nUnits\n=============');
const l = StructureElement.Location.create();
const l = StructureElement.Location.create(structure);
for (const unit of structure.units) {
l.unit = unit;
@@ -179,7 +183,8 @@ export function printUnits(structure: Structure) {
export function printSymmetryInfo(model: Model) {
console.log('\nSymmetry Info\n=============');
const { symmetry } = model;
const symmetry = ModelSymmetry.Provider.get(model)
if (!symmetry) return
const { size, anglesInRadians } = symmetry.spacegroup.cell;
console.log(`Spacegroup: ${symmetry.spacegroup.name} size: ${Vec3.toString(size)} angles: ${Vec3.toString(anglesInRadians)}`);
console.log(`Assembly names: ${symmetry.assemblies.map(a => a.id).join(', ')}`);
@@ -214,8 +219,8 @@ async function run(frame: CifFrame, args: Args) {
if (args.units) printUnits(structure);
if (args.sym) printSymmetryInfo(models[0]);
if (args.rings) printRings(structure);
if (args.intraLinks) printLinks(structure, true, false);
if (args.interLinks) printLinks(structure, false, true);
if (args.intraBonds) printBonds(structure, true, false);
if (args.interBonds) printBonds(structure, false, true);
if (args.mod) printModRes(models[0]);
if (args.sec) printSecStructure(models[0]);
}
@@ -242,8 +247,8 @@ parser.addArgument(['--seq'], { help: 'print sequence', action: 'storeTrue' });
parser.addArgument(['--units'], { help: 'print units', action: 'storeTrue' });
parser.addArgument(['--sym'], { help: 'print symmetry', action: 'storeTrue' });
parser.addArgument(['--rings'], { help: 'print rings', action: 'storeTrue' });
parser.addArgument(['--intraLinks'], { help: 'print intra unit links', action: 'storeTrue' });
parser.addArgument(['--interLinks'], { help: 'print inter unit links', action: 'storeTrue' });
parser.addArgument(['--intraBonds'], { help: 'print intra unit bonds', action: 'storeTrue' });
parser.addArgument(['--interBonds'], { help: 'print inter unit bonds', action: 'storeTrue' });
parser.addArgument(['--mod'], { help: 'print modified residues', action: 'storeTrue' });
parser.addArgument(['--sec'], { help: 'print secoundary structure', action: 'storeTrue' });
interface Args {
@@ -256,8 +261,8 @@ interface Args {
units?: boolean,
sym?: boolean,
rings?: boolean,
intraLinks?: boolean,
interLinks?: boolean,
intraBonds?: boolean,
interBonds?: boolean,
mod?: boolean,
sec?: boolean,
}

View File

@@ -16,7 +16,7 @@ import { Table } from '../../mol-data/db';
import { StringBuilder } from '../../mol-util';
import { Task } from '../../mol-task';
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
import { createEmptyTheme } from '../../mol-theme/theme';
import { Theme } from '../../mol-theme/theme';
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
require('util.promisify').shim();
@@ -40,7 +40,7 @@ function print(data: Volume) {
}
async function doMesh(data: Volume, filename: string) {
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, createEmptyTheme(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
// Export the mesh in OBJ format.
@@ -77,8 +77,8 @@ async function run(url: string, meshFilename: string) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
description: 'Info about VolumeData from mol-model module'
addHelp: true,
description: 'Info about VolumeData from mol-model module'
});
parser.addArgument([ '--emdb', '-e' ], {
help: 'EMDB id, for example 8116',

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -11,7 +11,7 @@ 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 { trajectoryFromMmCIF, MmcifFormat } 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';
@@ -28,11 +28,10 @@ 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';
import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`
@@ -124,7 +123,10 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
}
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
const d = model.sourceData.data.atom_site
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
const { db } = model.sourceData.data
const d = db.atom_site
const n = d._rowCount
const rowCount = n * transforms.length
@@ -201,8 +203,8 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
}
const categories = {
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
entity: CifCategory.ofTable('entity', db.entity),
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
atom_site: CifCategory.ofFields('atom_site', _atom_site)
}
@@ -217,8 +219,8 @@ 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)
const format = MmcifFormat.fromFrame(cif)
const models = await createModels(format.data.db, format, ctx)
return models[0]
})
@@ -279,6 +281,9 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
})
}
const RepresentationOptions = PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'] as const)
type RepresentationName = (typeof RepresentationOptions)[0][0]
export const LoadCellPackModel = StateAction.build({
display: { name: 'Load CellPack Model' },
params: {
@@ -289,15 +294,11 @@ export const LoadCellPackModel = StateAction.build({
['influenza_model1.json', 'influenza_model1'],
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
['curveTest', 'Curve Test'],
]),
] as const),
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][])
representation: PD.Select('spacefill', RepresentationOptions)
}, { isExpanded: true })
},
from: PSO.Root
@@ -393,7 +394,7 @@ export const LoadCellPackModel = StateAction.build({
MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
])
})
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } })
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } }) as any
}
cellpackTree
.apply(StateTransforms.Representation.StructureRepresentation3D,
@@ -425,7 +426,7 @@ export const LoadCellPackModel = StateAction.build({
console.timeEnd('cellpack')
}));
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
function getReprParams(ctx: PluginContext, params: { representation: RepresentationName, traceOnly: boolean }) {
const { representation, traceOnly } = params
switch (representation) {
case 'spacefill':
@@ -452,6 +453,11 @@ function getReprParams(ctx: PluginContext, params: { representation: 'spacefill'
ctx.structureRepresentation.registry.get('point'),
() => ({ ignoreHydrogens: true })
] as [any, any]
case 'ellipsoid':
return [
ctx.structureRepresentation.registry.get('orientation'),
() => ({})
] as [any, any]
}
}

View File

@@ -1,167 +1,168 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// /**
// * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
// *
// * @author David Sehnal <david.sehnal@gmail.com>
// */
import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
import { StateTransforms } from '../../../mol-plugin/state/transforms';
import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
import { PluginContext } from '../../../mol-plugin/context';
import { PluginStateObject } from '../../../mol-plugin/state/objects';
import { ParamDefinition } from '../../../mol-util/param-definition';
import { PluginCommands } from '../../../mol-plugin/command';
import { Vec3 } from '../../../mol-math/linear-algebra';
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/names';
import { Camera } from '../../../mol-canvas3d/camera';
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
// import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
// import { StateTransforms } from '../../../mol-plugin/state/transforms';
// import { createModelTree } from '../../../mol-plugin/state/actions/structure';
// import { PluginContext } from '../../../mol-plugin/context';
// import { PluginStateObject } from '../../../mol-plugin/state/objects';
// import { ParamDefinition } from '../../../mol-util/param-definition';
// import { PluginCommands } from '../../../mol-plugin/command';
// import { Vec3 } from '../../../mol-math/linear-algebra';
// 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/names';
// import { Camera } from '../../../mol-canvas3d/camera';
// import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
// import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
export const CreateJoleculeState = StateAction.build({
display: { name: 'Jolecule State Import' },
params: { id: ParamDefinition.Text('1mbo') },
from: PluginStateObject.Root
})(async ({ ref, state, params }, plugin: PluginContext) => {
try {
const id = params.id.trim().toLowerCase();
const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
// export const CreateJoleculeState = StateAction.build({
// display: { name: 'Jolecule State Import' },
// params: { id: ParamDefinition.Text('1mbo') },
// from: PluginStateObject.Root
// })(async ({ ref, state, params }, plugin: PluginContext) => {
// try {
// const id = params.id.trim().toLowerCase();
// const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
data.sort((a, b) => a.order - b.order);
// data.sort((a, b) => a.order - b.order);
await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
plugin.state.snapshots.clear();
// await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
// plugin.state.snapshots.clear();
const template = createTemplate(plugin, state, id);
const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
for (const s of snapshots) {
plugin.state.snapshots.add(s);
}
// const template = createTemplate(plugin, state, id);
// const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
// for (const s of snapshots) {
// plugin.state.snapshots.add(s);
// }
PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
} catch (e) {
plugin.log.error(`Jolecule Failed: ${e}`);
}
});
// PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
// } catch (e) {
// plugin.log.error(`Jolecule Failed: ${e}`);
// }
// });
interface JoleculeSnapshot {
order: number,
distances: { i_atom1: number, i_atom2: number }[],
labels: { i_atom: number, text: string }[],
camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
selected: number[],
text: string
}
// interface JoleculeSnapshot {
// order: number,
// distances: { i_atom1: number, i_atom2: number }[],
// labels: { i_atom: number, text: string }[],
// camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
// selected: number[],
// text: string
// }
function createTemplate(plugin: PluginContext, state: State, id: string) {
const b = new StateBuilder.Root(state.tree);
const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
const model = createModelTree(data, 'cif');
const structure = model.apply(StateTransforms.Model.StructureFromModel, {});
complexRepresentation(plugin, structure, { hideWater: true });
return { tree: b.getTree(), structure: structure.ref };
}
// function createTemplate(plugin: PluginContext, state: State, id: string) {
// const b = new StateBuilder.Root(state.tree);
// const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
// const model = createModelTree(data, 'cif');
// const structure = model.apply(StateTransforms.Model.StructureFromModel);
// createDefaultStructureComplex(plugin, structure);
// return { tree: b.getTree(), structure: structure.ref };
// }
const labelOptions: ParamDefinition.Values<Text.Params> = {
...ParamDefinition.getDefaultValues(Text.Params),
tether: true,
sizeFactor: 1.3,
attachment: 'bottom-right',
offsetZ: 10,
background: true,
backgroundMargin: 0.2,
backgroundColor: ColorNames.skyblue,
backgroundOpacity: 0.9
}
// const distanceLabelOptions = {
// const labelOptions: ParamDefinition.Values<Text.Params> = {
// ...ParamDefinition.getDefaultValues(Text.Params),
// sizeFactor: 1,
// offsetX: 0,
// offsetY: 0,
// tether: true,
// sizeFactor: 1.3,
// attachment: 'bottom-right',
// offsetZ: 10,
// background: true,
// backgroundMargin: 0.2,
// backgroundColor: ColorNames.snow,
// backgroundColor: ColorNames.skyblue,
// backgroundOpacity: 0.9
// }
function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
const b = new StateBuilder.Root(template.tree);
// // const distanceLabelOptions = {
// // ...ParamDefinition.getDefaultValues(Text.Params),
// // sizeFactor: 1,
// // offsetX: 0,
// // offsetY: 0,
// // offsetZ: 10,
// // background: true,
// // backgroundMargin: 0.2,
// // backgroundColor: ColorNames.snow,
// // backgroundOpacity: 0.9
// // }
let i = 0;
for (const l of params.e.labels) {
const expression = createExpression([l.i_atom]);
const group = b.to(template.structure)
.group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
// function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
// const b = new StateBuilder.Root(template.tree);
group
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
.apply(StateTransforms.Representation.StructureLabels3D, {
target: { name: 'static-text', params: { value: l.text || '' } },
options: labelOptions
});
// let i = 0;
// for (const l of params.e.labels) {
// const expression = createExpression([l.i_atom]);
// const group = b.to(template.structure)
// .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
group
.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.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.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
// });
// }
return PluginStateSnapshotManager.Entry({
id: UUID.create22(),
data: { tree: StateTree.toJSON(b.getTree()) },
camera: {
current: getCameraSnapshot(params.e.camera),
transitionStyle: 'animate',
transitionDurationInMs: 350
}
}, {
name: params.e.text
});
}
// group
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
// .apply(StateTransforms.Representation.StructureLabels3D, {
// target: { name: 'static-text', params: { value: l.text || '' } },
// options: labelOptions
// });
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
const direction = Vec3.sub(Vec3(), e.pos, e.in);
Vec3.normalize(direction, direction);
const up = Vec3.sub(Vec3(), e.pos, e.up);
Vec3.normalize(up, up);
// group
// .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.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.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
// // });
// // }
// return PluginStateSnapshotManager.Entry({
// id: UUID.create22(),
// data: { tree: StateTree.toJSON(b.getTree()) },
// camera: {
// current: getCameraSnapshot(params.e.camera),
// transitionStyle: 'animate',
// transitionDurationInMs: 350
// }
// }, {
// name: params.e.text
// });
// }
const s: Camera.Snapshot = {
mode: 'perspective',
fov: Math.PI / 4,
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 getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
// const direction = Vec3.sub(Vec3(), e.pos, e.in);
// Vec3.normalize(direction, direction);
// const up = Vec3.sub(Vec3(), e.pos, e.up);
// Vec3.normalize(up, up);
function createExpression(atomIndices: number[]) {
if (atomIndices.length === 0) return MS.struct.generator.empty();
// const s: Camera.Snapshot = {
// mode: 'perspective',
// fov: Math.PI / 4,
// 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;
// }
return MS.struct.generator.atomGroups({
'atom-test': atomIndices.length === 1
? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
: MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
'group-by': 0
});
}
// function createExpression(atomIndices: number[]) {
// if (atomIndices.length === 0) return MS.struct.generator.empty();
// return MS.struct.generator.atomGroups({
// 'atom-test': atomIndices.length === 1
// ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
// : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
// 'group-by': 0
// });
// }

View File

@@ -5,16 +5,16 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import '../../mol-util/polyfill';
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')
require('mol-plugin-ui/skin/light.scss')
function getParam(name: string, regex: string): string {
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
@@ -27,7 +27,7 @@ function init() {
const spec: PluginSpec = {
actions: [
...DefaultPluginSpec.actions,
PluginSpec.Action(CreateJoleculeState),
// PluginSpec.Action(CreateJoleculeState),
PluginSpec.Action(LoadCellPackModel),
PluginSpec.Action(StructureFromCellpack),
],
@@ -42,7 +42,8 @@ function init() {
controls: {
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
}
}
},
config: DefaultPluginSpec.config
};
const plugin = createPlugin(document.getElementById('app')!, spec);
trySetSnapshot(plugin);

View File

@@ -1,12 +1,14 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure';
import { Color } from '../../mol-util/color';
import { CustomProperty } from '../../mol-model-props/common/custom-property';
const EvolutionaryConservationPalette: Color[] = [
[255, 255, 129], // insufficient
@@ -23,13 +25,13 @@ const EvolutionaryConservationPalette: Color[] = [
const EvolutionaryConservationDefaultColor = Color(0x999999);
export const EvolutionaryConservation = CustomElementProperty.create<number>({
isStatic: true,
name: 'proteopedia-wrapper-evolutionary-conservation',
display: 'Evolutionary Conservation',
async getData(model: Model) {
label: 'Evolutionary Conservation',
type: 'static',
async getData(model: Model, ctx: CustomProperty.Context) {
const id = model.entryId.toLowerCase();
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
const json = await req.json();
const url = `https://proteopedia.org/cgi-bin/cnsrf?${id}`
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
const annotations = (json && json.residueAnnotations) || [];
const conservationMap = new Map<string, number>();
@@ -65,7 +67,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
},
defaultColor: EvolutionaryConservationDefaultColor
},
format(e) {
getLabel(e) {
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
return e ? `Evolutionary Conservation: ${e}` : void 0;
}

View File

@@ -6,7 +6,7 @@
*/
import { Unit, StructureProperties, StructureElement, Link } from '../../mol-model/structure';
import { Unit, StructureProperties, StructureElement, Bond } from '../../mol-model/structure';
import { Color } from '../../mol-util/color';
import { Location } from '../../mol-model/location';
@@ -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.Location.create()
const l = StructureElement.Location.create(ctx.structure)
const { models } = ctx.structure
const asymIdSerialMap = new Map<string, number>()
for (let i = 0, il = models.length; i < il; ++i) {
@@ -71,7 +71,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
const asym_id = getAsymId(location.unit);
const o = asymIdSerialMap.get(asym_id(location)) || 0;
return colors[o % colorCount].color;
} else if (Link.isLocation(location)) {
} else if (Bond.isLocation(location)) {
const asym_id = getAsymId(location.aUnit)
l.unit = location.aUnit
l.element = location.aUnit.elements[location.aIndex]

View File

@@ -9,6 +9,7 @@ import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/re
import { BuiltInColorThemeName } from '../../mol-theme/color';
import { AminoAcidNames } from '../../mol-model/structure/model/types';
import { PluginContext } from '../../mol-plugin/context';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
export interface ModelInfo {
hetResidues: { name: string, indices: ResidueIndex[] }[],
@@ -72,10 +73,11 @@ export namespace ModelInfo {
}
const preferredAssemblyId = await pref;
const symmetry = ModelSymmetry.Provider.get(model)
return {
hetResidues: hetResidues,
assemblies: model.symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })),
assemblies: symmetry ? symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })) : [],
preferredAssemblyId
};
}

View File

@@ -32,7 +32,7 @@ import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3
// import { Vec3 } from 'mol-math/linear-algebra';
// import { ParamDefinition } from 'mol-util/param-definition';
// import { Text } from 'mol-geo/geometry/text/text';
require('../../mol-plugin/skin/light.scss')
require('../../mol-plugin-ui/skin/light.scss')
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 3;
@@ -68,9 +68,9 @@ class MolStarProteopediaWrapper {
const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
}
get state() {
@@ -94,7 +94,7 @@ class MolStarProteopediaWrapper {
const model = this.state.build().to(StateElements.Model);
const s = model
.apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: StateElements.ModelProps, state: { isGhost: false } })
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [EvolutionaryConservation.propertyProvider.descriptor.name], properties: {} }, { ref: StateElements.ModelProps, state: { isGhost: false } })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
@@ -160,9 +160,9 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.WaterVisual);
} else {
root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.water && style.water.kind) || 'ball-and-stick',
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.water && style.water.kind) || 'ball-and-stick',
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
}
}
@@ -230,11 +230,13 @@ class MolStarProteopediaWrapper {
}
setBackground(color: number) {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
if (!this.plugin.canvas3d) return;
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
@@ -297,7 +299,7 @@ class MolStarProteopediaWrapper {
// }
const tree = state.build();
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
if (!params || !!params.sequence) {
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
@@ -383,7 +385,8 @@ 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, Math.max(sphere.radius, 5));
const radius = Math.max(sphere.radius, 5)
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius, radius);
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
}
}
@@ -415,8 +418,7 @@ class MolStarProteopediaWrapper {
},
download: async (url: string) => {
try {
const data = await this.plugin.runTask(this.plugin.fetch({ url }));
const snapshot = JSON.parse(data);
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
await this.plugin.state.setSnapshot(snapshot);
} catch (e) {
console.log(e);

View File

@@ -6,12 +6,12 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PluginUIComponent } from '../../../mol-plugin/ui/base';
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
import { PluginUIComponent } from '../../../mol-plugin-ui/base';
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 { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
import { StateElements } from '../helpers';
export class ControlsWrapper extends PluginUIComponent {

View File

@@ -82,12 +82,12 @@ class Camera {
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
}
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
getFocus(target: Vec3, radiusNear: number, radiusFar: 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 targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
Vec3.sub(this.deltaDirection, this.target, this.position)
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
@@ -96,15 +96,18 @@ class Camera {
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
state.target = Vec3.clone(target)
state.radius = radius
state.radiusNear = radiusNear
state.radiusFar = radiusFar
state.position = Vec3.clone(this.newPosition)
if (up) Vec3.matchDirection(state.up, up, state.up)
return state
}
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radius > 0) this.setState(this.getFocus(target, radius, up, dir), durationMs);
focus(target: Vec3, radiusNear: number, radiusFar: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radiusNear > 0 && radiusFar > 0) {
this.setState(this.getFocus(target, radiusNear, radiusFar, up, dir), durationMs);
}
}
project(out: Vec4, point: Vec3) {
@@ -158,8 +161,10 @@ namespace Camera {
up: Vec3.create(0, 1, 0),
target: Vec3.create(0, 0, 0),
radius: 10,
radiusNear: 10,
radiusFar: 10,
fog: 50,
clipFar: true
};
}
@@ -171,8 +176,10 @@ namespace Camera {
up: Vec3
target: Vec3
radius: number
radiusNear: number
radiusFar: number
fog: number
clipFar: boolean
}
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
@@ -185,8 +192,10 @@ namespace Camera {
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.radius !== 'undefined') out.radius = source.radius;
if (typeof source.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
if (typeof source.fog !== 'undefined') out.fog = source.fog;
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
return out;
}
@@ -253,17 +262,15 @@ function updatePers(camera: Camera) {
}
function updateClip(camera: Camera) {
const { radius, mode, fog } = camera.state
const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
const cDist = Vec3.distance(camera.position, camera.target)
const bRadius = Math.max(1, radius)
let near = cDist - bRadius
let far = cDist + bRadius
let near = cDist - radiusNear
let far = cDist + (clipFar ? radiusNear : radiusFar)
const fogNearFactor = -(50 - fog) / 50
let fogNear = cDist - (bRadius * fogNearFactor)
let fogFar = cDist + bRadius
let fogNear = cDist - (radiusNear * fogNearFactor)
let fogFar = far
if (mode === 'perspective') {
// set at least to 5 to avoid slow sphere impostor rendering

View File

@@ -79,7 +79,9 @@ namespace CameraTransitionManager {
// 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);
out.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
// TODO take change of `clipFar` into account
out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
// Lerp fov & fog
out.fov = lerp(source.fov, target.fov, t);

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { BehaviorSubject, Subscription } from 'rxjs';
@@ -26,18 +27,21 @@ import { SetUtils } from '../mol-util/set';
import { Canvas3dInteractionHelper } from './helper/interaction-events';
import { PostprocessingParams, PostprocessingPass } from './passes/postprocessing';
import { MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
import { GLRenderingContext } from '../mol-gl/webgl/compat';
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';
import { Sphere3D } from '../mol-math/geometry';
import { isDebugMode } from '../mol-util/debug';
export const Canvas3DParams = {
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
cameraClipFar: PD.Boolean(true),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
@@ -53,29 +57,33 @@ export { Canvas3D }
interface Canvas3D {
readonly webgl: WebGLContext,
add: (repr: Representation.Any) => void
remove: (repr: Representation.Any) => void
update: (repr?: Representation.Any, keepBoundingSphere?: boolean) => void
clear: () => void
add(repr: Representation.Any): void
remove(repr: Representation.Any): void
/**
* This function must be called if animate() is not set up so that add/remove actions take place.
*/
commit(): void
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
clear(): void
// draw: (force?: boolean) => void
requestDraw: (force?: boolean) => void
animate: () => void
identify: (x: number, y: number) => PickingId | undefined
mark: (loci: Representation.Loci, action: MarkerAction) => void
getLoci: (pickingId: PickingId) => Representation.Loci
requestDraw(force?: boolean): void
animate(): void
identify(x: number, y: number): PickingId | undefined
mark(loci: Representation.Loci, action: MarkerAction): void
getLoci(pickingId: PickingId): Representation.Loci
readonly didDraw: BehaviorSubject<now.Timestamp>
readonly reprCount: BehaviorSubject<number>
handleResize: () => void
handleResize(): void
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
resetCamera: () => void
requestCameraReset(): void
readonly camera: Camera
downloadScreenshot: () => void
getPixelData: (variant: GraphicsRenderVariant) => PixelData
setProps: (props: Partial<Canvas3DProps>) => void
getImagePass: () => ImagePass
readonly boundingSphere: Readonly<Sphere3D>
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>
@@ -83,15 +91,15 @@ interface Canvas3D {
readonly stats: RendererStats
readonly interaction: Canvas3dInteractionHelper['events']
dispose: () => void
dispose(): void
}
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 HoverEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
const gl = getGLContext(canvas, {
@@ -103,10 +111,45 @@ namespace Canvas3D {
})
if (gl === null) throw new Error('Could not create a WebGL rendering context')
const input = InputObserver.fromElement(canvas)
return Canvas3D.create(gl, input, props, runTask)
const webgl = createContext(gl)
if (isDebugMode) {
const loseContextExt = gl.getExtension('WEBGL_lose_context')
if (loseContextExt) {
canvas.addEventListener('mousedown', e => {
if (webgl.isContextLost) return
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return
console.log('lose context')
loseContextExt.loseContext()
setTimeout(() => {
if (!webgl.isContextLost) return
console.log('restore context')
loseContextExt.restoreContext()
}, 1000)
}, false)
}
}
// https://www.khronos.org/webgl/wiki/HandlingContextLost
canvas.addEventListener('webglcontextlost', e => {
webgl.setContextLost()
e.preventDefault()
if (isDebugMode) console.log('context lost')
}, false)
canvas.addEventListener('webglcontextrestored', () => {
if (!webgl.isContextLost) return
webgl.handleContextRestored()
if (isDebugMode) console.log('context restored')
}, false)
return Canvas3D.create(webgl, input, props, runTask)
}
export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
const p = { ...DefaultCanvas3DParams, ...props }
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
@@ -116,7 +159,7 @@ namespace Canvas3D {
const startTime = now()
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
const webgl = createContext(gl)
const { gl, contextRestored } = webgl
let width = gl.drawingBufferWidth
let height = gl.drawingBufferHeight
@@ -126,7 +169,8 @@ namespace Canvas3D {
const camera = new Camera({
position: Vec3.create(0, 0, 100),
mode: p.cameraMode,
fog: p.cameraFog
fog: p.cameraFog,
clipFar: p.cameraClipFar
})
const controls = TrackballControls.create(input, camera, p.trackball)
@@ -139,6 +183,11 @@ namespace Canvas3D {
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
const contextRestoredSub = contextRestored.subscribe(() => {
pickPass.pickDirty = true
draw(true)
})
let drawPending = false
let cameraResetRequested = false
@@ -174,8 +223,8 @@ namespace Canvas3D {
}
}
function render(variant: 'pick' | 'draw', force: boolean) {
if (scene.isCommiting) return false
function render(force: boolean) {
if (webgl.isContextLost) return false
let didRender = false
controls.update(currentTime)
@@ -184,21 +233,14 @@ namespace Canvas3D {
multiSample.update(force || cameraChanged, currentTime)
if (force || cameraChanged || multiSample.enabled) {
switch (variant) {
case 'pick':
pickPass.render()
break;
case 'draw':
renderer.setViewport(0, 0, width, height)
if (multiSample.enabled) {
multiSample.render(true)
} else {
drawPass.render(!postprocessing.enabled)
if (postprocessing.enabled) postprocessing.render(true)
}
pickPass.pickDirty = true
break;
renderer.setViewport(0, 0, width, height)
if (multiSample.enabled) {
multiSample.render(true, p.transparentBackground)
} else {
drawPass.render(!postprocessing.enabled, p.transparentBackground)
if (postprocessing.enabled) postprocessing.render(true)
}
pickPass.pickDirty = true
didRender = true
}
@@ -209,7 +251,7 @@ namespace Canvas3D {
let currentTime = 0;
function draw(force?: boolean) {
if (render('draw', !!force || forceNextDraw)) {
if (render(!!force || forceNextDraw)) {
didDraw.next(now() - startTime as now.Timestamp)
}
forceNextDraw = false;
@@ -224,45 +266,88 @@ namespace Canvas3D {
function animate() {
currentTime = now();
commit();
camera.transition.tick(currentTime);
draw(false);
if (!camera.transition.inTransition) interactionHelper.tick(currentTime);
if (!camera.transition.inTransition && !webgl.isContextLost) {
interactionHelper.tick(currentTime);
}
requestAnimationFrame(animate)
}
function identify(x: number, y: number): PickingId | undefined {
return pickPass.identify(x, y)
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
}
function commit(renderObjects?: readonly GraphicsRenderObject[]) {
scene.update(renderObjects, false)
function commit() {
commitScene();
resolveCameraReset();
}
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 resolveCameraReset() {
if (!cameraResetRequested) return;
const { center, radius } = scene.boundingSphere;
camera.focus(center, radius, radius, p.cameraResetDurationMs);
cameraResetRequested = false;
}
const sceneCommitTimeoutMs = 250;
function commitScene() {
if (!scene.needsCommit) return;
const allCommited = scene.commit(sceneCommitTimeoutMs);
if (debugHelper.isEnabled) debugHelper.update();
if (allCommited) reprCount.next(reprRenderObjects.size);
}
function add(repr: Representation.Any) {
registerAutoUpdate(repr);
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(oldRO)) { if (!newRO.has(o)) scene.remove(o) }
newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o) })
oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o) })
}
} else {
repr.renderObjects.forEach(o => scene.add(o))
}
reprRenderObjects.set(repr, newRO)
commit(repr.renderObjects)
scene.update(repr.renderObjects, false)
}
function remove(repr: Representation.Any) {
unregisterAutoUpdate(repr);
const renderObjects = reprRenderObjects.get(repr)
if (renderObjects) {
renderObjects.forEach(o => scene.remove(o))
reprRenderObjects.delete(repr)
scene.update(repr.renderObjects, false, true)
}
}
function registerAutoUpdate(repr: Representation.Any) {
if (reprUpdatedSubscriptions.has(repr)) return;
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
if (!repr.state.syncManually) add(repr);
}))
}
function unregisterAutoUpdate(repr: Representation.Any) {
const updatedSubscription = reprUpdatedSubscriptions.get(repr);
if (updatedSubscription) {
updatedSubscription.unsubscribe();
reprUpdatedSubscriptions.delete(repr);
}
}
handleResize()
@@ -270,24 +355,9 @@ namespace Canvas3D {
return {
webgl,
add: (repr: Representation.Any) => {
add(repr)
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
if (!repr.state.syncManually) add(repr)
}))
},
remove: (repr: Representation.Any) => {
const updatedSubscription = reprUpdatedSubscriptions.get(repr)
if (updatedSubscription) {
updatedSubscription.unsubscribe()
}
const renderObjects = reprRenderObjects.get(repr)
if (renderObjects) {
renderObjects.forEach(o => scene.remove(o))
reprRenderObjects.delete(repr)
commit()
}
},
add,
remove,
commit,
update: (repr, keepSphere) => {
if (repr) {
if (!reprRenderObjects.has(repr)) return;
@@ -297,6 +367,8 @@ namespace Canvas3D {
}
},
clear: () => {
reprUpdatedSubscriptions.forEach(v => v.unsubscribe())
reprUpdatedSubscriptions.clear()
reprRenderObjects.clear()
scene.clear()
debugHelper.clear()
@@ -312,15 +384,11 @@ namespace Canvas3D {
getLoci,
handleResize,
resetCamera: () => {
if (scene.isCommiting) {
cameraResetRequested = true
} else {
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
requestDraw(true);
}
requestCameraReset: () => {
cameraResetRequested = true;
},
camera,
boundingSphere: scene.boundingSphere,
downloadScreenshot: () => {
// TODO
},
@@ -342,7 +410,11 @@ namespace Canvas3D {
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
camera.setState({ fog: props.cameraFog })
}
if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
camera.setState({ clipFar: props.cameraClipFar })
}
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
if (props.multiSample) multiSample.setProps(props.multiSample)
@@ -359,7 +431,9 @@ namespace Canvas3D {
return {
cameraMode: camera.state.mode,
cameraFog: camera.state.fog,
cameraClipFar: camera.state.clipFar,
cameraResetDurationMs: p.cameraResetDurationMs,
transparentBackground: p.transparentBackground,
postprocessing: { ...postprocessing.props },
multiSample: { ...multiSample.props },
@@ -378,6 +452,8 @@ namespace Canvas3D {
return interactionHelper.events
},
dispose: () => {
contextRestoredSub.unsubscribe()
scene.clear()
debugHelper.clear()
input.dispose()

View File

@@ -208,8 +208,8 @@ namespace TrackballControls {
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 })
const radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
camera.setState({ radiusNear })
}
if (p.staticMoving) {
@@ -343,7 +343,7 @@ namespace TrackballControls {
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
if (dragFocusZoom) {
const dist = Vec3.distance(camera.state.position, camera.state.target);
camera.setState({ radius: dist / 5 })
camera.setState({ radiusNear: dist / 5 })
}
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
}

View File

@@ -82,7 +82,7 @@ export class BoundingSphereHelper {
})
this.scene.update(void 0, false)
this.scene.syncCommit()
this.scene.commit()
}
syncVisibility() {

View File

@@ -38,6 +38,7 @@ export class Canvas3dInteractionHelper {
private inside = false;
private buttons: ButtonsType = ButtonsType.create(0);
private button: ButtonsType.Flag = ButtonsType.create(0);
private modifiers: ModifiersKeys = ModifiersKeys.None;
private identify(isClick: boolean, t: number) {
@@ -50,7 +51,7 @@ export class Canvas3dInteractionHelper {
if (!this.id) return;
if (isClick) {
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, modifiers: this.modifiers });
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, button: this.button, modifiers: this.modifiers });
return;
}
@@ -61,7 +62,7 @@ export class Canvas3dInteractionHelper {
const loci = this.getLoci(this.id);
// only broadcast the latest hover
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
this.events.hover.next({ current: loci, buttons: this.buttons, modifiers: this.modifiers });
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
this.prevLoci = loci;
}
}
@@ -78,22 +79,24 @@ export class Canvas3dInteractionHelper {
this.inside = false;
if (this.prevLoci.loci !== EmptyLoci) {
this.prevLoci = Representation.Loci.Empty;
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
}
}
move(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
this.inside = true;
this.buttons = buttons;
this.button = button;
this.modifiers = modifiers;
this.cX = x;
this.cY = y;
}
select(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
select(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
this.cX = x;
this.cY = y;
this.buttons = buttons;
this.button = button;
this.modifiers = modifiers;
this.identify(true, 0);
}
@@ -101,7 +104,7 @@ export class Canvas3dInteractionHelper {
modify(modifiers: ModifiersKeys) {
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
this.modifiers = modifiers;
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
}
dispose() {
@@ -109,17 +112,17 @@ 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 }) => {
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
if (!inside) return;
this.move(x, y, buttons, modifiers);
this.move(x, y, buttons, button, modifiers);
});
input.leave.subscribe(() => {
this.leave();
});
input.click.subscribe(({x, y, buttons, modifiers }) => {
this.select(x, y, buttons, modifiers);
input.click.subscribe(({x, y, buttons, button, modifiers }) => {
this.select(x, y, buttons, button, modifiers);
});
input.modifiers.subscribe(modifiers => this.modify(modifiers));

View File

@@ -5,11 +5,11 @@
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
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 { createTexture, Texture } from '../../mol-gl/webgl/texture';
import { Texture } from '../../mol-gl/webgl/texture';
import { Camera } from '../camera';
export class DrawPass {
@@ -20,13 +20,13 @@ export class DrawPass {
private depthTarget: RenderTarget | null
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
const { gl, extensions } = webgl
const { gl, extensions, resources } = webgl
const width = gl.drawingBufferWidth
const height = gl.drawingBufferHeight
this.colorTarget = createRenderTarget(webgl, width, height)
this.colorTarget = webgl.createRenderTarget(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')
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest')
if (!this.packedDepth) {
this.depthTexture.define(width, height)
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
@@ -42,29 +42,33 @@ export class DrawPass {
}
}
render(toDrawingBuffer: boolean) {
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
if (toDrawingBuffer) {
webgl.unbindFramebuffer()
} else {
colorTarget.bind()
if (!this.packedDepth) {
// TODO unlcear why it is not enough to call `attachFramebuffer` in `Texture.reset`
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
}
}
renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
renderer.render(scene, camera, 'color', true)
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight())
renderer.render(scene, camera, 'color', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, camera, 'color', false)
renderer.render(debugHelper.scene, camera, 'color', false, transparentBackground)
}
// 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, camera, 'depth', true)
renderer.render(scene, camera, 'depth', true, transparentBackground)
if (debugHelper.isEnabled) {
debugHelper.syncVisibility()
renderer.render(debugHelper.scene, camera, 'depth', false)
renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
}
}
}

View File

@@ -17,6 +17,7 @@ import { Camera } from '../camera';
import { Viewport } from '../camera/util';
export const ImageParams = {
transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
}
@@ -26,6 +27,7 @@ export class ImagePass {
private _width = 1024
private _height = 768
private _camera = new Camera()
private _transparentBackground = false
private _colorTarget: RenderTarget
get colorTarget() { return this._colorTarget }
@@ -40,6 +42,8 @@ export class ImagePass {
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
const p = { ...PD.getDefaultValues(ImageParams), ...props }
this._transparentBackground = p.transparentBackground
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)
@@ -48,6 +52,8 @@ export class ImagePass {
}
setSize(width: number, height: number) {
if (width === this._width && height === this._height) return
this._width = width
this._height = height
@@ -57,6 +63,7 @@ export class ImagePass {
}
setProps(props: Partial<ImageProps> = {}) {
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
if (props.multiSample) this.multiSample.setProps(props.multiSample)
}
@@ -69,10 +76,10 @@ export class ImagePass {
this.renderer.setViewport(0, 0, this._width, this._height);
if (this.multiSample.enabled) {
this.multiSample.render(false)
this.multiSample.render(false, this._transparentBackground)
this._colorTarget = this.multiSample.colorTarget
} else {
this.drawPass.render(false)
this.drawPass.render(false, this._transparentBackground)
if (this.postprocessing.enabled) {
this.postprocessing.render(false)
this._colorTarget = this.postprocessing.target

View File

@@ -14,7 +14,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { RenderTarget, createRenderTarget } from '../../mol-gl/webgl/render-target';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Camera } from '../../mol-canvas3d/camera';
import { PostprocessingPass } from './postprocessing';
import { DrawPass } from './draw';
@@ -35,7 +35,7 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
const values: Values<typeof ComposeSchema> = {
...QuadValues,
tColor: ValueCell.create(colorTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
uWeight: ValueCell.create(1.0),
}
@@ -66,9 +66,9 @@ 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.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props }
}
@@ -105,15 +105,15 @@ export class MultiSamplePass {
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
}
render(toDrawingBuffer: boolean) {
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
if (this.props.mode === 'temporal') {
this.renderTemporalMultiSample(toDrawingBuffer)
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
} else {
this.renderMultiSample(toDrawingBuffer)
this.renderMultiSample(toDrawingBuffer, transparentBackground)
}
}
private renderMultiSample(toDrawingBuffer: boolean) {
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
const { gl, state } = webgl
@@ -131,7 +131,8 @@ export class MultiSamplePass {
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
compose.update()
const { width, height } = drawPass.colorTarget
const width = drawPass.colorTarget.getWidth()
const height = drawPass.colorTarget.getHeight()
// render the scene multiple times, each slightly jitter offset
// from the last and accumulate the results.
@@ -148,7 +149,7 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight)
// render scene and optionally postprocess
drawPass.render(false)
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
// compose rendered scene with compose target
@@ -184,7 +185,7 @@ export class MultiSamplePass {
camera.update()
}
private renderTemporalMultiSample(toDrawingBuffer: boolean) {
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
const { gl, state } = webgl
@@ -204,7 +205,7 @@ export class MultiSamplePass {
const i = this.sampleIndex
if (i === 0) {
drawPass.render(false)
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
ValueCell.update(compose.values.uWeight, 1.0)
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
@@ -222,7 +223,8 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight)
compose.update()
const { width, height } = drawPass.colorTarget
const width = drawPass.colorTarget.getWidth()
const height = drawPass.colorTarget.getHeight()
// render the scene multiple times, each slightly jitter offset
// from the last and accumulate the results.
@@ -233,7 +235,7 @@ export class MultiSamplePass {
camera.update()
// render scene and optionally postprocess
drawPass.render(false)
drawPass.render(false, transparentBackground)
if (postprocessing.enabled) postprocessing.render(false)
// compose rendered scene with compose target

View File

@@ -5,7 +5,7 @@
*/
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import Renderer from '../../mol-gl/renderer';
import Scene from '../../mol-gl/scene';
import { PickingId } from '../../mol-geo/geometry/picking';
@@ -36,9 +36,9 @@ export class PickPass {
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.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
this.setupBuffers()
}
@@ -68,11 +68,11 @@ export class PickPass {
const { renderer, scene, camera } = this
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
this.objectPickTarget.bind();
renderer.render(scene, camera, 'pickObject', true);
renderer.render(scene, camera, 'pickObject', true, false);
this.instancePickTarget.bind();
renderer.render(scene, camera, 'pickInstance', true);
renderer.render(scene, camera, 'pickInstance', true, false);
this.groupPickTarget.bind();
renderer.render(scene, camera, 'pickGroup', true);
renderer.render(scene, camera, 'pickGroup', true, false);
this.pickDirty = false
}
@@ -97,6 +97,8 @@ export class PickPass {
identify(x: number, y: number): PickingId | undefined {
const { webgl, pickScale } = this
if (webgl.isContextLost) return
const { gl } = webgl
if (this.pickDirty) {
this.render()

View File

@@ -14,7 +14,7 @@ import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { DrawPass } from './draw';
import { Camera } from '../../mol-canvas3d/camera';
@@ -27,7 +27,6 @@ const PostprocessingSchema = {
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
dUseFog: DefineSpec('boolean'),
dOrthographic: DefineSpec('number'),
uNear: UniformSpec('f'),
uFar: UniformSpec('f'),
@@ -56,8 +55,6 @@ export const PostprocessingParams = {
outlineEnable: PD.Boolean(false),
outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
outlineThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
useFog: PD.Boolean(true),
}
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
@@ -69,9 +66,8 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
...QuadValues,
tColor: ValueCell.create(colorTexture),
tDepth: ValueCell.create(depthTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
dUseFog: ValueCell.create(p.useFog),
dOrthographic: ValueCell.create(0),
uNear: ValueCell.create(1),
uFar: ValueCell.create(10000),
@@ -105,7 +101,7 @@ export class PostprocessingPass {
constructor(private webgl: WebGLContext, private camera: Camera, drawPass: DrawPass, props: Partial<PostprocessingProps>) {
const { gl } = webgl
this.target = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
this.target = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props }
const { colorTarget, depthTexture, packedDepth } = drawPass
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props)
@@ -151,11 +147,6 @@ export class PostprocessingPass {
ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
}
if (props.useFog !== undefined) {
this.props.useFog = props.useFog
ValueCell.update(this.renderable.values.dUseFog, props.useFog)
}
this.renderable.update()
}

View File

@@ -37,7 +37,7 @@ namespace Column {
export type Coordinate = { '@type': 'coord', T: number } & Base<'float'>
export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space, baseType: Int | Float } & Base<'tensor'>
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<'str' | 'int'>
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<T extends string ? 'str' : 'int'>
export type List<T extends number|string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
export const str: Str = { '@type': 'str', T: '', valueType: 'str' };
@@ -92,7 +92,13 @@ namespace Column {
return !!v && !!(v as Column<any>).schema && !!(v as Column<any>).value;
}
export const enum ValueKind { Present = 0, NotPresent = 1, Unknown = 2 }
export const enum ValueKind {
Present = 0,
/** Expressed in CIF as `.` */
NotPresent = 1,
/** Expressed in CIF as `?` */
Unknown = 2
}
export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> {
return constColumn(schema['T'], rowCount, schema, ValueKind.NotPresent);
@@ -131,6 +137,14 @@ namespace Column {
return arrayColumn({ array, schema: Schema.str });
}
export function ofStringAliasArray<T extends string>(array: ArrayLike<T>) {
return arrayColumn<Schema.Aliased<T>>({ array, schema: Schema.Aliased(Schema.str) });
}
export function ofStringListArray<T extends string>(array: ArrayLike<T[]>, separator = ',') {
return arrayColumn<Schema.List<T>>({ array, schema: Schema.List<T>(separator, x => x as T) });
}
export function ofIntTokens(tokens: Tokens) {
const { count, data, indices } = tokens
return lambdaColumn({
@@ -290,14 +304,14 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
return ret;
}
: isTyped
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
: params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
return ret;
},
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
: params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
return ret;
},
areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB]
}
}
@@ -311,7 +325,8 @@ function windowColumn<T>(column: Column<T>, start: number, end: number): Column<
function windowTyped<T>(c: Column<T>, start: number, end: number): Column<T> {
const array = ColumnHelpers.typedArrayWindow(c.__array, { start, end });
return arrayColumn({ array, schema: c.schema, valueKind: c.valueKind }) as any;
const vk = c.valueKind;
return arrayColumn({ array, schema: c.schema, valueKind: row => vk(start + row) }) as any;
}
function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> {
@@ -353,7 +368,8 @@ function arrayView<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {
const array = c.__array!;
const ret = new (array as any).constructor(map.length);
for (let i = 0, _i = map.length; i < _i; i++) ret[i] = array[map[i]];
return arrayColumn({ array: ret, schema: c.schema, valueKind: c.valueKind });
const vk = c.valueKind;
return arrayColumn({ array: ret, schema: c.schema, valueKind: row => vk(map[row]) });
}
function viewFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {

View File

@@ -21,7 +21,8 @@ namespace Table {
export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & { [C in keyof S]?: Column<S[C]['T']> }
export type PartialColumns<S extends Schema> = { [C in keyof S]?: Column<S[C]['T']> }
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & PartialColumns<S>
export function is(t: any): t is Table<any> {
return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema;
@@ -47,6 +48,19 @@ namespace Table {
return { _rowCount, _columns, _schema: schema, ...(columns as any) };
}
export function ofPartialColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, partialColumns: PartialColumns<S>, rowCount: number): R {
const ret = Object.create(null);
const columns = Object.keys(schema);
ret._rowCount = rowCount;
ret._columns = columns;
ret._schema = schema;
for (const k of columns) {
if (k in partialColumns) ret[k] = partialColumns[k]
else ret[k] = Column.Undefined(rowCount, schema[k])
}
return ret;
}
export function ofUndefinedColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rowCount: number): R {
const ret = Object.create(null);
const columns = Object.keys(schema);

View File

@@ -90,14 +90,14 @@ class LinkedListImpl<T> implements LinkedList<T> {
if (node.previous !== null) {
node.previous.next = node.next;
}
else if (/*first == item*/ node.previous === null) {
else if (/* first == item*/ node.previous === null) {
this.first = node.next;
}
if (node.next !== null) {
node.next.previous = node.previous;
}
else if (/*last == item*/ node.next === null) {
else if (/* last == item*/ node.next === null) {
this.last = node.previous;
}

View File

@@ -6,6 +6,7 @@
import OrderedSet from '../ordered-set'
import Interval from '../interval'
import SortedArray from '../sorted-array';
describe('ordered set', () => {
function ordSetToArray(set: OrderedSet) {
@@ -163,6 +164,15 @@ describe('ordered set', () => {
testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
testEq('idxIntersect 1', OrderedSet.indexedIntersect(
OrderedSet.ofSortedArray([1, 2, 4]),
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6]),
SortedArray.ofSortedArray([2, 4, 5, 8])), [0, 2]);
testEq('idxIntersect 2', OrderedSet.indexedIntersect(
OrderedSet.ofSortedArray([0, 1]),
SortedArray.ofSortedArray([1, 2]),
SortedArray.ofSortedArray([1, 2])), [0, 1]);
testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []);
testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []);
testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);

View File

@@ -63,6 +63,16 @@ describe('sortedArray', () => {
compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
})
it('indicesOf 2', () => {
compareArrays(
SortedArray.indicesOf(
SortedArray.ofSortedArray([0, 1, 2, 3, 4, 8, 9, 10]),
SortedArray.ofSortedArray([1, 3, 4, 9, 10])
),
[1, 3, 4, 6, 7]
);
})
test('intersectionSize', SortedArray.intersectionSize(a1234, a2468), 2);
it('union1', () => {

View File

@@ -286,4 +286,69 @@ export function forEach(set: OrderedSetImpl, f: (value: number, i: number, ctx:
}
}
return ctx;
}
export function forEachSegment(set: OrderedSetImpl, segment: (v: number) => number, f: (value: number, segIndex: number, ctx: any) => void, ctx: any) {
if (I.is(set)) {
let sI = 0;
for (let i = I.min(set), _i = I.max(set); i <= _i; i++) {
const s = segment(i);
let endI = i + 1;
while (endI < _i && segment(endI) === s) endI++;
i = endI - 1;
f(s, sI, ctx);
sI++;
}
} else {
let sI = 0;
for (let i = 0, _i = set.length; i < _i; i++) {
const s = segment(set[i]);
let endI = i + 1;
while (endI < _i && segment(set[endI]) === s) endI++;
i = endI - 1;
f(s, sI, ctx);
sI++;
}
}
return ctx;
}
export function indexedIntersect(idxA: OrderedSetImpl, a: S, b: S): OrderedSetImpl {
if (a === b) return idxA;
const lenI = size(idxA), lenA = a.length, lenB = b.length;
if (lenI === 0 || lenA === 0 || lenB === 0) return Empty;
const startJ = S.findPredecessorIndex(b, a[min(idxA)]);
const endJ = S.findPredecessorIndex(b, a[max(idxA)] + 1);
let commonCount = 0;
let offset = 0;
let O = 0;
let j = startJ;
while (O < lenI && j < endJ) {
const x = a[getAt(idxA, O)], y = b[j];
if (x < y) { O++; }
else if (x > y) { j++; }
else { commonCount++; O++; j++; }
}
// no common elements
if (commonCount === 0) return Empty;
// A === B
if (commonCount === lenA && commonCount === lenB) return idxA;
const indices = new Int32Array(commonCount);
offset = 0;
O = 0;
j = startJ;
while (O < lenI && j < endJ) {
const x = a[getAt(idxA, O)], y = b[j];
if (x < y) { O++; }
else if (x > y) { j++; }
else { indices[offset++] = j; O++; j++; }
}
return ofSortedArray(indices);
}

View File

@@ -39,6 +39,7 @@ 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;
export const indexedIntersect: <T extends number = number, S extends number = number>(idxA: OrderedSet<T>, a: SortedArray<S>, b: SortedArray<S>) => OrderedSet<T> = Base.indexedIntersect 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;
@@ -56,6 +57,10 @@ namespace OrderedSet {
return Base.forEach(set as any, f as any, ctx);
}
export function forEachSegment<T extends number, S extends number, Ctx>(set: OrderedSet<T>, segment: (v: T) => S, f: (v: S, sI: number, ctx: Ctx) => void, ctx?: Ctx): Ctx {
return Base.forEachSegment(set as any, segment as any, f as any, ctx);
}
export function isInterval<T extends number = number>(set: OrderedSet<T>): set is Interval<T> {
return Interval.is(set);
}

View File

@@ -7,12 +7,12 @@
import { Interval, OrderedSet, SortedArray } from '../../int';
import { IntervalIterator } from '../interval-iterator';
describe('interval', () => {
describe('interval', () => {
function testIterator(name: string, interval: Interval, set: OrderedSet, expectedValues: { index: number[], start: number[], end: number[]}) {
it(`iterator, ${name}`, () => {
const intervalIt = new IntervalIterator(interval, set)
const { index, start, end } = expectedValues
let i = 0
while (intervalIt.hasNext) {
const segment = intervalIt.move()

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>
*/
@@ -27,7 +27,7 @@ function nextIndex(n: number) {
return ripple | ones
};
export class CombinationIterator<T> implements Iterator<T[]> {
export class CombinationIterator<T> implements Iterator<ReadonlyArray<T>> {
private value: T[]
private index: number
private maxIndex: number

View File

@@ -9,8 +9,6 @@ import { ValueCell } from '../../mol-util';
import { BaseValues } from '../../mol-gl/renderable/schema';
import { LocationIterator } from '../util/location-iterator';
import { ParamDefinition as PD } from '../../mol-util/param-definition'
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/names';
@@ -38,10 +36,6 @@ export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [Visua
export namespace BaseGeometry {
export const Params = {
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
useFog: PD.Boolean(true),
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
}
export type Params = typeof Params
@@ -62,24 +56,13 @@ export namespace BaseGeometry {
return {
alpha: ValueCell.create(props.alpha),
uAlpha: ValueCell.create(props.alpha),
uHighlightColor: ValueCell.create(Color.toArrayNormalized(props.highlightColor, Vec3.zero(), 0)),
uSelectColor: ValueCell.create(Color.toArrayNormalized(props.selectColor, Vec3.zero(), 0)),
dUseFog: ValueCell.create(props.useFog),
uGroupCount: ValueCell.create(counts.groupCount),
drawCount: ValueCell.create(counts.drawCount),
}
}
export function updateValues(values: BaseValues, props: PD.Values<Params>) {
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
}
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
}
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
}
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {

View File

@@ -18,7 +18,7 @@ import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
import { GeometryUtils } from '../geometry';
import { transformPositionArray } from '../../../mol-geo/util';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Theme } from '../../../mol-theme/theme';
import { RenderableState } from '../../../mol-gl/renderable';
import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
@@ -26,12 +26,14 @@ import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
const VolumeBox = Box()
const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
export interface DirectVolume {
readonly kind: 'direct-volume',
readonly gridTexture: ValueCell<Texture>,
readonly gridTextureDim: ValueCell<Vec3>,
readonly gridDimension: ValueCell<Vec3>,
@@ -41,32 +43,66 @@ export interface DirectVolume {
readonly transform: ValueCell<Mat4>
/** Bounding sphere of the volume */
boundingSphere?: Sphere3D
boundingSphere: Sphere3D
}
export namespace DirectVolume {
export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume): DirectVolume {
const { width, height, depth } = texture
if (directVolume) {
ValueCell.update(directVolume.gridDimension, gridDimension)
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
ValueCell.update(directVolume.bboxMin, bbox.min)
ValueCell.update(directVolume.bboxMax, bbox.max)
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
ValueCell.update(directVolume.transform, transform)
return directVolume
} else {
return {
kind: 'direct-volume',
gridDimension: ValueCell.create(gridDimension),
gridTexture: ValueCell.create(texture),
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
bboxMin: ValueCell.create(bbox.min),
bboxMax: ValueCell.create(bbox.max),
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
transform: ValueCell.create(transform),
}
return directVolume ?
update(bbox, gridDimension, transform, texture, directVolume) :
fromData(bbox, gridDimension, transform, texture)
}
function hashCode(directVolume: DirectVolume) {
return hashFnv32a([
directVolume.bboxSize.ref.version, directVolume.gridDimension.ref.version,
directVolume.gridTexture.ref.version, directVolume.transform.ref.version,
])
}
function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture): DirectVolume {
const boundingSphere = Sphere3D()
let currentHash = -1
const width = texture.getWidth()
const height = texture.getHeight()
const depth = texture.getDepth()
const directVolume = {
kind: 'direct-volume' as const,
gridDimension: ValueCell.create(gridDimension),
gridTexture: ValueCell.create(texture),
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
bboxMin: ValueCell.create(bbox.min),
bboxMax: ValueCell.create(bbox.max),
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
transform: ValueCell.create(transform),
get boundingSphere() {
const newHash = hashCode(directVolume)
if (newHash !== currentHash) {
const b = getBoundingSphere(directVolume.gridDimension.ref.value, directVolume.transform.ref.value)
Sphere3D.copy(boundingSphere, b)
currentHash = newHash
}
return boundingSphere
},
}
return directVolume
}
function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume: DirectVolume): DirectVolume {
const width = texture.getWidth()
const height = texture.getHeight()
const depth = texture.getDepth()
ValueCell.update(directVolume.gridDimension, gridDimension)
ValueCell.update(directVolume.gridTexture, texture)
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
ValueCell.update(directVolume.bboxMin, bbox.min)
ValueCell.update(directVolume.bboxMax, bbox.max)
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
ValueCell.update(directVolume.transform, transform)
return directVolume
}
export function createEmpty(directVolume?: DirectVolume): DirectVolume {
@@ -108,7 +144,8 @@ export namespace DirectVolume {
const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount }
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(gridDimension.ref.value, gridTransform.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
const transferTex = createTransferFunctionTexture(controlPoints, props.list)
@@ -138,7 +175,7 @@ export namespace DirectVolume {
dRenderMode: ValueCell.create(props.renderMode),
tTransferTex: transferTex,
dGridTexType: ValueCell.create(gridTexture.ref.value.depth > 0 ? '3d' : '2d'),
dGridTexType: ValueCell.create(gridTexture.ref.value.getDepth() > 0 ? '3d' : '2d'),
uGridTexDim: gridTextureDim,
tGridTex: gridTexture,
}
@@ -153,7 +190,6 @@ export namespace DirectVolume {
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
ValueCell.updateIfChanged(values.uIsoValue, props.isoValueNorm)
ValueCell.updateIfChanged(values.uAlpha, props.alpha)
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
@@ -161,7 +197,9 @@ export namespace DirectVolume {
}
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value)
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}
@@ -188,11 +226,12 @@ const mTmp = Mat4.identity()
const mTmp2 = Mat4.identity()
const vHalfUnit = Vec3.create(0.5, 0.5, 0.5)
const tmpVertices = new Float32Array(VolumeBox.vertices.length)
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4, transform: Float32Array, transformCount: number) {
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4) {
tmpVertices.set(VolumeBox.vertices)
Mat4.fromTranslation(mTmp, vHalfUnit)
Mat4.mul(mTmp, Mat4.fromScaling(mTmp2, gridDimension), mTmp)
Mat4.mul(mTmp, gridTransform, mTmp)
transformPositionArray(mTmp, tmpVertices, 0, tmpVertices.length / 3)
return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
return calculateInvariantBoundingSphere(tmpVertices, tmpVertices.length / 3, 1)
// return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -19,32 +19,30 @@ import { Spheres } from './spheres/spheres';
import { arrayMax } from '../../mol-util/array';
import { TransformData } from './transform-data';
import { Theme } from '../../mol-theme/theme';
import { RenderObjectValuesType } from '../../mol-gl/render-object';
import { ValueOf } from '../../mol-util/type-helpers';
import { RenderObjectValues } from '../../mol-gl/render-object';
import { TextureMesh } from './texture-mesh/texture-mesh';
export type GeometryKindType = {
'mesh': Mesh,
'points': Points,
'spheres': Spheres,
'text': Text,
'lines': Lines,
'direct-volume': DirectVolume,
'texture-mesh': TextureMesh,
}
export type GeometryKindParams = {
'mesh': Mesh.Params,
'points': Points.Params,
'spheres': Spheres.Params,
'text': Text.Params,
'lines': Lines.Params,
'direct-volume': DirectVolume.Params,
'texture-mesh': TextureMesh.Params,
}
export type GeometryKind = keyof GeometryKindType
export type Geometry = ValueOf<GeometryKindType>
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryKindParams[G['kind']], V = RenderObjectValuesType[G['kind']]> {
export type Geometry<T extends GeometryKind = GeometryKind> =
T extends 'mesh' ? Mesh :
T extends 'points' ? Points :
T extends 'spheres' ? Spheres :
T extends 'text' ? Text :
T extends 'lines' ? Lines :
T extends 'direct-volume' ? DirectVolume :
T extends 'texture-mesh' ? TextureMesh : never
type GeometryParams<T extends GeometryKind> =
T extends 'mesh' ? Mesh.Params :
T extends 'points' ? Points.Params :
T extends 'spheres' ? Spheres.Params :
T extends 'text' ? Text.Params :
T extends 'lines' ? Lines.Params :
T extends 'direct-volume' ? DirectVolume.Params :
T extends 'texture-mesh' ? TextureMesh.Params : never
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryParams<G['kind']>, V = RenderObjectValues<G['kind']>> {
Params: P
createEmpty(geometry?: G): G
createValues(geometry: G, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<P>): V
@@ -56,7 +54,7 @@ export interface GeometryUtils<G extends Geometry, P extends PD.Params = Geometr
}
export namespace Geometry {
export type Params<G extends Geometry> = GeometryKindParams[G['kind']]
export type Params<G extends Geometry> = GeometryParams<G['kind']>
export function getDrawCount(geometry: Geometry): number {
switch (geometry.kind) {
@@ -66,7 +64,7 @@ export namespace Geometry {
case 'text': return geometry.charCount * 2 * 3
case 'lines': return geometry.lineCount * 2 * 3
case 'direct-volume': return 12 * 3
case 'texture-mesh': return geometry.vertexCount.ref.value
case 'texture-mesh': return geometry.vertexCount
}
}
@@ -81,7 +79,7 @@ export namespace Geometry {
case 'direct-volume':
return 1
case 'texture-mesh':
return geometry.groupCount.ref.value
return geometry.groupCount
}
}
@@ -96,7 +94,6 @@ export namespace Geometry {
case 'direct-volume': return DirectVolume.Utils as any
case 'texture-mesh': return TextureMesh.Utils as any
}
throw new Error('unknown geometry kind')
}
export function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {

View File

@@ -1,10 +1,9 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util/value-cell'
import { ChunkedArray } from '../../../mol-data/util';
import { Lines } from './lines';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
@@ -12,12 +11,15 @@ import { Cage } from '../../../mol-geo/primitive/cage';
export interface LinesBuilder {
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void
addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, group: number): void
addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, group: number): void
addCage(t: Mat4, cage: Cage, group: number): void
getLines(): Lines
}
const tmpVecA = Vec3.zero()
const tmpVecB = Vec3.zero()
const tmpVecA = Vec3()
const tmpVecB = Vec3()
const tmpDir = Vec3()
export namespace LinesBuilder {
export function create(initialCount = 2048, chunkSize = 1024, lines?: Lines): LinesBuilder {
@@ -42,8 +44,29 @@ export namespace LinesBuilder {
ChunkedArray.add3(indices, offset + 1, offset + 3, offset + 2);
}
const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, group: number) => {
const d = Vec3.distance(start, end)
const s = Math.floor(segmentCount / 2)
const step = 1 / segmentCount
Vec3.sub(tmpDir, end, start)
for (let j = 0; j < s; ++j) {
const f = step * (j * 2 + 1)
Vec3.setMagnitude(tmpDir, tmpDir, d * f)
Vec3.add(tmpVecA, start, tmpDir)
Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2))
Vec3.add(tmpVecB, start, tmpDir)
add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], group)
}
}
return {
add,
addFixedCountDashes,
addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, group: number) => {
const d = Vec3.distance(start, end)
addFixedCountDashes(start, end, d / segmentLength, group)
},
addCage: (t: Mat4, cage: Cage, group: number) => {
const { vertices, edges } = cage
for (let i = 0, il = edges.length; i < il; i += 2) {
@@ -60,15 +83,7 @@ export namespace LinesBuilder {
const gb = ChunkedArray.compact(groups, true) as Float32Array
const sb = ChunkedArray.compact(starts, true) as Float32Array
const eb = ChunkedArray.compact(ends, true) as Float32Array
return {
kind: 'lines',
lineCount: indices.elementCount / 2,
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
}
return Lines.create(mb, ib, gb, sb, eb, indices.elementCount / 2)
}
}
}

View File

@@ -1,12 +1,14 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util'
import { Mat4 } from '../../../mol-math/linear-algebra'
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
GroupMapping,
createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -17,19 +19,22 @@ import { LinesValues } from '../../../mol-gl/renderable/lines';
import { Mesh } from '../mesh/mesh';
import { LinesBuilder } from './lines-builder';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Theme } from '../../../mol-theme/theme';
import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
/** Wide line */
export interface Lines {
readonly kind: 'lines',
/** Number of lines */
lineCount: number,
/** Mapping buffer as array of xy values wrapped in a value cell */
readonly mappingBuffer: ValueCell<Float32Array>,
/** Index buffer as array of vertex index triplets wrapped in a value cell */
@@ -40,24 +45,27 @@ export interface Lines {
readonly startBuffer: ValueCell<Float32Array>,
/** Line end buffer as array of xyz values wrapped in a value cell */
readonly endBuffer: ValueCell<Float32Array>,
/** Bounding sphere of the lines */
readonly boundingSphere: Sphere3D
/** Maps group ids to line indices */
readonly groupMapping: GroupMapping
}
export namespace Lines {
export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines?: Lines): Lines {
return lines ?
update(mappings, indices, groups, starts, ends, lineCount, lines) :
fromArrays(mappings, indices, groups, starts, ends, lineCount)
}
export function createEmpty(lines?: Lines): Lines {
const mb = lines ? lines.mappingBuffer.ref.value : new Float32Array(0)
const ib = lines ? lines.indexBuffer.ref.value : new Uint32Array(0)
const gb = lines ? lines.groupBuffer.ref.value : new Float32Array(0)
const sb = lines ? lines.startBuffer.ref.value : new Float32Array(0)
const eb = lines ? lines.endBuffer.ref.value : new Float32Array(0)
return {
kind: 'lines',
lineCount: 0,
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
}
return create(mb, ib, gb, sb, eb, 0, lines)
}
export function fromMesh(mesh: Mesh, lines?: Lines) {
@@ -81,16 +89,67 @@ export namespace Lines {
return builder.getLines();
}
export function transformImmediate(line: Lines, t: Mat4) {
transformRangeImmediate(line, t, 0, line.lineCount)
function hashCode(lines: Lines) {
return hashFnv32a([
lines.lineCount, lines.mappingBuffer.ref.version, lines.indexBuffer.ref.version,
lines.groupBuffer.ref.version, lines.startBuffer.ref.version, lines.startBuffer.ref.version
])
}
export function transformRangeImmediate(lines: Lines, t: Mat4, offset: number, count: number) {
function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number): Lines {
const boundingSphere = Sphere3D()
let groupMapping: GroupMapping
let currentHash = -1
let currentGroup = -1
const lines = {
kind: 'lines' as const,
lineCount,
mappingBuffer: ValueCell.create(mappings),
indexBuffer: ValueCell.create(indices),
groupBuffer: ValueCell.create(groups),
startBuffer: ValueCell.create(starts),
endBuffer: ValueCell.create(ends),
get boundingSphere() {
const newHash = hashCode(lines)
if (newHash !== currentHash) {
const s = calculateInvariantBoundingSphere(lines.startBuffer.ref.value, lines.lineCount * 4, 4)
const e = calculateInvariantBoundingSphere(lines.endBuffer.ref.value, lines.lineCount * 4, 4)
Sphere3D.expandBySphere(boundingSphere, s, e)
currentHash = newHash
}
return boundingSphere
},
get groupMapping() {
if (lines.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(lines.groupBuffer.ref.value, lines.lineCount, 4)
currentGroup = lines.groupBuffer.ref.version
}
return groupMapping
}
}
return lines
}
function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines: Lines) {
lines.lineCount = lineCount
ValueCell.update(lines.mappingBuffer, mappings)
ValueCell.update(lines.indexBuffer, indices)
ValueCell.update(lines.groupBuffer, groups)
ValueCell.update(lines.startBuffer, starts)
ValueCell.update(lines.endBuffer, ends)
return lines
}
export function transform(lines: Lines, t: Mat4) {
const start = lines.startBuffer.ref.value
transformPositionArray(t, start, offset, count * 4)
transformPositionArray(t, start, 0, lines.lineCount * 4)
ValueCell.update(lines.startBuffer, start);
const end = lines.endBuffer.ref.value
transformPositionArray(t, end, offset, count * 4)
transformPositionArray(t, end, 0, lines.lineCount * 4)
ValueCell.update(lines.endBuffer, end);
}
@@ -124,8 +183,8 @@ export namespace Lines {
const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount }
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(lines.startBuffer.ref.value, lines.endBuffer.ref.value, lines.lineCount,
transform.aTransform.ref.value, transform.instanceCount.ref.value)
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
return {
aMapping: lines.mappingBuffer,
@@ -163,10 +222,9 @@ export namespace Lines {
}
function updateBoundingSphere(values: LinesValues, lines: Lines) {
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(
values.aStart.ref.value, values.aEnd.ref.value, lines.lineCount,
values.aTransform.ref.value, values.instanceCount.ref.value
)
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}
@@ -174,13 +232,4 @@ export namespace Lines {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere)
}
}
}
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
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

@@ -0,0 +1,34 @@
/**
* 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 { Axes3D } from '../../../../mol-math/geometry';
import { createCage } from '../../../primitive/cage';
const tmpVec = Vec3()
const tmpMatrix = Mat4.identity()
const tmpVertices = new Float32Array(6 * 3)
const tmpEdges = new Uint8Array([0, 1, 2, 3, 4, 5])
export function addAxes(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
const { origin, dirA, dirB, dirC } = axes
Vec3.add(tmpVec, origin, dirA)
Vec3.toArray(Vec3.add(tmpVec, origin, dirA), tmpVertices, 0)
Vec3.toArray(Vec3.sub(tmpVec, origin, dirA), tmpVertices, 3)
Vec3.toArray(Vec3.add(tmpVec, origin, dirB), tmpVertices, 6)
Vec3.toArray(Vec3.sub(tmpVec, origin, dirB), tmpVertices, 9)
Vec3.toArray(Vec3.add(tmpVec, origin, dirC), tmpVertices, 12)
Vec3.toArray(Vec3.sub(tmpVec, origin, dirC), tmpVertices, 15)
const cage = createCage(tmpVertices, tmpEdges)
const volume = Axes3D.volume(axes)
const radius = (Math.cbrt(volume) / 300) * radiusScale
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
}

View File

@@ -1,15 +1,16 @@
/**
* 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 { Box3D } from '../../../../mol-math/geometry';
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
import { Box3D, Axes3D } from '../../../../mol-math/geometry';
import { MeshBuilder } from '../mesh-builder';
import { CylinderProps } from '../../../primitive/cylinder';
import { addCylinder } from './cylinder';
import { addSphere } from './sphere';
import { createCage } from '../../../primitive/cage';
const tmpStart = Vec3.zero()
const tmpEnd = Vec3.zero()
@@ -62,4 +63,49 @@ export function addBoundingBox(state: MeshBuilder.State, box: Box3D, radius: num
Vec3.set(tmpEnd, min[0], max[1], max[2])
addSphere(state, tmpEnd, radius, detail)
addCylinder(state, tmpStart, tmpEnd, 1, cylinderProps)
}
//
const tmpBoxVecCorner = Vec3()
const tmpBoxVecA = Vec3()
const tmpBoxVecB = Vec3()
const tmpBoxVecC = Vec3()
const tmpMatrix = Mat4.identity()
const tmpVertices = new Float32Array(8 * 3)
const tmpEdges = new Uint8Array([
0, 1, 0, 3, 0, 6, 1, 2, 1, 7, 2, 3,
2, 4, 3, 5, 4, 5, 4, 7, 5, 6, 6, 7
])
export function addOrientedBox(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
const { origin, dirA, dirB, dirC } = axes
const negDirA = Vec3.negate(tmpBoxVecA, dirA)
const negDirB = Vec3.negate(tmpBoxVecB, dirB)
const negDirC = Vec3.negate(tmpBoxVecC, dirC)
let offset = 0
const addCornerHelper = function (v1: Vec3, v2: Vec3, v3: Vec3) {
Vec3.copy(tmpBoxVecCorner, origin)
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v1)
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v2)
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v3)
Vec3.toArray(tmpBoxVecCorner, tmpVertices, offset)
offset += 3
}
addCornerHelper(dirA, dirB, dirC)
addCornerHelper(dirA, dirB, negDirC)
addCornerHelper(dirA, negDirB, negDirC)
addCornerHelper(dirA, negDirB, dirC)
addCornerHelper(negDirA, negDirB, negDirC)
addCornerHelper(negDirA, negDirB, dirC)
addCornerHelper(negDirA, dirB, dirC)
addCornerHelper(negDirA, dirB, negDirC)
const cage = createCage(tmpVertices, tmpEdges)
const volume = Axes3D.volume(axes)
const radius = (Math.cbrt(volume) / 300) * radiusScale
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
// TODO
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
// dst[3 * j] += src[3 * i];
// dst[3 * j + 1] += src[3 * i + 1];
// dst[3 * j + 2] += src[3 * i + 2];
// }
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
// const triCount = surface.triangleIndices.length,
// src = surface.vertices;
// const triangleIndices = surface.triangleIndices;
// for (let i = 0; i < triCount; i += 3) {
// const a = triangleIndices[i],
// b = triangleIndices[i + 1],
// c = triangleIndices[i + 2];
// addVertex(src, b, vs, a);
// addVertex(src, c, vs, a);
// addVertex(src, a, vs, b);
// addVertex(src, c, vs, b);
// addVertex(src, a, vs, c);
// addVertex(src, b, vs, c);
// }
// const vw = 2 * vertexWeight;
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
// const n = vertexCounts[i] + vw;
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
// }
// }
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
// await ctx.updateProgress('Smoothing surface...', true);
// const vertexCounts = new Int32Array(surface.vertexCount),
// triCount = surface.triangleIndices.length;
// const tris = surface.triangleIndices;
// for (let i = 0; i < triCount; i++) {
// // in a triangle 2 edges touch each vertex, hence the constant.
// vertexCounts[tris[i]] += 2;
// }
// let vs = new Float32Array(surface.vertices.length);
// let started = Utils.PerformanceMonitor.currentTime();
// await ctx.updateProgress('Smoothing surface...', true);
// for (let i = 0; i < iterCount; i++) {
// if (i > 0) {
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
// }
// surface.normals = void 0;
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
// const t = surface.vertices;
// surface.vertices = <any>vs;
// vs = <any>t;
// const time = Utils.PerformanceMonitor.currentTime();
// if (time - started > Computation.UpdateProgressDelta) {
// started = time;
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
// }
// }
// return surface;
// }
// /*
// * Smooths the vertices by averaging the neighborhood.
// *
// * Resets normals. Might replace vertex array.
// */
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
// if (iterCount < 1) iterCount = 0;
// if (iterCount === 0) return Computation.resolve(surface);
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
// }

View File

@@ -1,14 +1,12 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util/value-cell'
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
import { ChunkedArray } from '../../../mol-data/util';
import { Mesh } from './mesh';
import { getNormalMatrix } from '../../util';
import { Primitive } from '../../primitive/primitive';
import { Cage } from '../../../mol-geo/primitive/cage';
import { addSphere } from './builder/sphere';
@@ -85,7 +83,7 @@ export namespace MeshBuilder {
const { vertices: va, normals: na, indices: ia } = primitive
const { vertices, normals, indices, groups, currentGroup } = state
const offset = vertices.elementCount
const n = getNormalMatrix(tmpMat3, t)
const n = Mat3.directionTransform(tmpMat3, t)
for (let i = 0, il = va.length; i < il; i += 3) {
// position
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
@@ -101,6 +99,27 @@ export namespace MeshBuilder {
}
}
/** Flips triangle normals and winding order */
export function addPrimitiveFlipped(state: State, t: Mat4, primitive: Primitive) {
const { vertices: va, normals: na, indices: ia } = primitive
const { vertices, normals, indices, groups, currentGroup } = state
const offset = vertices.elementCount
const n = Mat3.directionTransform(tmpMat3, t)
for (let i = 0, il = va.length; i < il; i += 3) {
// position
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
// normal
Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, na, i), n)
ChunkedArray.add3(normals, -tmpV[0], -tmpV[1], -tmpV[2]);
// group
ChunkedArray.add(groups, currentGroup);
}
for (let i = 0, il = ia.length; i < il; i += 3) {
ChunkedArray.add3(indices, ia[i + 2] + offset, ia[i + 1] + offset, ia[i] + offset);
}
}
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, radialSegments }
@@ -121,15 +140,6 @@ export namespace MeshBuilder {
const ib = ChunkedArray.compact(indices, true) as Uint32Array
const nb = ChunkedArray.compact(normals, true) as Float32Array
const gb = ChunkedArray.compact(groups, true) as Float32Array
return {
kind: 'mesh',
vertexCount: state.vertices.elementCount,
triangleCount: state.indices.elementCount,
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
normalsComputed: true,
}
return Mesh.create(vb, ib, nb, gb, state.vertices.elementCount, state.indices.elementCount, mesh)
}
}

View File

@@ -1,22 +1,22 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Task } from '../../../mol-task'
import { ValueCell } from '../../../mol-util'
import { Vec3, Mat4 } from '../../../mol-math/linear-algebra'
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra'
import { Sphere3D } from '../../../mol-math/geometry'
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createMarkers } from '../marker-data';
import { TransformData } from '../transform-data';
import { LocationIterator } from '../../util/location-iterator';
import { createColors } from '../color-data';
import { ChunkedArray } from '../../../mol-data/util';
import { ChunkedArray, hashFnv32a } from '../../../mol-data/util';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Theme } from '../../../mol-theme/theme';
import { MeshValues } from '../../../mol-gl/renderable/mesh';
import { Color } from '../../../mol-util/color';
@@ -41,85 +41,96 @@ export interface Mesh {
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
readonly groupBuffer: ValueCell<Float32Array>,
/** Flag indicating if normals are computed for the current set of vertices */
normalsComputed: boolean,
/** Bounding sphere of the mesh */
boundingSphere?: Sphere3D
readonly boundingSphere: Sphere3D
/** Maps group ids to vertex indices */
readonly groupMapping: GroupMapping
}
export namespace Mesh {
export function create(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh?: Mesh): Mesh {
return mesh ?
update(vertices, indices, normals, groups, vertexCount, triangleCount, mesh) :
fromArrays(vertices, indices, normals, groups, vertexCount, triangleCount)
}
export function createEmpty(mesh?: Mesh): Mesh {
const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0)
const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0)
const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0)
const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0)
return {
kind: 'mesh',
vertexCount: 0,
triangleCount: 0,
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
normalsComputed: true,
}
return create(vb, ib, nb, gb, 0, 0, mesh)
}
export function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, normalsComputed: boolean): Mesh {
return {
kind: 'mesh',
function hashCode(mesh: Mesh) {
return hashFnv32a([
mesh.vertexCount, mesh.triangleCount,
mesh.vertexBuffer.ref.version, mesh.indexBuffer.ref.version,
mesh.normalBuffer.ref.version, mesh.groupBuffer.ref.version
])
}
function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number): Mesh {
const boundingSphere = Sphere3D()
let groupMapping: GroupMapping
let currentHash = -1
let currentGroup = -1
const mesh = {
kind: 'mesh' as const,
vertexCount,
triangleCount,
vertexBuffer: ValueCell.create(vertices),
indexBuffer: ValueCell.create(indices),
normalBuffer: ValueCell.create(normals),
groupBuffer: ValueCell.create(groups),
normalsComputed,
}
}
export function computeNormalsImmediate(mesh: Mesh) {
if (mesh.normalsComputed) return;
const normals = mesh.normalBuffer.ref.value.length >= mesh.vertexCount * 3
? mesh.normalBuffer.ref.value : new Float32Array(mesh.vertexBuffer.ref.value.length);
const v = mesh.vertexBuffer.ref.value, triangles = mesh.indexBuffer.ref.value;
if (normals === mesh.normalBuffer.ref.value) {
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
normals[i] = 0; normals[i + 1] = 0; normals[i + 2] = 0;
get boundingSphere() {
const newHash = hashCode(mesh)
if (newHash !== currentHash) {
const b = calculateInvariantBoundingSphere(mesh.vertexBuffer.ref.value, mesh.vertexCount, 1)
Sphere3D.copy(boundingSphere, b)
currentHash = newHash
}
return boundingSphere
},
get groupMapping() {
if (mesh.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(mesh.groupBuffer.ref.value, mesh.vertexCount)
currentGroup = mesh.groupBuffer.ref.version
}
return groupMapping
}
}
return mesh
}
const x = Vec3.zero(), y = Vec3.zero(), z = Vec3.zero(), d1 = Vec3.zero(), d2 = Vec3.zero(), n = Vec3.zero();
for (let i = 0, ii = 3 * mesh.triangleCount; i < ii; i += 3) {
const a = 3 * triangles[i], b = 3 * triangles[i + 1], c = 3 * triangles[i + 2];
function update(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh: Mesh) {
mesh.vertexCount = vertexCount
mesh.triangleCount = triangleCount
ValueCell.update(mesh.vertexBuffer, vertices)
ValueCell.update(mesh.indexBuffer, indices)
ValueCell.update(mesh.normalBuffer, normals)
ValueCell.update(mesh.groupBuffer, groups)
return mesh
}
Vec3.fromArray(x, v, a);
Vec3.fromArray(y, v, b);
Vec3.fromArray(z, v, c);
Vec3.sub(d1, z, y);
Vec3.sub(d2, x, y);
Vec3.cross(n, d1, d2);
export function computeNormals(mesh: Mesh) {
const { vertexCount, triangleCount } = mesh
const vertices = mesh.vertexBuffer.ref.value
const indices = mesh.indexBuffer.ref.value
normals[a] += n[0]; normals[a + 1] += n[1]; normals[a + 2] += n[2];
normals[b] += n[0]; normals[b + 1] += n[1]; normals[b + 2] += n[2];
normals[c] += n[0]; normals[c + 1] += n[1]; normals[c + 2] += n[2];
const normals = mesh.normalBuffer.ref.value.length >= vertexCount * 3
? mesh.normalBuffer.ref.value
: new Float32Array(vertexCount * 3);
if (normals === mesh.normalBuffer.ref.value) {
normals.fill(0, 0, vertexCount * 3)
}
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
const nx = normals[i];
const ny = normals[i + 1];
const nz = normals[i + 2];
const f = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
normals[i] *= f; normals[i + 1] *= f; normals[i + 2] *= f;
// console.log([normals[i], normals[i + 1], normals[i + 2]], [v[i], v[i + 1], v[i + 2]])
}
computeIndexedVertexNormals(vertices, indices, normals, vertexCount, triangleCount)
ValueCell.update(mesh.normalBuffer, normals);
mesh.normalsComputed = true;
}
export function checkForDuplicateVertices(mesh: Mesh, fractionDigits = 3) {
@@ -129,7 +140,7 @@ export namespace Mesh {
const hash = (v: Vec3, d: number) => `${v[0].toFixed(d)}|${v[1].toFixed(d)}|${v[2].toFixed(d)}`
let duplicates = 0
const a = Vec3.zero()
const a = Vec3()
for (let i = 0, il = mesh.vertexCount; i < il; ++i) {
Vec3.fromArray(a, v, i * 3)
const k = hash(a, fractionDigits)
@@ -144,63 +155,15 @@ export namespace Mesh {
return duplicates
}
export function computeNormals(surface: Mesh): Task<Mesh> {
return Task.create<Mesh>('Surface (Compute Normals)', async ctx => {
if (surface.normalsComputed) return surface;
await ctx.update('Computing normals...');
computeNormalsImmediate(surface);
return surface;
});
}
export function transformImmediate(mesh: Mesh, t: Mat4) {
transformRangeImmediate(mesh, t, 0, mesh.vertexCount)
}
export function transformRangeImmediate(mesh: Mesh, t: Mat4, offset: number, count: number) {
const tmpMat3 = Mat3()
export function transform(mesh: Mesh, t: Mat4) {
const v = mesh.vertexBuffer.ref.value
transformPositionArray(t, v, offset, count)
// TODO normals transformation does not work for an unknown reason, ASR
// if (mesh.normalBuffer.ref.value) {
// const n = getNormalMatrix(Mat3.zero(), t)
// transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count)
// mesh.normalsComputed = true;
// }
transformPositionArray(t, v, 0, mesh.vertexCount)
if (!Mat4.isTranslationAndUniformScaling(t)) {
const n = Mat3.directionTransform(tmpMat3, t)
transformDirectionArray(n, mesh.normalBuffer.ref.value, 0, mesh.vertexCount)
}
ValueCell.update(mesh.vertexBuffer, v);
mesh.normalsComputed = false;
}
export function computeBoundingSphere(mesh: Mesh): Task<Mesh> {
return Task.create<Mesh>('Mesh (Compute Bounding Sphere)', async ctx => {
if (mesh.boundingSphere) {
return mesh;
}
await ctx.update('Computing bounding sphere...');
const vertices = mesh.vertexBuffer.ref.value;
let x = 0, y = 0, z = 0;
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
x += vertices[i];
y += vertices[i + 1];
z += vertices[i + 2];
}
x /= mesh.vertexCount;
y /= mesh.vertexCount;
z /= mesh.vertexCount;
let r = 0;
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
const dx = x - vertices[i];
const dy = y - vertices[i + 1];
const dz = z - vertices[i + 2];
r = Math.max(r, dx * dx + dy * dy + dz * dz);
}
mesh.boundingSphere = {
center: Vec3.create(x, y, z),
radius: Math.sqrt(r)
}
return mesh;
});
}
/**
@@ -228,12 +191,12 @@ export namespace Mesh {
group.currentIndex = vertexCount
group.elementCount = vertexCount
const vi = Vec3.zero()
const vj = Vec3.zero()
const vk = Vec3.zero()
const ni = Vec3.zero()
const nj = Vec3.zero()
const nk = Vec3.zero()
const vi = Vec3()
const vj = Vec3()
const vk = Vec3()
const ni = Vec3()
const nj = Vec3()
const nk = Vec3()
function add(i: number) {
Vec3.fromArray(vi, vb, i * 3)
@@ -361,6 +324,7 @@ export namespace Mesh {
doubleSided: PD.Boolean(false),
flipSided: PD.Boolean(false),
flatShaded: PD.Boolean(false),
ignoreLight: PD.Boolean(false),
}
export type Params = typeof Params
@@ -388,10 +352,8 @@ export namespace Mesh {
const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
mesh.vertexBuffer.ref.value, mesh.vertexCount,
transform.aTransform.ref.value, instanceCount
)
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
return {
aPosition: mesh.vertexBuffer,
@@ -410,6 +372,7 @@ export namespace Mesh {
dDoubleSided: ValueCell.create(props.doubleSided),
dFlatShaded: ValueCell.create(props.flatShaded),
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
}
}
@@ -424,13 +387,13 @@ export namespace Mesh {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
}
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
values.aPosition.ref.value, mesh.vertexCount,
values.aTransform.ref.value, values.instanceCount.ref.value
)
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}
@@ -439,86 +402,3 @@ export namespace Mesh {
}
}
}
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
// dst[3 * j] += src[3 * i];
// dst[3 * j + 1] += src[3 * i + 1];
// dst[3 * j + 2] += src[3 * i + 2];
// }
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
// const triCount = surface.triangleIndices.length,
// src = surface.vertices;
// const triangleIndices = surface.triangleIndices;
// for (let i = 0; i < triCount; i += 3) {
// const a = triangleIndices[i],
// b = triangleIndices[i + 1],
// c = triangleIndices[i + 2];
// addVertex(src, b, vs, a);
// addVertex(src, c, vs, a);
// addVertex(src, a, vs, b);
// addVertex(src, c, vs, b);
// addVertex(src, a, vs, c);
// addVertex(src, b, vs, c);
// }
// const vw = 2 * vertexWeight;
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
// const n = vertexCounts[i] + vw;
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
// }
// }
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
// await ctx.updateProgress('Smoothing surface...', true);
// const vertexCounts = new Int32Array(surface.vertexCount),
// triCount = surface.triangleIndices.length;
// const tris = surface.triangleIndices;
// for (let i = 0; i < triCount; i++) {
// // in a triangle 2 edges touch each vertex, hence the constant.
// vertexCounts[tris[i]] += 2;
// }
// let vs = new Float32Array(surface.vertices.length);
// let started = Utils.PerformanceMonitor.currentTime();
// await ctx.updateProgress('Smoothing surface...', true);
// for (let i = 0; i < iterCount; i++) {
// if (i > 0) {
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
// }
// surface.normals = void 0;
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
// const t = surface.vertices;
// surface.vertices = <any>vs;
// vs = <any>t;
// const time = Utils.PerformanceMonitor.currentTime();
// if (time - started > Computation.UpdateProgressDelta) {
// started = time;
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
// }
// }
// return surface;
// }
// /*
// * Smooths the vertices by averaging the neighborhood.
// *
// * Resets normals. Might replace vertex array.
// */
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
// if (iterCount < 1) iterCount = 0;
// if (iterCount === 0) return Computation.resolve(surface);
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
// }

View File

@@ -1,10 +1,9 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util/value-cell'
import { ChunkedArray } from '../../../mol-data/util';
import { Points } from './points';
@@ -26,12 +25,7 @@ export namespace PointsBuilder {
getPoints: () => {
const cb = ChunkedArray.compact(centers, true) as Float32Array
const gb = ChunkedArray.compact(groups, true) as Float32Array
return {
kind: 'points',
pointCount: centers.elementCount,
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
}
return Points.create(cb, gb, centers.elementCount, points)
}
}
}

View File

@@ -1,12 +1,14 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util'
import { Mat4 } from '../../../mol-math/linear-algebra'
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
GroupMapping,
createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -14,7 +16,7 @@ import { createSizes } from '../size-data';
import { TransformData } from '../transform-data';
import { LocationIterator } from '../../util/location-iterator';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Theme } from '../../../mol-theme/theme';
import { PointsValues } from '../../../mol-gl/renderable/points';
@@ -23,37 +25,88 @@ import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
/** Point cloud */
export interface Points {
readonly kind: 'points',
/** Number of vertices in the point cloud */
pointCount: number,
/** Center buffer as array of xyz values wrapped in a value cell */
readonly centerBuffer: ValueCell<Float32Array>,
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
readonly groupBuffer: ValueCell<Float32Array>,
/** Bounding sphere of the points */
readonly boundingSphere: Sphere3D
/** Maps group ids to point indices */
readonly groupMapping: GroupMapping
}
export namespace Points {
export function create(centers: Float32Array, groups: Float32Array, pointCount: number, points?: Points): Points {
return points ?
update(centers, groups, pointCount, points) :
fromArrays(centers, groups, pointCount)
}
export function createEmpty(points?: Points): Points {
const cb = points ? points.centerBuffer.ref.value : new Float32Array(0)
const gb = points ? points.groupBuffer.ref.value : new Float32Array(0)
return {
kind: 'points',
pointCount: 0,
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
return create(cb, gb, 0, points)
}
function hashCode(points: Points) {
return hashFnv32a([
points.pointCount, points.centerBuffer.ref.version, points.groupBuffer.ref.version,
])
}
function fromArrays(centers: Float32Array, groups: Float32Array, pointCount: number): Points {
const boundingSphere = Sphere3D()
let groupMapping: GroupMapping
let currentHash = -1
let currentGroup = -1
const points = {
kind: 'points' as const,
pointCount,
centerBuffer: ValueCell.create(centers),
groupBuffer: ValueCell.create(groups),
get boundingSphere() {
const newHash = hashCode(points)
if (newHash !== currentHash) {
const b = calculateInvariantBoundingSphere(points.centerBuffer.ref.value, points.pointCount, 1)
Sphere3D.copy(boundingSphere, b)
currentHash = newHash
}
return boundingSphere
},
get groupMapping() {
if (points.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(points.groupBuffer.ref.value, points.pointCount)
currentGroup = points.groupBuffer.ref.version
}
return groupMapping
}
}
return points
}
export function transformImmediate(points: Points, t: Mat4) {
transformRangeImmediate(points, t, 0, points.pointCount)
function update(centers: Float32Array, groups: Float32Array, pointCount: number, points: Points) {
points.pointCount = pointCount
ValueCell.update(points.centerBuffer, centers)
ValueCell.update(points.groupBuffer, groups)
return points
}
export function transformRangeImmediate(points: Points, t: Mat4, offset: number, count: number) {
export function transform(points: Points, t: Mat4) {
const c = points.centerBuffer.ref.value
transformPositionArray(t, c, offset, count)
transformPositionArray(t, c, 0, points.pointCount)
ValueCell.update(points.centerBuffer, c);
}
@@ -89,10 +142,8 @@ export namespace Points {
const counts = { drawCount: points.pointCount, groupCount, instanceCount }
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
points.centerBuffer.ref.value, points.pointCount,
transform.aTransform.ref.value, transform.instanceCount.ref.value
)
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
return {
aPosition: points.centerBuffer,
@@ -129,10 +180,9 @@ export namespace Points {
}
function updateBoundingSphere(values: PointsValues, points: Points) {
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
values.aPosition.ref.value, points.pointCount,
values.aTransform.ref.value, values.instanceCount.ref.value
)
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}

View File

@@ -1,10 +1,9 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util/value-cell'
import { ChunkedArray } from '../../../mol-data/util';
import { Spheres } from './spheres';
@@ -50,14 +49,7 @@ export namespace SpheresBuilder {
const mb = ChunkedArray.compact(mappings, true) as Float32Array
const ib = ChunkedArray.compact(indices, true) as Uint32Array
const gb = ChunkedArray.compact(groups, true) as Float32Array
return {
kind: 'spheres',
sphereCount: centers.elementCount / 4,
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb),
}
return Spheres.create(cb, mb, ib, gb, centers.elementCount / 4, spheres)
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -13,15 +13,16 @@ import { Theme } from '../../../mol-theme/theme';
import { SpheresValues } from '../../../mol-gl/renderable/spheres';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { createSizes, getMaxSize } from '../size-data';
import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { GroupMapping, createGroupMapping } from '../../util';
/** Spheres */
export interface Spheres {
readonly kind: 'spheres',
@@ -36,28 +37,85 @@ export interface Spheres {
readonly indexBuffer: ValueCell<Uint32Array>,
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
readonly groupBuffer: ValueCell<Float32Array>,
/** Bounding sphere of the spheres */
readonly boundingSphere: Sphere3D
/** Maps group ids to sphere indices */
readonly groupMapping: GroupMapping
}
export namespace Spheres {
export function create(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres?: Spheres): Spheres {
return spheres ?
update(centers, mappings, indices, groups, sphereCount, spheres) :
fromArrays(centers, mappings, indices, groups, sphereCount)
}
export function createEmpty(spheres?: Spheres): Spheres {
const cb = spheres ? spheres.centerBuffer.ref.value : new Float32Array(0)
const mb = spheres ? spheres.mappingBuffer.ref.value : new Float32Array(0)
const ib = spheres ? spheres.indexBuffer.ref.value : new Uint32Array(0)
const gb = spheres ? spheres.groupBuffer.ref.value : new Float32Array(0)
return {
kind: 'spheres',
sphereCount: 0,
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb)
return create(cb, mb, ib, gb, 0, spheres)
}
function hashCode(spheres: Spheres) {
return hashFnv32a([
spheres.sphereCount,
spheres.centerBuffer.ref.version, spheres.mappingBuffer.ref.version,
spheres.indexBuffer.ref.version, spheres.groupBuffer.ref.version
])
}
function fromArrays(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number): Spheres {
const boundingSphere = Sphere3D()
let groupMapping: GroupMapping
let currentHash = -1
let currentGroup = -1
const spheres = {
kind: 'spheres' as const,
sphereCount,
centerBuffer: ValueCell.create(centers),
mappingBuffer: ValueCell.create(mappings),
indexBuffer: ValueCell.create(indices),
groupBuffer: ValueCell.create(groups),
get boundingSphere() {
const newHash = hashCode(spheres)
if (newHash !== currentHash) {
const b = calculateInvariantBoundingSphere(spheres.centerBuffer.ref.value, spheres.sphereCount * 4, 4)
Sphere3D.copy(boundingSphere, b)
currentHash = newHash
}
return boundingSphere
},
get groupMapping() {
if (spheres.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(spheres.groupBuffer.ref.value, spheres.sphereCount, 4)
currentGroup = spheres.groupBuffer.ref.version
}
return groupMapping
}
}
return spheres
}
function update(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres: Spheres) {
spheres.sphereCount = sphereCount
ValueCell.update(spheres.centerBuffer, centers)
ValueCell.update(spheres.mappingBuffer, mappings)
ValueCell.update(spheres.indexBuffer, indices)
ValueCell.update(spheres.groupBuffer, groups)
return spheres
}
export const Params = {
...BaseGeometry.Params,
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
doubleSided: PD.Boolean(false),
ignoreLight: PD.Boolean(false),
}
export type Params = typeof Params
@@ -87,10 +145,8 @@ export namespace Spheres {
const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount }
const padding = getMaxSize(size)
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
transform.aTransform.ref.value, instanceCount, padding, 4
)
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
return {
aPosition: spheres.centerBuffer,
@@ -111,6 +167,7 @@ export namespace Spheres {
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor),
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
}
}
@@ -124,14 +181,14 @@ export namespace Spheres {
BaseGeometry.updateValues(values, props)
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
}
function updateBoundingSphere(values: SpheresValues, spheres: 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, 4
)
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}

View File

@@ -24,11 +24,11 @@ export type FontVariant = 'normal' | 'small-caps'
export type FontWeight = 'normal' | 'bold'
export const FontAtlasParams = {
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
}
export type FontAtlasParams = typeof FontAtlasParams
export type FontAtlasProps = PD.Values<FontAtlasParams>

View File

@@ -1,11 +1,10 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { ValueCell } from '../../../mol-util/value-cell'
import { ChunkedArray } from '../../../mol-data/util';
import { Text } from './text';
import { getFontAtlas } from './font-atlas';
@@ -16,7 +15,7 @@ const quadIndices = new Uint16Array([
])
export interface TextBuilder {
add(str: string, x: number, y: number, z: number, depth: number, group: number): void
add(str: string, x: number, y: number, z: number, depth: number, scale: number, group: number): void
getText(): Text
}
@@ -45,7 +44,7 @@ export namespace TextBuilder {
}
return {
add: (str: string, x: number, y: number, z: number, depth: number, group: number) => {
add: (str: string, x: number, y: number, z: number, depth: number, scale: number, group: number) => {
let bWidth = 0
const nChar = str.length
@@ -111,10 +110,10 @@ export namespace TextBuilder {
}
}
const xLeft = -xShift - margin - 0.1
const xRight = bWidth - xShift + margin + 0.1
const yTop = bHeight - yShift + margin
const yBottom = -yShift - margin
const xLeft = (-xShift - margin - 0.1) * scale
const xRight = (bWidth - xShift + margin + 0.1) * scale
const yTop = (bHeight - yShift + margin) * scale
const yBottom = (-yShift - margin) * scale
// background
if (background) {
@@ -137,45 +136,49 @@ export namespace TextBuilder {
let xBaseA: number, yBaseA: number
let xBaseB: number, yBaseB: number
let xBaseCenter: number, yBaseCenter: number
const scaledTetherLength = tetherLength * scale
const scaledTetherBaseWidth = tetherBaseWidth * scale
switch (attachment) {
case 'bottom-left':
xTip = xLeft - tetherLength / 2
xBaseA = xLeft + tetherBaseWidth / 2
xTip = xLeft - scaledTetherLength / 2
xBaseA = xLeft + scaledTetherBaseWidth / 2
xBaseB = xLeft
xBaseCenter = xLeft
yTip = yBottom - tetherLength / 2
yTip = yBottom - scaledTetherLength / 2
yBaseA = yBottom
yBaseB = yBottom + tetherBaseWidth / 2
yBaseB = yBottom + scaledTetherBaseWidth / 2
yBaseCenter = yBottom
break
case 'bottom-center':
xTip = 0
xBaseA = tetherBaseWidth / 2
xBaseB = -tetherBaseWidth / 2
xBaseA = scaledTetherBaseWidth / 2
xBaseB = -scaledTetherBaseWidth / 2
xBaseCenter = 0
yTip = yBottom - tetherLength
yTip = yBottom - scaledTetherLength
yBaseA = yBottom
yBaseB = yBottom
yBaseCenter = yBottom
break
case 'bottom-right':
xTip = xRight + tetherLength / 2
xTip = xRight + scaledTetherLength / 2
xBaseA = xRight
xBaseB = xRight - tetherBaseWidth / 2
xBaseB = xRight - scaledTetherBaseWidth / 2
xBaseCenter = xRight
yTip = yBottom - tetherLength / 2
yBaseA = yBottom + tetherBaseWidth / 2
yTip = yBottom - scaledTetherLength / 2
yBaseA = yBottom + scaledTetherBaseWidth / 2
yBaseB = yBottom
yBaseCenter = yBottom
break
case 'middle-left':
xTip = xLeft - tetherLength
xTip = xLeft - scaledTetherLength
xBaseA = xLeft
xBaseB = xLeft
xBaseCenter = xLeft
yTip = 0
yBaseA = -tetherBaseWidth / 2
yBaseB = tetherBaseWidth / 2
yBaseA = -scaledTetherBaseWidth / 2
yBaseB = scaledTetherBaseWidth / 2
yBaseCenter = 0
break
case 'middle-center':
@@ -189,42 +192,42 @@ export namespace TextBuilder {
yBaseCenter = 0
break
case 'middle-right':
xTip = xRight + tetherLength
xTip = xRight + scaledTetherLength
xBaseA = xRight
xBaseB = xRight
xBaseCenter = xRight
yTip = 0
yBaseA = tetherBaseWidth / 2
yBaseB = -tetherBaseWidth / 2
yBaseA = scaledTetherBaseWidth / 2
yBaseB = -scaledTetherBaseWidth / 2
yBaseCenter = 0
break
case 'top-left':
xTip = xLeft - tetherLength / 2
xBaseA = xLeft + tetherBaseWidth / 2
xTip = xLeft - scaledTetherLength / 2
xBaseA = xLeft + scaledTetherBaseWidth / 2
xBaseB = xLeft
xBaseCenter = xLeft
yTip = yTop + tetherLength / 2
yTip = yTop + scaledTetherLength / 2
yBaseA = yTop
yBaseB = yTop - tetherBaseWidth / 2
yBaseB = yTop - scaledTetherBaseWidth / 2
yBaseCenter = yTop
break
case 'top-center':
xTip = 0
xBaseA = tetherBaseWidth / 2
xBaseB = -tetherBaseWidth / 2
xBaseA = scaledTetherBaseWidth / 2
xBaseB = -scaledTetherBaseWidth / 2
xBaseCenter = 0
yTip = yTop + tetherLength
yTip = yTop + scaledTetherLength
yBaseA = yTop
yBaseB = yTop
yBaseCenter = yTop
break
case 'top-right':
xTip = xRight + tetherLength / 2
xTip = xRight + scaledTetherLength / 2
xBaseA = xRight
xBaseB = xRight - tetherBaseWidth / 2
xBaseB = xRight - scaledTetherBaseWidth / 2
xBaseCenter = xRight
yTip = yTop + tetherLength / 2
yBaseA = yTop - tetherBaseWidth / 2
yTip = yTop + scaledTetherLength / 2
yBaseA = yTop - scaledTetherBaseWidth / 2
yBaseB = yTop
yBaseCenter = yTop
break
@@ -252,10 +255,15 @@ export namespace TextBuilder {
for (let iChar = 0; iChar < nChar; ++iChar) {
const c = fontAtlas.get(str[iChar])
ChunkedArray.add2(mappings, xadvance - xShift, c.nh - yShift) // top left
ChunkedArray.add2(mappings, xadvance - xShift, -yShift) // bottom left
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, c.nh - yShift) // top right
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, -yShift) // bottom right
const left = (xadvance - xShift) * scale
const right = (xadvance + c.nw - xShift) * scale
const top = (c.nh - yShift) * scale
const bottom = (-yShift) * scale
ChunkedArray.add2(mappings, left, top)
ChunkedArray.add2(mappings, left, bottom)
ChunkedArray.add2(mappings, right, top)
ChunkedArray.add2(mappings, right, bottom)
const texWidth = fontAtlas.texture.width
const texHeight = fontAtlas.texture.height
@@ -281,17 +289,7 @@ export namespace TextBuilder {
const ib = ChunkedArray.compact(indices, true) as Uint32Array
const gb = ChunkedArray.compact(groups, true) as Float32Array
const tb = ChunkedArray.compact(tcoords, true) as Float32Array
return {
kind: 'text',
charCount: indices.elementCount / 2,
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb),
}
return Text.create(ft, cb,mb, db, ib, gb, tb, indices.elementCount / 2, text)
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -15,7 +15,7 @@ import { createSizes, getMaxSize } from '../size-data';
import { createMarkers } from '../marker-data';
import { ColorNames } from '../../../mol-util/color/names';
import { Sphere3D } from '../../../mol-math/geometry';
import { calculateBoundingSphere, TextureImage, createTextureImage } from '../../../mol-gl/renderable/util';
import { TextureImage, createTextureImage, calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { TextValues } from '../../../mol-gl/renderable/text';
import { Color } from '../../../mol-util/color';
import { Vec3 } from '../../../mol-math/linear-algebra';
@@ -26,6 +26,8 @@ import { createRenderObject as _createRenderObject } from '../../../mol-gl/rende
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { GroupMapping, createGroupMapping } from '../../util';
type TextAttachment = (
'bottom-left' | 'bottom-center' | 'bottom-right' |
@@ -38,7 +40,8 @@ export interface Text {
readonly kind: 'text',
/** Number of characters in the text */
readonly charCount: number,
charCount: number,
/** Font Atlas */
readonly fontTexture: ValueCell<TextureImage<Uint8Array>>,
@@ -54,9 +57,20 @@ export interface Text {
readonly groupBuffer: ValueCell<Float32Array>,
/** Texture coordinates buffer as array of uv values wrapped in a value cell */
readonly tcoordBuffer: ValueCell<Float32Array>,
/** Bounding sphere of the text */
readonly boundingSphere: Sphere3D
/** Maps group ids to text indices */
readonly groupMapping: GroupMapping
}
export namespace Text {
export function create(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text?: Text): Text {
return text ?
update(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount, text) :
fromData(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount)
}
export function createEmpty(text?: Text): Text {
const ft = text ? text.fontTexture.ref.value : createTextureImage(0, 1, Uint8Array)
const cb = text ? text.centerBuffer.ref.value : new Float32Array(0)
@@ -65,17 +79,66 @@ export namespace Text {
const ib = text ? text.indexBuffer.ref.value : new Uint32Array(0)
const gb = text ? text.groupBuffer.ref.value : new Float32Array(0)
const tb = text ? text.tcoordBuffer.ref.value : new Float32Array(0)
return {
kind: 'text',
charCount: 0,
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb)
return create(ft, cb, mb, db, ib, gb, tb, 0, text)
}
function hashCode(text: Text) {
return hashFnv32a([
text.charCount, text.fontTexture.ref.version,
text.centerBuffer.ref.version, text.mappingBuffer.ref.version,
text.depthBuffer.ref.version, text.indexBuffer.ref.version,
text.groupBuffer.ref.version, text.tcoordBuffer.ref.version
])
}
function fromData(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number): Text {
const boundingSphere = Sphere3D()
let groupMapping: GroupMapping
let currentHash = -1
let currentGroup = -1
const text = {
kind: 'text' as const,
charCount,
fontTexture: ValueCell.create(fontTexture),
centerBuffer: ValueCell.create(centers),
mappingBuffer: ValueCell.create(mappings),
depthBuffer: ValueCell.create(depths),
indexBuffer: ValueCell.create(indices),
groupBuffer: ValueCell.create(groups),
tcoordBuffer: ValueCell.create(tcoords),
get boundingSphere() {
const newHash = hashCode(text)
if (newHash !== currentHash) {
const b = calculateInvariantBoundingSphere(text.centerBuffer.ref.value, text.charCount * 4, 4)
Sphere3D.copy(boundingSphere, b)
currentHash = newHash
}
return boundingSphere
},
get groupMapping() {
if (text.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(text.groupBuffer.ref.value, text.charCount, 4)
currentGroup = text.groupBuffer.ref.version
}
return groupMapping
}
}
return text
}
function update(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text: Text) {
text.charCount = charCount
ValueCell.update(text.fontTexture, fontTexture)
ValueCell.update(text.centerBuffer, centers)
ValueCell.update(text.mappingBuffer, mappings)
ValueCell.update(text.depthBuffer, depths)
ValueCell.update(text.indexBuffer, indices)
ValueCell.update(text.groupBuffer, groups)
ValueCell.update(text.tcoordBuffer, tcoords)
return text
}
export const Params = {
@@ -130,10 +193,8 @@ export namespace Text {
const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount }
const padding = getPadding(text.mappingBuffer.ref.value, text.depthBuffer.ref.value, text.charCount, getMaxSize(size))
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
text.centerBuffer.ref.value, text.charCount * 4,
transform.aTransform.ref.value, instanceCount, padding
)
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
return {
aPosition: text.centerBuffer,
@@ -194,10 +255,9 @@ export namespace Text {
function updateBoundingSphere(values: TextValues, text: Text) {
const padding = getPadding(values.aMapping.ref.value, values.aDepth.ref.value, text.charCount, getMaxSize(values))
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
values.aPosition.ref.value, text.charCount * 4,
values.aTransform.ref.value, values.instanceCount.ref.value, padding
)
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)
}
@@ -215,6 +275,7 @@ export namespace Text {
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props)
state.pickable = false
state.opaque = false
}
}

View File

@@ -27,38 +27,39 @@ export interface TextureMesh {
readonly kind: 'texture-mesh',
/** Number of vertices in the texture-mesh */
readonly vertexCount: ValueCell<number>,
vertexCount: number,
/** Number of groups in the texture-mesh */
readonly groupCount: ValueCell<number>,
groupCount: number,
readonly geoTextureDim: ValueCell<Vec2>,
/** texture has vertex positions in XYZ and group id in W */
readonly vertexGroupTexture: ValueCell<Texture>,
readonly normalTexture: ValueCell<Texture>,
readonly boundingSphere: ValueCell<Sphere3D>,
readonly boundingSphere: Sphere3D
}
export namespace TextureMesh {
export function create(vertexCount: number, groupCount: number, vertexGroupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh {
const { width, height } = vertexGroupTexture
const width = vertexGroupTexture.getWidth()
const height = vertexGroupTexture.getHeight()
if (textureMesh) {
ValueCell.update(textureMesh.vertexCount, vertexCount)
ValueCell.update(textureMesh.groupCount, groupCount)
textureMesh.vertexCount = vertexCount
textureMesh.groupCount = groupCount
ValueCell.update(textureMesh.geoTextureDim, Vec2.set(textureMesh.geoTextureDim.ref.value, width, height))
ValueCell.update(textureMesh.vertexGroupTexture, vertexGroupTexture)
ValueCell.update(textureMesh.normalTexture, normalTexture)
ValueCell.update(textureMesh.boundingSphere, boundingSphere)
Sphere3D.copy(textureMesh.boundingSphere, boundingSphere)
return textureMesh
} else {
return {
kind: 'texture-mesh',
vertexCount: ValueCell.create(vertexCount),
groupCount: ValueCell.create(groupCount),
vertexCount,
groupCount,
geoTextureDim: ValueCell.create(Vec2.create(width, height)),
vertexGroupTexture: ValueCell.create(vertexGroupTexture),
normalTexture: ValueCell.create(normalTexture),
boundingSphere: ValueCell.create(boundingSphere),
boundingSphere: Sphere3D.clone(boundingSphere),
}
}
}
@@ -93,9 +94,9 @@ export namespace TextureMesh {
const overpaint = createEmptyOverpaint()
const transparency = createEmptyTransparency()
const counts = { drawCount: textureMesh.vertexCount.ref.value, groupCount, instanceCount }
const counts = { drawCount: textureMesh.vertexCount, groupCount, instanceCount }
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere, transform.aTransform.ref.value, transform.instanceCount.ref.value)
return {
uGeoTexDim: textureMesh.geoTextureDim,
@@ -103,9 +104,9 @@ export namespace TextureMesh {
tNormal: textureMesh.normalTexture,
// aGroup is used as a vertex index here and the group id is retirieved from tPositionGroup
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount.ref.value))),
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))),
boundingSphere: ValueCell.create(transformBoundingSphere),
invariantBoundingSphere: textureMesh.boundingSphere,
invariantBoundingSphere: ValueCell.create(Sphere3D.clone(textureMesh.boundingSphere)),
...color,
...marker,
@@ -128,14 +129,7 @@ export namespace TextureMesh {
}
function updateValues(values: TextureMeshValues, props: PD.Values<Params>) {
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
}
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
}
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
@@ -148,7 +142,7 @@ export namespace TextureMesh {
}
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {
const invariantBoundingSphere = textureMesh.boundingSphere.ref.value
const invariantBoundingSphere = textureMesh.boundingSphere
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere)

View File

@@ -61,23 +61,20 @@ export function PerforatedBox() {
let boxCage: Cage
export function BoxCage() {
if (!boxCage) {
boxCage = createCage(
[
0.5, 0.5, -0.5, // bottom
-0.5, 0.5, -0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, 0.5, // top
-0.5, 0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5
],
[
0, 4, 1, 5, 2, 6, 3, 7, // sides
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
4, 5, 5, 6, 6, 7, 7, 4 // top base
]
)
boxCage = createCage([
0.5, 0.5, -0.5, // bottom
-0.5, 0.5, -0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, 0.5, // top
-0.5, 0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5
], [
0, 4, 1, 5, 2, 6, 3, 7, // sides
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
4, 5, 5, 6, 6, 7, 7, 4 // top base
])
}
return boxCage
}

View File

@@ -16,7 +16,7 @@ export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>
return { vertices, edges }
}
export function copyCage(cage: Cage): Cage {
export function cloneCage(cage: Cage): Cage {
return {
vertices: new Float32Array(cage.vertices),
edges: new Uint32Array(cage.edges)

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Primitive } from './primitive';
export const DefaultCircleProps = {
radius: 1,
segments: 36,
thetaStart: 0,
thetaLength: Math.PI * 2
}
export type CirclerProps = Partial<typeof DefaultCircleProps>
export function Circle(props?: CirclerProps): Primitive {
const { radius, segments, thetaStart, thetaLength } = { ...DefaultCircleProps, ...props }
const isFull = thetaLength === Math.PI * 2
const count = isFull ? segments + 1 : segments + 2
const vertices = new Float32Array(count * 3)
const normals = new Float32Array(count * 3)
const indices = new Uint32Array(segments * 3)
// center
vertices[0] = 0; vertices[1] = 0; vertices[2] = 0;
normals[0] = 0; normals[1] = 1; normals[2] = 0;
// vertices & normals
for (let s = 0, i = 3; s < segments; ++s, i += 3) {
const segment = thetaStart + s / segments * thetaLength;
vertices[i] = radius * Math.sin(segment)
vertices[i + 1] = 0
vertices[i + 2] = radius * Math.cos(segment)
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
}
// indices
for (let s = 1, i = 0; s < segments; ++s, i += 3) {
indices[i] = s; indices[i + 1] = s + 1; indices[i + 2] = 0;
}
if (isFull) {
const j = (segments - 1) * 3
indices[j] = segments
indices[j + 1] = 1
indices[j + 2] = 0
} else {
const segment = thetaStart + thetaLength;
const i = (segments + 1) * 3
vertices[i] = radius * Math.sin(segment)
vertices[i + 1] = 0
vertices[i + 2] = radius * Math.cos(segment)
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
const j = (segments - 1) * 3
indices[j] = segments
indices[j + 1] = segments + 1
indices[j + 2] = 0
}
return { vertices, normals, indices }
}

View File

@@ -14,46 +14,46 @@ const b = 1 / t;
const c = 2 - t;
export const dodecahedronVertices: ReadonlyArray<number> = [
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
-b, -b, -b, 0, -a, -c, b, -b, -b, b, b, -b, 0, a, -c,
-b, b, -b, a, c, 0, -a, c, 0, -a, -c, 0, a, -c, 0
];
/** indices of pentagonal faces, groups of five */
export const dodecahedronFaces: ReadonlyArray<number> = [
4, 3, 2, 1, 0,
7, 6, 5, 0, 1,
4, 3, 2, 1, 0,
7, 6, 5, 0, 1,
12, 11, 10, 9, 8,
15, 14, 13, 8, 9,
14, 3, 4, 16, 13,
3, 14, 15, 17, 2,
3, 14, 15, 17, 2,
11, 6, 7, 18, 10,
6, 11, 12, 19, 5,
4, 0, 5, 19, 16,
6, 11, 12, 19, 5,
4, 0, 5, 19, 16,
12, 8, 13, 16, 19,
15, 9, 10, 18, 17,
7, 1, 2, 17, 18
7, 1, 2, 17, 18
];
const dodecahedronIndices: ReadonlyArray<number> = [ // pentagonal faces
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
12, 11, 10, 10, 9, 8, 12, 10, 8, // 12, 11, 10, 9, 8
15, 14, 13, 13, 8, 9, 15, 13, 9, // 15, 14, 13, 8, 9
14, 3, 4, 4, 16, 13, 14, 4, 13, // 14, 3, 4, 16, 13
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
11, 6, 7, 7, 18, 10, 11, 7, 10, // 11, 6, 7, 18, 10
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
12, 8, 13, 13, 16, 19, 12, 13, 19, // 12, 8, 13, 16, 19
15, 9, 10, 10, 18, 17, 15, 10, 17, // 15, 9, 10, 18, 17
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
];
const dodecahedronEdges: ReadonlyArray<number> = [
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
10, 11, 10, 18, 11, 12, 12, 19, 13, 14, 13, 16, 14, 15, 15, 17, 16, 19, 17, 18,
]

View File

@@ -11,8 +11,8 @@ const t = (1 + Math.sqrt(5)) / 2;
const icosahedronVertices: ReadonlyArray<number> = [
-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
];
const icosahedronIndices: ReadonlyArray<number> = [

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -28,7 +28,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
appplyRadius(vertices, radius);
const normals = new Float32Array(vertices.length);
computeIndexedVertexNormals(vertices, indices, normals)
computeIndexedVertexNormals(vertices, indices, normals, vertices.length / 3, indices.length / 3)
return {
vertices: new Float32Array(vertices),
@@ -39,9 +39,9 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
// helper functions
function subdivide(detail: number) {
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
const a = Vec3()
const b = Vec3()
const c = Vec3()
// iterate over all faces and apply a subdivison with the given detail value
for (let i = 0; i < _indices.length; i += 3) {
@@ -66,10 +66,10 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
for (let i = 0; i <= cols; ++i) {
v[i] = []
const aj = Vec3.zero()
const aj = Vec3()
Vec3.lerp(aj, a, c, i / cols)
const bj = Vec3.zero()
const bj = Vec3()
Vec3.lerp(bj, b, c, i / cols)
const rows = cols - i
@@ -77,7 +77,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
if (j === 0 && i === cols) {
v[i][j] = aj
} else {
const abj = Vec3.zero()
const abj = Vec3()
Vec3.lerp(abj, aj, bj, j / rows)
v[i][j] = abj
@@ -90,7 +90,6 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
for (let j = 0; j < 2 * (cols - i) - 1; ++j) {
const k = Math.floor(j / 2)
if (j % 2 === 0) {
builder.add
builder.add(v[i][k + 1], v[i + 1][k], v[i][k])
} else {
builder.add(v[i][k + 1], v[i + 1][k + 1], v[i + 1][k])

View File

@@ -5,7 +5,6 @@
*/
import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra';
import { getNormalMatrix } from '../util';
import { NumberArray } from '../../mol-util/type-helpers';
export interface Primitive {
@@ -74,7 +73,7 @@ const tmpMat3 = Mat3.zero()
/** Transform primitive in-place */
export function transformPrimitive(primitive: Primitive, t: Mat4) {
const { vertices, normals } = primitive
const n = getNormalMatrix(tmpMat3, t)
const n = Mat3.directionTransform(tmpMat3, t)
for (let i = 0, il = vertices.length; i < il; i += 3) {
// position
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,20 +8,16 @@ import { createPrimitive, Primitive } from './primitive';
import { createCage, Cage } from './cage';
export const tetrahedronVertices: ReadonlyArray<number> = [
0.7071, 0, 0, -0.3535, 0.6123, 0, -0.3535, -0.6123, 0,
0, 0, 0.7071, 0, 0, -0.7071
0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5
];
export const tetrahedronIndices: ReadonlyArray<number> = [
4, 1, 0, 4, 2, 1, 4, 0, 2,
0, 1, 3, 1, 2, 3, 2, 0, 3,
2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
];
const tetrahedronEdges: ReadonlyArray<number> = [
0, 1, 1, 2, 2, 0,
0, 3, 1, 3, 2, 3,
0, 4, 1, 4, 2, 4,
]
let tetrahedron: Primitive

View File

@@ -1,30 +1,24 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3, Mat4, Mat3 } from '../mol-math/linear-algebra'
import { NumberArray } from '../mol-util/type-helpers';
import { arrayMax } from '../mol-util/array';
export function normalizeVec3Array<T extends NumberArray> (a: T) {
const n = a.length
for (let i = 0; i < n; i += 3) {
const x = a[ i ]
const y = a[ i + 1 ]
const z = a[ i + 2 ]
export function normalizeVec3Array<T extends NumberArray> (a: T, count: number) {
for (let i = 0, il = count * 3; i < il; i += 3) {
const x = a[i]
const y = a[i + 1]
const z = a[i + 2]
const s = 1 / Math.sqrt(x * x + y * y + z * z)
a[ i ] = x * s
a[ i + 1 ] = y * s
a[ i + 2 ] = z * s
a[i] = x * s
a[i + 1] = y * s
a[i + 2] = z * s
}
}
export function getNormalMatrix(out: Mat3, t: Mat4) {
Mat3.fromMat4(out, t)
Mat3.invert(out, out)
Mat3.transpose(out, out)
return out
return a
}
const tmpV3 = Vec3.zero()
@@ -45,38 +39,33 @@ export function transformDirectionArray (n: Mat3, array: NumberArray, offset: nu
}
}
export function setArrayZero(array: NumberArray) {
const n = array.length
for (let i = 0; i < n; ++i) array[i] = 0
}
/** iterate over the entire buffer and apply the radius to each vertex */
/** iterate over the entire array and apply the radius to each vertex */
export function appplyRadius(vertices: NumberArray, radius: number) {
const v = Vec3.zero()
const n = vertices.length
for (let i = 0; i < n; i += 3) {
Vec3.fromArray(v, vertices, i)
Vec3.normalize(v, v)
Vec3.scale(v, v, radius)
Vec3.toArray(v, vertices, i)
for (let i = 0, il = vertices.length; i < il; i += 3) {
Vec3.fromArray(tmpV3, vertices, i)
Vec3.normalize(tmpV3, tmpV3)
Vec3.scale(tmpV3, tmpV3, radius)
Vec3.toArray(tmpV3, vertices, i)
}
}
/**
* indexed vertex normals weighted by triangle areas http://www.iquilezles.org/www/articles/normals/normals.htm
* normal array must contain only zeros
*/
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T) {
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
const cb = Vec3.zero()
const ab = Vec3.zero()
const a = Vec3()
const b = Vec3()
const c = Vec3()
const cb = Vec3()
const ab = Vec3()
for (let i = 0, il = indices.length; i < il; i += 3) {
const ai = indices[ i ] * 3
const bi = indices[ i + 1 ] * 3
const ci = indices[ i + 2 ] * 3
/**
* indexed vertex normals weighted by triangle areas
* http://www.iquilezles.org/www/articles/normals/normals.htm
* - normals array must contain only zeros
*/
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T, vertexCount: number, triangleCount: number) {
for (let i = 0, il = triangleCount * 3; i < il; i += 3) {
const ai = indices[i] * 3
const bi = indices[i + 1] * 3
const ci = indices[i + 2] * 3
Vec3.fromArray(a, vertices, ai)
Vec3.fromArray(b, vertices, bi)
@@ -86,34 +75,28 @@ export function computeIndexedVertexNormals<T extends NumberArray> (vertices: Nu
Vec3.sub(ab, a, b)
Vec3.cross(cb, cb, ab)
normals[ ai ] += cb[ 0 ]
normals[ ai + 1 ] += cb[ 1 ]
normals[ ai + 2 ] += cb[ 2 ]
normals[ai] += cb[0]
normals[ai + 1] += cb[1]
normals[ai + 2] += cb[2]
normals[ bi ] += cb[ 0 ]
normals[ bi + 1 ] += cb[ 1 ]
normals[ bi + 2 ] += cb[ 2 ]
normals[bi] += cb[0]
normals[bi + 1] += cb[1]
normals[bi + 2] += cb[2]
normals[ ci ] += cb[ 0 ]
normals[ ci + 1 ] += cb[ 1 ]
normals[ ci + 2 ] += cb[ 2 ]
normals[ci] += cb[0]
normals[ci + 1] += cb[1]
normals[ci + 2] += cb[2]
}
normalizeVec3Array(normals)
return normals
return normalizeVec3Array(normals, vertexCount)
}
/** vertex normals for unindexed triangle soup, normal array must contain only zeros */
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T) {
setArrayZero(normals)
const a = Vec3.zero()
const b = Vec3.zero()
const c = Vec3.zero()
const cb = Vec3.zero()
const ab = Vec3.zero()
for (let i = 0, il = vertices.length; i < il; i += 9) {
/**
* vertex normals for unindexed triangle soup
* - normals array must contain only zeros
*/
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T, vertexCount: number) {
for (let i = 0, il = vertexCount * 3; i < il; i += 9) {
Vec3.fromArray(a, vertices, i)
Vec3.fromArray(b, vertices, i + 3)
Vec3.fromArray(c, vertices, i + 6)
@@ -122,19 +105,58 @@ export function computeVertexNormals<T extends NumberArray> (vertices: NumberArr
Vec3.sub(ab, a, b)
Vec3.cross(cb, cb, ab)
normals[ i ] = cb[ 0 ]
normals[ i + 1 ] = cb[ 1 ]
normals[ i + 2 ] = cb[ 2 ]
normals[i] = cb[0]
normals[i + 1] = cb[1]
normals[i + 2] = cb[2]
normals[ i + 3 ] = cb[ 0 ]
normals[ i + 4 ] = cb[ 1 ]
normals[ i + 5 ] = cb[ 2 ]
normals[i + 3] = cb[0]
normals[i + 4] = cb[1]
normals[i + 5] = cb[2]
normals[ i + 6 ] = cb[ 0 ]
normals[ i + 7 ] = cb[ 1 ]
normals[ i + 8 ] = cb[ 2 ]
normals[i + 6] = cb[0]
normals[i + 7] = cb[1]
normals[i + 8] = cb[2]
}
normalizeVec3Array(normals)
return normals
}
return normalizeVec3Array(normals, vertexCount)
}
/**
* Maps groups to data, range for group i is offsets[i] to offsets[i + 1]
*/
export type GroupMapping = {
/** data indices */
readonly indices: ArrayLike<number>
/** range for group i is offsets[i] to offsets[i + 1] */
readonly offsets: ArrayLike<number>
}
/**
* The `step` parameter allows to skip over repeated values in `groups`
*/
export function createGroupMapping(groups: ArrayLike<number>, dataCount: number, step = 1): GroupMapping {
const maxId = arrayMax(groups)
const offsets = new Int32Array(maxId + 2)
const bucketFill = new Int32Array(dataCount)
const bucketSizes = new Int32Array(dataCount)
for (let i = 0, il = dataCount * step; i < il; i += step) ++bucketSizes[groups[i]]
let offset = 0
for (let i = 0; i < dataCount; i++) {
offsets[i] = offset
offset += bucketSizes[i]
}
offsets[dataCount] = offset
const indices = new Int32Array(offset)
for (let i = 0, il = dataCount * step; i < il; i += step) {
const g = groups[i]
const og = offsets[g] + bucketFill[g]
indices[og] = i
++bucketFill[g]
}
return { indices, offsets }
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -7,13 +7,13 @@
*/
import { ChunkedArray } from '../../../mol-data/util';
import { ValueCell, noop } from '../../../mol-util';
import { noop } from '../../../mol-util';
import { Mesh } from '../../geometry/mesh/mesh';
import { AllowedContours } from './tables';
import { LinesBuilder } from '../../geometry/lines/lines-builder';
import { Lines } from '../../geometry/lines/lines';
export interface MarchinCubesBuilder<T> {
export interface MarchinCubesBuilder<T> {
addVertex(x: number, y: number, z: number): number
addNormal(x: number, y: number, z: number): void
addGroup(group: number): void
@@ -52,17 +52,7 @@ export function MarchinCubesMeshBuilder(vertexChunkSize: number, mesh?: Mesh): M
const nb = ChunkedArray.compact(normals, true) as Float32Array;
const ib = ChunkedArray.compact(indices, true) as Uint32Array;
const gb = ChunkedArray.compact(groups, true) as Float32Array;
return {
kind: 'mesh',
vertexCount,
triangleCount,
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
normalsComputed: true
}
return Mesh.create(vb, ib, nb, gb, vertexCount, triangleCount, mesh)
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -65,8 +65,6 @@ function createPoints() {
...transparency,
uAlpha: ValueCell.create(1.0),
uHighlightColor: ValueCell.create(Vec3.create(1.0, 0.4, 0.6)),
uSelectColor: ValueCell.create(Vec3.create(0.2, 1.0, 0.1)),
uInstanceCount: ValueCell.create(1),
uGroupCount: ValueCell.create(3),
@@ -83,7 +81,6 @@ function createPoints() {
dPointSizeAttenuation: ValueCell.create(true),
dPointFilledCircle: ValueCell.create(false),
uPointEdgeBleach: ValueCell.create(0.5),
dUseFog: ValueCell.create(true),
}
const state: RenderableState = {
visible: true,
@@ -104,11 +101,11 @@ describe('renderer', () => {
expect(ctx.gl.canvas.width).toBe(32)
expect(ctx.gl.canvas.height).toBe(32)
expect(ctx.stats.bufferCount).toBe(0);
expect(ctx.stats.textureCount).toBe(0);
expect(ctx.stats.vaoCount).toBe(0);
expect(ctx.programCache.count).toBe(0);
expect(ctx.shaderCache.count).toBe(0);
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(0);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(0);
expect(ctx.stats.resourceCounts.shader).toBe(0);
renderer.setViewport(0, 0, 64, 48)
expect(ctx.gl.getParameter(ctx.gl.VIEWPORT)[2]).toBe(64)
@@ -124,25 +121,23 @@ 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);
expect(ctx.programCache.count).toBe(5);
expect(ctx.shaderCache.count).toBe(10);
scene.commit()
expect(ctx.stats.resourceCounts.attribute).toBe(4);
expect(ctx.stats.resourceCounts.texture).toBe(5);
expect(ctx.stats.resourceCounts.vertexArray).toBe(5);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).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);
expect(ctx.programCache.count).toBe(5);
expect(ctx.shaderCache.count).toBe(10);
scene.commit()
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(0);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);
ctx.programCache.dispose()
expect(ctx.programCache.count).toBe(0);
ctx.shaderCache.clear()
expect(ctx.shaderCache.count).toBe(0);
ctx.resources.destroy()
expect(ctx.stats.resourceCounts.program).toBe(0);
expect(ctx.stats.resourceCounts.shader).toBe(0);
})
})

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { LinkedList } from '../mol-data/generic'
import { GraphicsRenderObject } from './render-object'
type N = LinkedList.Node<GraphicsRenderObject>
export class CommitQueue {
private removeList = LinkedList<GraphicsRenderObject>();
private removeMap = new Map<GraphicsRenderObject, N>();
private addList = LinkedList<GraphicsRenderObject>();
private addMap = new Map<GraphicsRenderObject, N>();
get isEmpty() {
return this.removeList.count === 0 && this.addList.count === 0;
}
add(o: GraphicsRenderObject) {
if (this.removeMap.has(o)) {
const a = this.removeMap.get(o)!;
this.removeMap.delete(o);
this.removeList.remove(a);
}
if (this.addMap.has(o)) return;
const b = this.addList.addLast(o);
this.addMap.set(o, b);
}
remove(o: GraphicsRenderObject) {
if (this.addMap.has(o)) {
const a = this.addMap.get(o)!;
this.addMap.delete(o);
this.addList.remove(a);
}
if (this.removeMap.has(o)) return;
const b = this.removeList.addLast(o);
this.removeMap.set(o, b);
}
tryGetRemove() {
const o = this.removeList.removeFirst();
if (o) this.removeMap.delete(o);
return o;
}
tryGetAdd() {
const o = this.addList.removeFirst();
if (o) this.addMap.delete(o);
return o;
}
}

View File

@@ -8,13 +8,13 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
import { Texture } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { QuadSchema, QuadValues } from '../util';
import { Vec2 } from '../../../mol-math/linear-algebra';
import { getHistopyramidSum } from './sum';
import { Framebuffer, createFramebuffer } from '../../../mol-gl/webgl/framebuffer';
import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
import { isPowerOfTwo } from '../../../mol-math/misc';
import quad_vert from '../../../mol-gl/shader/quad.vert'
import reduction_frag from '../../../mol-gl/shader/histogram-pyramid/reduction.frag'
@@ -55,8 +55,8 @@ function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) {
let textureFramebuffer = LevelTexturesFramebuffers[level]
const size = Math.pow(2, level)
if (textureFramebuffer === undefined) {
const texture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
const framebuffer = createFramebuffer(ctx.gl, ctx.stats)
const texture = ctx.resources.texture('image-float32', 'rgba', 'float', 'nearest')
const framebuffer = ctx.resources.framebuffer()
texture.attachFramebuffer(framebuffer, 0)
textureFramebuffer = { texture, framebuffer }
textureFramebuffer.texture.define(size, size)
@@ -85,22 +85,22 @@ export interface HistogramPyramid {
}
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2): HistogramPyramid {
const { gl, framebufferCache } = ctx
const { gl, resources } = ctx
// printTexture(ctx, inputTexture, 2)
if (inputTexture.width !== inputTexture.height || !isPowerOfTwo(inputTexture.width)) {
if (inputTexture.getWidth() !== inputTexture.getHeight() || !isPowerOfTwo(inputTexture.getWidth())) {
throw new Error('inputTexture must be of square power-of-two size')
}
// This part set the levels
const levels = Math.ceil(Math.log(inputTexture.width) / Math.log(2))
const levels = Math.ceil(Math.log(inputTexture.getWidth()) / Math.log(2))
const maxSize = Math.pow(2, levels)
// console.log('levels', levels, 'maxSize', maxSize)
const pyramidTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
const pyramidTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
pyramidTexture.define(maxSize, maxSize)
const framebuffer = framebufferCache.get('reduction').value
const framebuffer = resources.framebuffer()
pyramidTexture.attachFramebuffer(framebuffer, 0)
gl.clear(gl.COLOR_BUFFER_BIT)

View File

@@ -8,7 +8,7 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec } from '../../renderable/schema';
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
import { Texture } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { decodeFloatRGB } from '../../../mol-util/float-packing';
@@ -45,14 +45,11 @@ function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
let SumTexture: Texture
function getSumTexture(ctx: WebGLContext) {
if (SumTexture) return SumTexture
SumTexture = createTexture(ctx, 'image-uint8', 'rgba', 'ubyte', 'nearest')
SumTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest')
SumTexture.define(1, 1)
return SumTexture
}
/** name for shared framebuffer used for histogram-pyramid operations */
const FramebufferName = 'histogram-pyramid-sum'
function setRenderingDefaults(ctx: WebGLContext) {
const { gl, state } = ctx
state.disable(gl.CULL_FACE)
@@ -66,12 +63,12 @@ function setRenderingDefaults(ctx: WebGLContext) {
const sumArray = new Uint8Array(4)
export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
const { gl, framebufferCache } = ctx
const { gl, resources } = ctx
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture)
ctx.state.currentRenderItemId = -1
const framebuffer = framebufferCache.get(FramebufferName).value
const framebuffer = resources.framebuffer()
const sumTexture = getSumTexture(ctx)
sumTexture.attachFramebuffer(framebuffer, 0)

View File

@@ -8,7 +8,7 @@ import { createComputeRenderable } from '../../renderable'
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
import { Texture } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { Vec3, Vec2 } from '../../../mol-math/linear-algebra';
@@ -17,9 +17,6 @@ import { getTriCount } from './tables';
import quad_vert from '../../../mol-gl/shader/quad.vert'
import active_voxels_frag from '../../../mol-gl/shader/marching-cubes/active-voxels.frag'
/** name for shared framebuffer used for gpu marching cubes operations */
const FramebufferName = 'marching-cubes-active-voxels'
const ActiveVoxelsSchema = {
...QuadSchema,
@@ -67,13 +64,14 @@ function setRenderingDefaults(ctx: WebGLContext) {
}
export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
const { gl, framebufferCache } = ctx
const { width, height } = volumeData
const { gl, resources } = ctx
const width = volumeData.getWidth()
const height = volumeData.getHeight()
const framebuffer = framebufferCache.get(FramebufferName).value
const framebuffer = resources.framebuffer()
framebuffer.bind()
const activeVoxelsTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
const activeVoxelsTex = resources.texture('image-float32', 'rgba', 'float', 'nearest')
activeVoxelsTex.define(width, height)
const renderable = getActiveVoxelsRenderable(ctx, volumeData, gridDim, gridTexDim, isoValue, gridScale)

View File

@@ -8,7 +8,7 @@ import { createComputeRenderable } from '../../renderable'
import { WebGLContext } from '../../webgl/context';
import { createComputeRenderItem } from '../../webgl/render-item';
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
import { Texture } from '../../../mol-gl/webgl/texture';
import { ShaderCode } from '../../../mol-gl/shader-code';
import { ValueCell } from '../../../mol-util';
import { Vec3, Vec2, Mat4 } from '../../../mol-math/linear-algebra';
@@ -18,9 +18,6 @@ import { getTriIndices } from './tables';
import quad_vert from '../../../mol-gl/shader/quad.vert'
import isosurface_frag from '../../../mol-gl/shader/marching-cubes/isosurface.frag'
/** name for shared framebuffer used for gpu marching cubes operations */
const FramebufferName = 'marching-cubes-isosurface'
const IsosurfaceSchema = {
...QuadSchema,
@@ -83,30 +80,30 @@ function setRenderingDefaults(ctx: WebGLContext) {
}
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) {
const { gl, framebufferCache } = ctx
const { gl, resources } = ctx
const { pyramidTex, height, levels, scale, count } = histogramPyramid
// console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim)
// console.log('iso volumeData', volumeData)
const framebuffer = framebufferCache.get(FramebufferName).value
const framebuffer = resources.framebuffer()
let needsClear = false
if (!vertexGroupTexture) {
vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
} else if (vertexGroupTexture.width !== pyramidTex.width || vertexGroupTexture.height !== pyramidTex.height) {
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
} else if (vertexGroupTexture.getWidth() !== pyramidTex.getWidth() || vertexGroupTexture.getHeight() !== pyramidTex.getHeight()) {
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
} else {
needsClear = true
}
if (!normalTexture) {
normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
normalTexture.define(pyramidTex.width, pyramidTex.height)
} else if (normalTexture.width !== pyramidTex.width || normalTexture.height !== pyramidTex.height) {
normalTexture.define(pyramidTex.width, pyramidTex.height)
normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
} else if (normalTexture.getWidth() !== pyramidTex.getWidth() || normalTexture.getHeight() !== pyramidTex.getHeight()) {
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
} else {
needsClear = true
}
@@ -150,7 +147,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
])
setRenderingDefaults(ctx)
gl.viewport(0, 0, pyramidTex.width, pyramidTex.height)
gl.viewport(0, 0, pyramidTex.getWidth(), pyramidTex.getHeight())
if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT)
renderable.render()

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -13,7 +13,7 @@ import { Vec2 } from '../../mol-math/linear-algebra';
import { GLRenderingContext } from '../../mol-gl/webgl/compat';
export const QuadPositions = new Float32Array([
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0 // Second triangle
])
@@ -42,11 +42,11 @@ function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: numb
}
export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number) {
const { gl, framebufferCache } = ctx
width = defaults(width, texture.width)
height = defaults(height, texture.height)
const { gl, resources } = ctx
width = defaults(width, texture.getWidth())
height = defaults(height, texture.getHeight())
const size = width * height * 4
const framebuffer = framebufferCache.get('read-texture').value
const framebuffer = resources.framebuffer()
const array = getArrayForTexture(gl, texture, size)
framebuffer.bind()
texture.attachFramebuffer(framebuffer, 0)

View File

@@ -1,11 +1,10 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { RenderableState, Renderable } from './renderable'
import { RenderableValues } from './renderable/schema';
import { idFactory } from '../mol-util/id-factory';
import { WebGLContext } from './webgl/context';
import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume';
@@ -20,53 +19,40 @@ const getNextId = idFactory(0, 0x7FFFFFFF)
export const getNextMaterialId = idFactory(0, 0x7FFFFFFF)
export interface BaseRenderObject<T extends RenderableValues> { id: number, type: string, values: T, state: RenderableState, materialId: number }
export interface MeshRenderObject extends BaseRenderObject<MeshValues> { type: 'mesh' }
export interface PointsRenderObject extends BaseRenderObject<PointsValues> { type: 'points' }
export interface SpheresRenderObject extends BaseRenderObject<SpheresValues> { type: 'spheres' }
export interface TextRenderObject extends BaseRenderObject<TextValues> { type: 'text' }
export interface LinesRenderObject extends BaseRenderObject<LinesValues> { type: 'lines' }
export interface DirectVolumeRenderObject extends BaseRenderObject<DirectVolumeValues> { type: 'direct-volume' }
export interface TextureMeshRenderObject extends BaseRenderObject<TextureMeshValues> { type: 'texture-mesh' }
export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectType> {
readonly id: number,
readonly type: T,
readonly values: RenderObjectValues<T>,
readonly state: RenderableState,
readonly materialId: number
}
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
export type RenderObjectValues<T extends RenderObjectType> =
T extends 'mesh' ? MeshValues :
T extends 'points' ? PointsValues :
T extends 'spheres' ? SpheresValues :
T extends 'text' ? TextValues :
T extends 'lines' ? LinesValues :
T extends 'direct-volume' ? DirectVolumeValues :
T extends 'texture-mesh' ? TextureMeshValues : never
//
export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject | TextureMeshRenderObject
export type RenderObjectKindType = {
'mesh': MeshRenderObject
'points': PointsRenderObject
'spheres': SpheresRenderObject
'text': TextRenderObject
'lines': LinesRenderObject
'direct-volume': DirectVolumeRenderObject
'texture-mesh': TextureMeshRenderObject
}
export type RenderObjectValuesType = {
'mesh': MeshValues
'points': PointsValues
'spheres': SpheresValues
'text': TextValues
'lines': LinesValues
'direct-volume': DirectVolumeValues
'texture-mesh': TextureMeshValues
}
export type RenderObjectType = keyof RenderObjectKindType
//
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValuesType[T], state: RenderableState, materialId: number): RenderObjectKindType[T] {
return { id: getNextId(), type, values, state, materialId } as RenderObjectKindType[T]
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValues<T>, state: RenderableState, materialId: number): GraphicsRenderObject<T> {
return { id: getNextId(), type, values, state, materialId } as GraphicsRenderObject<T>
}
export function createRenderable(ctx: WebGLContext, o: GraphicsRenderObject): Renderable<any> {
export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>): Renderable<any> {
switch (o.type) {
case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'points': return PointsRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'spheres': return SpheresRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'text': return TextRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId)
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId)
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId)
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId)
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId)
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId)
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId)
}
}
throw new Error('unsupported type')
}

View File

@@ -51,9 +51,6 @@ export const DirectVolumeSchema = {
elements: ElementsSpec('uint32'),
uAlpha: UniformSpec('f'),
uHighlightColor: UniformSpec('v3'),
uSelectColor: UniformSpec('v3'),
dUseFog: DefineSpec('boolean'),
uIsoValue: UniformSpec('f'),
uBboxMin: UniformSpec('v3'),

View File

@@ -19,7 +19,8 @@ export const MeshSchema = {
dFlatShaded: DefineSpec('boolean'),
dDoubleSided: DefineSpec('boolean'),
dFlipSided: DefineSpec('boolean'),
}
dIgnoreLight: DefineSpec('boolean'),
} as const
export type MeshSchema = typeof MeshSchema
export type MeshValues = Values<MeshSchema>

View File

@@ -62,7 +62,7 @@ export type KindValue = {
'sphere': Sphere3D
}
export type Values<S extends RenderableSchema> = { [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
export type Values<S extends RenderableSchema> = { readonly [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
export function splitValues(schema: RenderableSchema, values: RenderableValues) {
const attributeValues: AttributeValues = {}
@@ -128,12 +128,12 @@ export function ValueSpec<K extends ValueKind>(kind: K): ValueSpec<K> {
//
export type RenderableSchema = {
[k: string]: (
readonly [k: string]: (
AttributeSpec<AttributeKind> | UniformSpec<UniformKind> | TextureSpec<TextureKind> |
ValueSpec<ValueKind> | DefineSpec<DefineKind> | ElementsSpec<ElementsKind>
)
}
export type RenderableValues = { [k: string]: ValueCell<any> }
export type RenderableValues = { readonly [k: string]: ValueCell<any> }
//
@@ -148,13 +148,6 @@ export const GlobalUniformSchema = {
uModelViewProjection: UniformSpec('m4'),
uInvModelViewProjection: UniformSpec('m4'),
uLightIntensity: UniformSpec('f'),
uAmbientIntensity: UniformSpec('f'),
uMetalness: UniformSpec('f'),
uRoughness: UniformSpec('f'),
uReflectivity: UniformSpec('f'),
uIsOrtho: UniformSpec('f'),
uPixelRatio: UniformSpec('f'),
uViewportHeight: UniformSpec('f'),
@@ -169,16 +162,33 @@ export const GlobalUniformSchema = {
uFogColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('i'),
// all the following could in principle be per object
// as a kind of 'material' parameter set
// would need to test performance implications
uLightIntensity: UniformSpec('f'),
uAmbientIntensity: UniformSpec('f'),
uMetalness: UniformSpec('f'),
uRoughness: UniformSpec('f'),
uReflectivity: UniformSpec('f'),
uPickingAlphaThreshold: UniformSpec('f'),
uInteriorDarkening: UniformSpec('f'),
}
uInteriorColorFlag: UniformSpec('i'),
uInteriorColor: UniformSpec('v3'),
uHighlightColor: UniformSpec('v3'),
uSelectColor: UniformSpec('v3'),
} as const
export type GlobalUniformSchema = typeof GlobalUniformSchema
export type GlobalUniformValues = Values<GlobalUniformSchema> // { [k in keyof GlobalUniformSchema]: ValueCell<any> }
export const InternalSchema = {
uObjectId: UniformSpec('i'),
uPickable: UniformSpec('i', true),
}
} as const
export type InternalSchema = typeof InternalSchema
export type InternalValues = { [k in keyof InternalSchema]: ValueCell<any> }
@@ -188,7 +198,7 @@ export const ColorSchema = {
uColorTexDim: UniformSpec('v2'),
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
}
} as const
export type ColorSchema = typeof ColorSchema
export type ColorValues = Values<ColorSchema>
@@ -199,14 +209,14 @@ export const SizeSchema = {
tSize: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
uSizeFactor: UniformSpec('f'),
}
} as const
export type SizeSchema = typeof SizeSchema
export type SizeValues = Values<SizeSchema>
export const MarkerSchema = {
uMarkerTexDim: UniformSpec('v2'),
tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
}
} as const
export type MarkerSchema = typeof MarkerSchema
export type MarkerValues = Values<MarkerSchema>
@@ -214,7 +224,7 @@ export const OverpaintSchema = {
uOverpaintTexDim: UniformSpec('v2'),
tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
dOverpaint: DefineSpec('boolean'),
}
} as const
export type OverpaintSchema = typeof OverpaintSchema
export type OverpaintValues = Values<OverpaintSchema>
@@ -225,7 +235,7 @@ export const TransparencySchema = {
dTransparency: DefineSpec('boolean'),
// dTransparencyType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), // TODO
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
}
} as const
export type TransparencySchema = typeof TransparencySchema
export type TransparencyValues = Values<TransparencySchema>
@@ -250,9 +260,6 @@ export const BaseSchema = {
uInstanceCount: UniformSpec('i'),
uGroupCount: UniformSpec('i'),
uHighlightColor: UniformSpec('v3', true),
uSelectColor: UniformSpec('v3', true),
drawCount: ValueSpec('number'),
instanceCount: ValueSpec('number'),
@@ -270,8 +277,6 @@ export const BaseSchema = {
boundingSphere: ValueSpec('sphere'),
/** bounding sphere NOT taking aTransform into account */
invariantBoundingSphere: ValueSpec('sphere'),
dUseFog: DefineSpec('boolean'),
}
} as const
export type BaseSchema = typeof BaseSchema
export type BaseValues = Values<BaseSchema>

View File

@@ -20,6 +20,7 @@ export const SpheresSchema = {
padding: ValueSpec('number'),
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
}
export type SpheresSchema = typeof SpheresSchema
export type SpheresValues = Values<SpheresSchema>

View File

@@ -22,11 +22,12 @@ export interface RendererStats {
programCount: number
shaderCount: number
bufferCount: number
attributeCount: number
elementsCount: number
framebufferCount: number
renderbufferCount: number
textureCount: number
vaoCount: number
vertexArrayCount: number
drawCount: number
instanceCount: number
@@ -37,8 +38,8 @@ interface Renderer {
readonly stats: RendererStats
readonly props: Readonly<RendererProps>
clear: () => void
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
clear: (transparentBackground: boolean) => void
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => void
setProps: (props: Partial<RendererProps>) => void
setViewport: (x: number, y: number, width: number, height: number) => void
dispose: () => void
@@ -46,9 +47,13 @@ interface Renderer {
export const RendererParams = {
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
// the following are general 'material' parameters
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 }),
interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
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 }),
@@ -56,6 +61,9 @@ export const RendererParams = {
metalness: PD.Numeric(0.0, { 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 }),
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
}
export type RendererProps = PD.Values<typeof RendererParams>
@@ -95,6 +103,15 @@ namespace Renderer {
uViewportHeight: ValueCell.create(viewport.height),
uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
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(0),
// the following are general 'material' uniforms
uLightIntensity: ValueCell.create(p.lightIntensity),
uAmbientIntensity: ValueCell.create(p.ambientIntensity),
@@ -102,16 +119,14 @@ namespace Renderer {
uRoughness: ValueCell.create(p.roughness),
uReflectivity: ValueCell.create(p.reflectivity),
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),
uInteriorColorFlag: ValueCell.create(p.interiorColorFlag ? 1 : 0),
uInteriorColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.interiorColor)),
uHighlightColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.highlightColor)),
uSelectColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.selectColor)),
}
const globalUniformList = Object.entries(globalUniforms)
@@ -161,7 +176,7 @@ namespace Renderer {
}
}
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => {
ValueCell.update(globalUniforms.uModel, scene.view)
ValueCell.update(globalUniforms.uView, camera.view)
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
@@ -181,6 +196,8 @@ namespace Renderer {
ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
ValueCell.update(globalUniforms.uTransparentBackground, transparentBackground ? 1 : 0)
globalUniformsNeedUpdate = true
state.currentRenderItemId = -1
@@ -194,7 +211,7 @@ namespace Renderer {
if (clear) {
if (variant === 'color') {
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
} else {
state.clearColor(1, 1, 1, 1)
}
@@ -211,8 +228,10 @@ namespace Renderer {
state.enable(gl.BLEND)
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i]
state.depthMask(r.values.uAlpha.ref.value === 1.0)
if (!r.state.opaque) renderObject(r, variant)
if (!r.state.opaque) {
state.depthMask(false)
renderObject(r, variant)
}
}
} else { // picking & depth
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -224,32 +243,39 @@ namespace Renderer {
}
return {
clear: () => {
clear: (transparentBackground: boolean) => {
state.depthMask(true)
state.colorMask(true, true, true, true)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
},
render,
setProps: (props: Partial<RendererProps>) => {
if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
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.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
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.interiorColorFlag !== undefined && props.interiorColorFlag !== p.interiorColorFlag) {
p.interiorColorFlag = props.interiorColorFlag
ValueCell.update(globalUniforms.uInteriorColorFlag, p.interiorColorFlag ? 1 : 0)
}
if (props.interiorColor !== undefined && props.interiorColor !== p.interiorColor) {
p.interiorColor = props.interiorColor
ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor))
}
if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
p.lightIntensity = props.lightIntensity
ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
@@ -271,6 +297,15 @@ namespace Renderer {
p.reflectivity = props.reflectivity
ValueCell.update(globalUniforms.uReflectivity, p.reflectivity)
}
if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
p.highlightColor = props.highlightColor
ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor))
}
if (props.selectColor !== undefined && props.selectColor !== p.selectColor) {
p.selectColor = props.selectColor
ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor))
}
},
setViewport: (x: number, y: number, width: number, height: number) => {
gl.viewport(x, y, width, height)
@@ -286,14 +321,15 @@ namespace Renderer {
},
get stats(): RendererStats {
return {
programCount: ctx.programCache.count,
shaderCount: ctx.shaderCache.count,
programCount: ctx.stats.resourceCounts.program,
shaderCount: ctx.stats.resourceCounts.shader,
bufferCount: stats.bufferCount,
framebufferCount: stats.framebufferCount,
renderbufferCount: stats.renderbufferCount,
textureCount: stats.textureCount,
vaoCount: stats.vaoCount,
attributeCount: ctx.stats.resourceCounts.attribute,
elementsCount: ctx.stats.resourceCounts.elements,
framebufferCount: ctx.stats.resourceCounts.framebuffer,
renderbufferCount: ctx.stats.resourceCounts.renderbuffer,
textureCount: ctx.stats.resourceCounts.texture,
vertexArrayCount: ctx.stats.resourceCounts.vertexArray,
drawCount: stats.drawCount,
instanceCount: stats.instanceCount,

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Renderable } from './renderable'
@@ -12,8 +13,9 @@ 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';
import { CommitQueue } from './commit-queue';
import { now } from '../mol-util/now';
import { arraySetRemove } from '../mol-util/array';
const boundaryHelper = new BoundaryHelper();
function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D {
@@ -56,13 +58,12 @@ 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
update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean, isRemoving?: boolean) => void
add: (o: GraphicsRenderObject) => void // Renderable<any>
remove: (o: GraphicsRenderObject) => void
syncCommit: () => void
commit: () => Task<void>
commit: (maxTimeMs?: number) => boolean
readonly needsCommit: boolean
has: (o: GraphicsRenderObject) => boolean
clear: () => void
forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void
@@ -78,7 +79,7 @@ namespace Scene {
const object3d = Object3D.create()
const add = (o: GraphicsRenderObject) => {
function add(o: GraphicsRenderObject) {
if (!renderableMap.has(o)) {
const renderable = createRenderable(ctx, o)
renderables.push(renderable)
@@ -91,47 +92,52 @@ namespace Scene {
}
}
const remove = (o: GraphicsRenderObject) => {
function remove(o: GraphicsRenderObject) {
const renderable = renderableMap.get(o)
if (renderable) {
renderable.dispose()
renderables.splice(renderables.indexOf(renderable), 1)
arraySetRemove(renderables, renderable);
renderableMap.delete(o)
boundingSphereDirty = true
}
}
const commitQueue = new AsyncQueue<any>();
const toAdd: GraphicsRenderObject[] = []
const toRemove: GraphicsRenderObject[] = []
const commitBulkSize = 100;
function commit(maxTimeMs: number) {
const start = now();
type CommitParams = { toAdd: GraphicsRenderObject[], toRemove: GraphicsRenderObject[] }
let i = 0;
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])
}
while (true) {
const o = commitQueue.tryGetRemove();
if (!o) break;
remove(o);
if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false;
}
while (true) {
const o = commitQueue.tryGetAdd();
if (!o) break;
add(o);
if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false;
}
renderables.sort(renderableSort)
return true;
}
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)
}
// const toAdd: GraphicsRenderObject[] = []
// const toRemove: GraphicsRenderObject[] = []
const commitQueue = new CommitQueue();
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 },
// get isCommiting () { return commitQueue.length > 0 },
update(objects, keepBoundingSphere) {
update(objects, keepBoundingSphere, isRemoving) {
Object3D.update(object3d)
if (objects) {
for (let i = 0, il = objects.length; i < il; ++i) {
@@ -139,44 +145,17 @@ namespace Scene {
if (!o) continue;
o.update();
}
} else {
} else if (!isRemoving) {
for (let i = 0, il = renderables.length; i < il; ++i) {
renderables[i].update()
}
}
if (!keepBoundingSphere) boundingSphereDirty = true
},
add: (o: GraphicsRenderObject) => {
toAdd.push(o)
},
remove: (o: GraphicsRenderObject) => {
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);
})
},
add: (o: GraphicsRenderObject) => commitQueue.add(o),
remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
commit: (maxTime = Number.MAX_VALUE) => commit(maxTime),
get needsCommit() { return !commitQueue.isEmpty; },
has: (o: GraphicsRenderObject) => {
return renderableMap.has(o)
},

View File

@@ -30,6 +30,7 @@ export interface ShaderCode {
}
import apply_fog from './shader/chunks/apply-fog.glsl'
import apply_interior_color from './shader/chunks/apply-interior-color.glsl'
import apply_light_color from './shader/chunks/apply-light-color.glsl'
import apply_marker_color from './shader/chunks/apply-marker-color.glsl'
import assign_color_varying from './shader/chunks/assign-color-varying.glsl'
@@ -39,6 +40,7 @@ import assign_material_color from './shader/chunks/assign-material-color.glsl'
import assign_normal from './shader/chunks/assign-normal.glsl'
import assign_position from './shader/chunks/assign-position.glsl'
import assign_size from './shader/chunks/assign-size.glsl'
import check_picking_alpha from './shader/chunks/check-picking-alpha.glsl'
import color_frag_params from './shader/chunks/color-frag-params.glsl'
import color_vert_params from './shader/chunks/color-vert-params.glsl'
import common_frag_params from './shader/chunks/common-frag-params.glsl'
@@ -54,6 +56,7 @@ import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest
const ShaderChunks: { [k: string]: string } = {
apply_fog,
apply_interior_color,
apply_light_color,
apply_marker_color,
assign_color_varying,
@@ -63,6 +66,7 @@ const ShaderChunks: { [k: string]: string } = {
assign_normal,
assign_position,
assign_size,
check_picking_alpha,
color_frag_params,
color_vert_params,
common_frag_params,
@@ -197,13 +201,17 @@ const glsl300FragPrefixCommon = `
#define gl_FragColor out_FragData0
#define gl_FragDepthEXT gl_FragDepth
#define enabledStandardDerivatives
#define enabledFragDepth
#define requiredDrawBuffers
`
function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
const prefix = [ '#version 300 es' ]
if (shaderExtensions.standardDerivatives) {
prefix.push('#define enabledStandardDerivatives')
}
if (shaderExtensions.fragDepth) {
prefix.push('#define enabledFragDepth')
}
if (extensions.drawBuffers) {
const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number
for (let i = 0, il = maxDrawBuffers; i < il; ++i) {

View File

@@ -1,12 +1,10 @@
export default `
#ifdef dUseFog
float depth = length(vViewPosition);
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
if (uTransparentBackground == 0) {
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
} else {
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
gl_FragColor.a = fogAlpha;
}
#endif
float depth = length(vViewPosition);
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
if (uTransparentBackground == 0) {
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
} else {
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
gl_FragColor.a = fogAlpha;
}
`

View File

@@ -0,0 +1,9 @@
export default `
if (interior) {
if (uInteriorColorFlag == 1) {
gl_FragColor.rgb = uInteriorColor;
} else {
gl_FragColor.rgb *= 1.0 - uInteriorDarkening;
}
}
`

View File

@@ -13,12 +13,6 @@ export default `
#endif
#endif
#if defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
if (gl_FrontFacing == false) {
material.rgb *= 1.0 - uInteriorDarkening;
}
#endif
// mix material with overpaint
#if defined(dOverpaint) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);

View File

@@ -0,0 +1,7 @@
export default `
float depth = length(vViewPosition);
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
float alpha = (1.0 - fogFactor) * uAlpha;
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1)
discard; // ignore so the element below can be picked
`

View File

@@ -25,4 +25,7 @@ uniform int uPickable;
uniform int uTransparentBackground;
uniform float uInteriorDarkening;
uniform int uInteriorColorFlag;
uniform vec3 uInteriorColor;
bool interior;
`

View File

@@ -11,7 +11,7 @@ float intMod(const in float a, const in float b) { return a - b * float(int(a) /
float pow2(const in float x) { return x*x; }
const float maxFloat = 10000.0; // NOTE constant also set in TypeScript
const float floatLogFactor = log(maxFloat + 1.0);
const float floatLogFactor = 9.210440366976517; // log(maxFloat + 1.0);
float encodeFloatLog(const in float value) { return log(value + 1.0) / floatLogFactor; }
float decodeFloatLog(const in float value) { return exp(value * floatLogFactor) - 1.0; }

View File

@@ -16,8 +16,7 @@ void main(){
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
if (uAlpha < uPickingAlphaThreshold)
discard; // ignore so the element below can be picked
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dColorType_depth)
gl_FragColor = material;

View File

@@ -50,6 +50,11 @@ void main(){
vec4 start = modelView * vec4(aStart, 1.0);
vec4 end = modelView * vec4(aEnd, 1.0);
// assign position
vec3 position = (aMapping.y < 0.5) ? aStart : aEnd;
vec4 mvPosition = modelView * vec4(position, 1.0);
vViewPosition = mvPosition.xyz;
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly

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