Compare commits

..

142 Commits

Author SHA1 Message Date
dsehnal
41d67eb642 2.3.4 2021-10-12 19:05:48 +02:00
dsehnal
c76c8335d1 changelog 2021-10-12 19:03:37 +02:00
dsehnal
42528b7be5 fix argparse config 2021-10-12 19:01:23 +02:00
Alexander Rose
3d651b40f0 use node 14 in lint action 2021-10-03 15:12:58 -07:00
Alexander Rose
c94acff82e update packages 2021-10-03 11:12:24 -07:00
Alexander Rose
93b9953f6d add missing createEmpty* geo utils
- direct-volume
- image
- texture-mesh
2021-10-03 10:43:56 -07:00
Alexander Rose
dcaf6f8927 add multipleBonds param to bond visuals 2021-10-02 15:44:17 -07:00
Alexander Rose
d96eb404e1 add elements crosses visual 2021-10-02 15:09:13 -07:00
Alexander Rose
07322819f0 Merge pull request #271 from molstar/pick-atom
Picking improvements
2021-10-02 13:41:25 -07:00
Alexander Rose
9be686686d add pickPadding config option 2021-10-02 09:34:56 -07:00
Alexander Rose
3df539c9e1 Merge branch 'master' of https://github.com/molstar/molstar into pick-atom 2021-10-02 09:30:07 -07:00
dsehnal
903f06bab6 2.3.3 2021-10-01 17:56:26 +02:00
dsehnal
13f2810f90 fix direct volume shader 2021-10-01 17:54:33 +02:00
dsehnal
ee8cae16d2 2.3.2 2021-10-01 17:12:42 +02:00
dsehnal
feaf6f7fd4 (temporarily) prefer webgl1 on iOS 2021-10-01 17:10:49 +02:00
dsehnal
04775a2e44 2.3.1 2021-09-28 16:13:51 +02:00
dsehnal
bec9fec755 chem_comp_bond and struct_conn to mmcif exporter
+ fix argparse config for Model/Volume servers
2021-09-28 16:12:01 +02:00
Alexander Rose
e840059a38 pick improvements
- ensure lines & points are at least 1 pixel big
- look around center pixel in a spiral for hits
2021-09-26 17:20:16 -07:00
Alexander Rose
1bd0339dec add points visual to line repr 2021-09-26 17:13:49 -07:00
Alexander Rose
d0eaf2f71e Merge branch 'master' of https://github.com/molstar/molstar into pick-atom 2021-09-26 13:41:23 -07:00
Alexander Rose
c7edf40afe add markerPriority param to renderer 2021-09-26 13:31:41 -07:00
Alexander Rose
b44962eb2f lint: add semi-spacing rule 2021-09-26 13:14:32 -07:00
Alexander Rose
254c9efbf5 improve/fix implicit atom picking 2021-09-26 13:06:56 -07:00
Alexander Rose
73e9aed98c add preferAtoms param to Select/Highlight behaviors 2021-09-26 12:58:29 -07:00
Alexander Rose
6e60d9713a fix bond atoms not added in selection manager 2021-09-26 12:53:53 -07:00
Alexander Rose
ef0593b1e2 add pixel-scale & pick-scale GET params to Viewer 2021-09-26 12:43:57 -07:00
Alexander Rose
7831fa8b33 fix: pickScale not considered in line/point shader 2021-09-26 12:39:44 -07:00
dsehnal
c64851492c applyMarkerAction take 2 2021-09-21 18:38:05 +02:00
dsehnal
4a2e93e265 fix applyMarkerAction edge case 2021-09-21 10:15:29 +02:00
Alexander Rose
d4bb1a6e93 wip: prefer to pick atoms close to ends over bonds 2021-09-19 16:24:45 -07:00
Alexander Rose
55de0aba69 fix point repr & shader 2021-09-19 16:00:40 -07:00
Alexander Rose
ecfa7b5a99 fix currentTheme not set in Representation.createMulti 2021-09-19 15:45:08 -07:00
dsehnal
bee3dc4595 fix double bonds from sctruct_conn records 2021-09-19 17:40:38 +02:00
Alexander Rose
787ca47825 fix line shader not accounting for aspect ratio
- also remove uViewportHeight in favor of uViewport
2021-09-18 23:13:00 -07:00
Alexander Rose
6fab6ce1f2 add map-provider GET param to Viewer app 2021-09-18 16:52:35 -07:00
Alexander Rose
9fd95f1a11 handle missing occupancy column
- treat as occ 1
2021-09-18 16:47:52 -07:00
Alexander Rose
69fe0901e2 add CharmmSaccharideNames 2021-09-18 16:37:39 -07:00
Alexander Rose
8c417ef35c lint: add space-before-blocks rule 2021-09-12 23:23:03 -07:00
Alexander Rose
01691f3050 lint: add keyword-spacing rule 2021-09-12 23:20:59 -07:00
Alexander Rose
128abf3090 lint: add no-multi-spaces rule 2021-09-12 23:13:44 -07:00
Alexander Rose
0c0e995256 lint: add no-multi-spaces rule 2021-09-12 23:11:29 -07:00
Alexander Rose
4090498f92 lint: add func-call-spacing rule 2021-09-12 23:03:35 -07:00
Alexander Rose
24677d6931 lint: add space-before-function-paren rule 2021-09-12 22:57:48 -07:00
Alexander Rose
908fff8041 lint: add prefer-const rule 2021-09-12 20:02:53 -07:00
Alexander Rose
aa1eb90f66 lint: add computed-property-spacing rule 2021-09-12 19:41:13 -07:00
Alexander Rose
1d21787e7e lint: add space-in-parens rule 2021-09-12 19:37:26 -07:00
Alexander Rose
42409a2bc7 lint: add array-bracket-spacing rule 2021-09-12 19:32:54 -07:00
Alexander Rose
b31302ba3a lint: add object-curly-spacing rule 2021-09-12 18:45:32 -07:00
Alexander Rose
3840b0041b lint: add key-spacing rule 2021-09-12 18:38:45 -07:00
Alexander Rose
cc68f8311d lint: add no-throw-literal rule 2021-09-12 16:22:43 -07:00
Alexander Rose
3400c8e94a update coreCif dictionary 2021-09-12 16:19:23 -07:00
Alexander Rose
61c47c517b update mmcif schema types and derived data 2021-09-12 15:06:54 -07:00
Alexander Rose
76674917ca update rscb graphql schema types 2021-09-12 15:04:49 -07:00
Alexander Rose
b835eb8de7 update packages 2021-09-12 13:39:00 -07:00
Alexander Rose
3f0d476e94 Merge pull request #266 from JonStargaryen/master
ANVIL: add TMDET amino acid classification and reference
2021-09-10 22:41:01 -07:00
Sebastian Bittrich
9423d56d36 lint 2021-09-09 16:16:49 -07:00
Sebastian Bittrich
0f03fe99d6 ANVIL: fix for 6y1z 2021-09-09 16:02:39 -07:00
Sebastian Bittrich
556d5bb003 ANVIL: add TMDET ref and option 2021-09-09 09:06:51 -07:00
dsehnal
8e3ea6943f updated packages & tsc 2021-09-07 14:10:10 +02:00
Alexander Rose
599cfc1b1c 2.3.0 2021-09-06 15:33:03 -07:00
Alexander Rose
84eccb5019 changelog 2021-09-06 15:29:44 -07:00
Alexander Rose
2dd07bb0e3 improved getMarkersAverage performance
- use lut
- add performance tests
2021-09-04 15:12:35 -07:00
Alexander Rose
f404d23280 take include/exclude flags into account when displaying aromatic bonds
- this allows to show aromatic rings only when explicitely given
2021-09-04 14:56:40 -07:00
Alexander Rose
8b7333b470 avoid unnecessary draw calls/ui updates when marking
- fix wrong type narrowing of Loci.isEmpty
2021-09-04 14:48:09 -07:00
Alexander Rose
7d26567d40 Merge pull request #258 from molstar/marking
Add optional marking pass
2021-08-31 21:18:23 -07:00
Alexander Rose
8f6dbf2192 asorted performance tweaks 2021-08-31 21:14:02 -07:00
Alexander Rose
db350ddfd3 loci/marking performance improvements
- use Interval for ranges instead of SortedArray
- pre-check if loci overlaps with unit visual
2021-08-30 22:43:01 -07:00
Alexander Rose
c0144d826c SortedArray: add .isRange, improve .areEqual 2021-08-30 22:33:09 -07:00
Alexander Rose
de3e819b80 fix uMarker not being updated 2021-08-30 22:29:35 -07:00
Alexander Rose
bbf96567b1 handle clipping/fog for marking edges 2021-08-29 22:14:11 -07:00
Alexander Rose
c9a3254bd6 fix partial marker average calculation 2021-08-29 21:25:41 -07:00
Alexander Rose
bad6d030f1 marker-data improvements
- add uniform marker type
- check if a loci is superset of a visual
- special case to improve reversing the previous mark
2021-08-29 10:44:09 -07:00
Alexander Rose
e1ad67a059 StructureElement.Loci improvements
- early return in .isEmpty
- ensure Interval is used when possible in .extendToAllInstances
2021-08-29 10:35:05 -07:00
dsehnal
8d3ac92989 2.2.3 2021-08-25 17:40:28 +02:00
dsehnal
d6eb334d12 changelog 2021-08-25 17:38:28 +02:00
Alexander Rose
c62f19623c add optional marking pass
- outlines visible and hidden parts of highlighted/selected groups
- add highlightStrength/selectStrength renderer params
2021-08-22 12:08:15 -07:00
Alexander Rose
77139afe7f tweak Interval.size 2021-08-21 14:00:51 -07:00
Alexander Rose
54476ad85e improve print texture debug helpers 2021-08-21 13:59:38 -07:00
Alexander Rose
6dd876232d avoid superfluous calls to Loci.isWholeStructure 2021-08-21 13:51:40 -07:00
Alexander Rose
ae2314d76c fix camera/bounding helper not showing up 2021-08-21 13:48:56 -07:00
David Sehnal
6667509745 Merge pull request #257 from JonStargaryen/master
ANVIL: improve prediction for 3pqr
2021-08-21 20:22:38 +02:00
JonStargaryen
5c871a5aae ANVIL: increase number of sphere points to 175 2021-08-20 12:43:24 -07:00
Alexander Rose
2482ef92af Improved StructureElement.Loci.size performance
- inlined code
- important for marking large cellpack models
2021-08-15 14:42:49 -07:00
Alexander Rose
db59303a84 Merge pull request #244 from molstar/meshproc
Mesh processing: border smoothing
2021-08-14 19:36:23 -07:00
Alexander Rose
fe700953ff Merge branch 'master' into meshproc 2021-08-14 19:32:06 -07:00
Alexander Rose
047946e41c remove superfluous type casts 2021-08-14 19:30:16 -07:00
dsehnal
f833efae37 2.2.2 2021-08-11 14:54:03 +02:00
dsehnal
be4b787e66 Fix mol-script query compiler const expression recognition 2021-08-11 14:49:52 +02:00
David Sehnal
950b1c179a Merge pull request #248 from MadCatX/fix-isosurface
Do not cache LevelTexturesFramebuffers as they may become invalid
2021-08-10 12:40:52 +02:00
Michal Malý
2bd1a01afb Do not attach framebuffer unnecessarily 2021-08-10 09:15:05 +02:00
dsehnal
2fe43eda2b Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-09 20:33:57 +02:00
dsehnal
45fc0c61af Mesh.smoothEdges options 2021-08-09 20:32:40 +02:00
dsehnal
7e7993f5ba improve fillEdges in mesh edge smoothing
- sort boundary vertices
- limit the length added edges
2021-08-09 20:22:13 +02:00
Michal Malý
9d34dbff0f Do not cache LevelTexturesFramebuffers as they may become invalid 2021-08-09 09:58:55 +02:00
Alexander Rose
ba1b03f01b fix TransformData issues, see #133
- handle structure vs structure.root in ExplodeStructureRepresentation3D and SpinStructureRepresentation3D
2021-08-08 13:11:50 -07:00
Alexander Rose
791f7ca3c8 Merge pull request #245 from sukolsak/optimize-setCylinderMat 2021-08-08 10:45:23 -07:00
Sukolsak Sakshuwong
c14d50e4ff optimize setCylinderMat() 2021-08-08 08:51:16 -07:00
Alexander Rose
3e9de449c8 cleanup 2021-08-07 19:45:47 -07:00
Alexander Rose
0132c7ef5e Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-07 19:42:41 -07:00
Alexander Rose
aa2222c086 remove clipSphere option & param cleanup
- clip objects are better
- includeParent option not useful for gaussian-surface visuals
2021-08-07 19:20:51 -07:00
dsehnal
fc2765d376 2.2.1 2021-08-02 18:11:24 +02:00
dsehnal
9d85194082 2.2.1 changelog 2021-08-02 18:09:50 +02:00
David Sehnal
abfcc60898 Merge pull request #243 from molstar/input-observer-improvements
Input observer improvements
2021-08-02 18:08:48 +02:00
dsehnal
c688a83fa2 fix typo 2021-08-02 18:07:34 +02:00
dsehnal
77376056b9 changelog 2021-08-02 18:03:07 +02:00
dsehnal
8efd943c2b PinchInput.fractionDelta 2021-08-02 17:24:49 +02:00
dsehnal
b230655439 fix type 2021-08-02 16:55:06 +02:00
dsehnal
8ba792c4b0 add maxWheelDelta 2021-08-02 16:50:09 +02:00
dsehnal
fde8ca69e4 support for Safari gestures (pinch zoom on MacBook trackpad) 2021-08-02 16:30:48 +02:00
dsehnal
9ee1439299 normalize wheel speed in input observer 2021-08-02 14:45:25 +02:00
David Sehnal
195668760e Merge pull request #242 from sukolsak/export-overpaint
Add overpaint support to geometry exporters
2021-08-02 13:04:09 +02:00
Sukolsak Sakshuwong
bd64f1db9a update changelog and fix type 2021-08-02 01:19:31 -07:00
Sukolsak Sakshuwong
38a5a857aa refactor color calculation 2021-08-01 23:09:30 -07:00
Sukolsak Sakshuwong
5e8cdfe3a7 add overpaint support to geometry exporters 2021-08-01 22:30:05 -07:00
Alexander Rose
f892917e1c Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-08-01 14:04:50 -07:00
Alexander Rose
738b7f4ca5 Merge pull request #238 from molstar/dynbonds
Bond improvements (mostly IndexPairBonds)
2021-08-01 13:53:42 -07:00
Alexander Rose
bce53d03a5 bond tweaks
- add DefaultBondMaxRadius constant
- add IndexPairBonds.Props object
2021-08-01 13:49:44 -07:00
Alexander Rose
74f721ab9f improve Structure.asParent
- handle parent coordinate system not identity
2021-08-01 13:48:26 -07:00
Alexander Rose
f011025f16 changelog 2021-07-31 23:22:11 -07:00
Alexander Rose
9e44cd83fa add clipSphere param to molecular-surface mesh 2021-07-31 23:21:57 -07:00
Alexander Rose
e0e45b64ac optimize invertCantorPairing 2021-07-31 23:15:33 -07:00
Alexander Rose
f10b152252 Merge branch 'master' of https://github.com/molstar/molstar into meshproc 2021-07-31 14:42:18 -07:00
Alexander Rose
23cf5c2fdd changelog and docs 2021-07-31 14:22:45 -07:00
Alexander Rose
e211abd5ae Merge branch 'master' of https://github.com/molstar/molstar into dynbonds 2021-07-31 14:11:45 -07:00
Alexander Rose
7199be4d62 tweak getColorSmoothingProps
- make independent of webgl context
2021-07-31 14:10:50 -07:00
Alexander Rose
e1a40ded1d add surronding atoms structure selection query 2021-07-31 13:53:49 -07:00
Alexander Rose
8999c3097d handle dynamicBonds in root structure helper 2021-07-31 13:50:07 -07:00
Alexander Rose
44308fa1fd add maxDistance prop to IndexPairBonds 2021-07-31 13:48:10 -07:00
Alexander Rose
f5dd2f4579 support coordinateSystem in structure.asParent 2021-07-31 13:45:39 -07:00
dsehnal
104999b7dc 2.2.0 2021-07-31 15:15:09 +02:00
dsehnal
e5341623d3 changelog v2.2.0 2021-07-31 15:12:40 +02:00
dsehnal
0e9238e5ec Canvas3D tweaks:
- update "forceDraw" logic
- Ensure the scene is re-rendered when viewport size changes
- Support noDraw mode in PluginAnimationLoop
2021-07-31 15:06:58 +02:00
dsehnal
43c292e2df Support new EMDB API for EM volume contour levels 2021-07-31 14:12:33 +02:00
dsehnal
fbfd1b20d8 Prefer _label_seq_id fields in secondary structure assignment 2021-07-31 13:54:11 +02:00
dsehnal
5330df87e1 Merge branch 'master' of https://github.com/molstar/molstar 2021-07-27 12:29:17 +02:00
dsehnal
ad6b3c6fe0 add DS_store to .gitignore 2021-07-27 12:28:56 +02:00
Alexander Rose
b983df7eb5 mesh edge smoothing 2021-07-25 20:26:17 -07:00
Alexander Rose
add76a87d9 remove unnecessary check
- see 7686b61728
2021-07-25 20:22:00 -07:00
Alexander Rose
f9f8350d28 dynamic pair bonds on coordinate changes
- add dynamicBonds structure parameter
- add maxRadius, ignoreWater bond compute parameters
- ensure inter unit bond visuals are recreated
2021-07-24 17:16:59 -07:00
Alexander Rose
b71c2f365c add operator Loci granularity 2021-07-24 16:55:07 -07:00
Alexander Rose
a5443189d3 missing param 2021-07-24 16:46:30 -07:00
Alexander Rose
7686b61728 fix includeParent for multi instance bond visuals 2021-07-24 16:31:26 -07:00
485 changed files with 19816 additions and 32483 deletions

View File

@@ -38,7 +38,24 @@
"selector": "ExportDefaultDeclaration",
"message": "Default exports are not allowed"
}
]
],
"no-throw-literal": "error",
"key-spacing": "error",
"object-curly-spacing": ["error", "always"],
"array-bracket-spacing": "error",
"space-in-parens": "error",
"computed-property-spacing": "error",
"prefer-const": ["error", {
"destructuring": "all",
"ignoreReadBeforeAssign": false
}],
"space-before-function-paren": "off",
"func-call-spacing": "off",
"no-multi-spaces": "error",
"block-spacing": "error",
"keyword-spacing": "off",
"space-before-blocks": "error",
"semi-spacing": "error"
},
"overrides": [
{
@@ -89,7 +106,14 @@
"error",
"1tbs", { "allowSingleLine": true }
],
"@typescript-eslint/comma-spacing": "error"
"@typescript-eslint/comma-spacing": "error",
"@typescript-eslint/space-before-function-paren": ["error", {
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}],
"@typescript-eslint/func-call-spacing": ["error"],
"@typescript-eslint/keyword-spacing": ["error"]
}
}
]

View File

@@ -8,10 +8,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: install node v12
- name: install node v14
uses: actions/setup-node@v1
with:
node-version: 12
node-version: 14
- name: yarn install
run: yarn install
- name: eslint

2
.gitignore vendored
View File

@@ -9,3 +9,5 @@ tsconfig.commonjs.tsbuildinfo
*.sublime-workspace
.idea
.DS_Store

View File

@@ -6,13 +6,100 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
- Add `tubularHelices` parameter to Cartoon representation
- Add `SdfFormat` and update SDF parser to be able to parse data headers according to spec (hopefully :)) #230
## [v2.3.4] - 2021-10-12
- Fix pickScale not taken into account in line/point shader
- Add pixel-scale, pick-scale & pick-padding GET params to Viewer app
- Fix selecting bonds not adding their atoms in selection manager
- Add ``preferAtoms`` option to SelectLoci/HighlightLoci behaviors
- Make the implicit atoms of bond visuals pickable
- Add ``preferAtomPixelPadding`` to Canvas3dInteractionHelper
- Add points & crosses visuals to Line representation
- Add ``pickPadding`` config option (look around in case target pixel is empty)
- Add ``multipleBonds`` param to bond visuals with options: off, symmetric, offset
- Fix ``argparse`` config in servers.
## [v2.3.3] - 2021-10-01
- Fix direct volume shader
## [v2.3.2] - 2021-10-01
- Prefer WebGL1 on iOS devices until WebGL2 support has stabilized.
## [v2.3.1] - 2021-09-28
- Add Charmm saccharide names
- Treat missing occupancy column as occupancy of 1
- Fix line shader not accounting for aspect ratio
- [Breaking] Fix point repr & shader
- Was unusable with ``wboit``
- Replaced ``pointFilledCircle`` & ``pointEdgeBleach`` params by ``pointStyle`` (square, circle, fuzzy)
- Set ``pointSizeAttenuation`` to false by default
- Set ``sizeTheme`` to ``uniform`` by default
- Add ``markerPriority`` option to Renderer (useful in combination with edges of marking pass)
- Add support support for ``chem_comp_bond`` and ``struct_conn`` categories (fixes ModelServer behavior where these categories should have been present)
- Model and VolumeServer: fix argparse config
## [v2.3.0] - 2021-09-06
- Take include/exclude flags into account when displaying aromatic bonds
- Improve marking performance
- Avoid unnecessary draw calls/ui updates when marking
- Check if loci is superset of visual
- Check if loci overlaps with unit visual
- Ensure ``Interval`` is used for ranges instead of ``SortedArray``
- Add uniform marker type
- Special case for reversing previous mark
- Add optional marking pass
- Outlines visible and hidden parts of highlighted/selected groups
- Add highlightStrength/selectStrength renderer params
## [v2.2.3] - 2021-08-25
- Add ``invertCantorPairing`` helper function
- Add ``Mesh`` processing helper ``.smoothEdges``
- Smooth border of molecular-surface with ``includeParent`` enabled
- Hide ``includeParent`` option from gaussian-surface visuals (not particularly useful)
- Improved ``StructureElement.Loci.size`` performance (for marking large cellpack models)
- Fix new ``TransformData`` issues (camera/bounding helper not showing up)
- Improve marking performance (avoid superfluous calls to ``StructureElement.Loci.isWholeStructure``)
## [v2.2.2] - 2021-08-11
- Fix ``TransformData`` issues [#133](https://github.com/molstar/molstar/issues/133)
- Fix ``mol-script`` query compiler const expression recognition.
## [v2.2.1] - 2021-08-02
- Add surrounding atoms (5 Angstrom) structure selection query
- [Breaking] Add maxDistance prop to ``IndexPairBonds``
- Fix coordinateSystem not handled in ``Structure.asParent``
- Add ``dynamicBonds`` to ``Structure`` props (force re-calc on model change)
- Expose as optional param in root structure transform helper
- Add overpaint support to geometry exporters
- ``InputObserver`` improvements
- normalize wheel speed across browsers/platforms
- support Safari gestures (used by ``TrackballControls``)
- ``PinchInput.fractionDelta`` and use it in ``TrackballControls``
## [v2.2.0] - 2021-07-31
- Add ``tubularHelices`` parameter to Cartoon representation
- Add ``SdfFormat`` and update SDF parser to be able to parse data headers according to spec (hopefully :)) #230
- Fix mononucleotides detected as polymer components (#229)
- Set default outline scale back to 1
- Improved DCD reader cell angle handling (intepret near 0 angles as 90 deg)
- Improved DCD reader cell angle handling (interpret near 0 angles as 90 deg)
- Handle more residue/atom names commonly used in force-fields
- Add USDZ support to ``geo-export`` extension.
- Fix ``includeParent`` support for multi-instance bond visuals.
- Add ``operator`` Loci granularity, selecting everything with the same operator name.
- Prefer ``_label_seq_id`` fields in secondary structure assignment.
- Support new EMDB API (https://www.ebi.ac.uk/emdb/api/entry/map/[EMBD-ID]) for EM volume contour levels.
- ``Canvas3D`` tweaks:
- Update ``forceDraw`` logic.
- Ensure the scene is re-rendered when viewport size changes.
- Support ``noDraw`` mode in ``PluginAnimationLoop``.
## [v2.1.0] - 2021-07-05
@@ -46,8 +133,8 @@ Note that since we don't clearly distinguish between a public and private interf
- Add ability to select residues from a list of identifiers to the Selection UI.
- Fix SSAO bugs when used with ``Canvas3D`` viewport.
- Support for full pausing (no draw) rendering: ``Canvas3D.pause(true)``.
- Add `MeshBuilder.addMesh`.
- Add `Torus` primitive.
- Add ``MeshBuilder.addMesh``.
- Add ``Torus`` primitive.
- Lazy volume loading support.
- [Breaking] ``Viewer.loadVolumeFromUrl`` signature change.
- ``loadVolumeFromUrl(url, format, isBinary, isovalues, entryId)`` => ``loadVolumeFromUrl({ url, format, isBinary }, isovalues, { entryId, isLazy })``
@@ -65,12 +152,12 @@ Note that since we don't clearly distinguish between a public and private interf
- Support for ``ColorTheme.palette`` designed for providing gradient-like coloring.
### Changed
- [Breaking] The `zip` function is now asynchronous and expects a `RuntimeContext`. Also added `Zip()` returning a `Task`.
- [Breaking] The ``zip`` function is now asynchronous and expects a ``RuntimeContext``. Also added ``Zip()`` returning a ``Task``.
- [Breaking] Add ``CubeGridFormat`` in ``alpha-orbitals`` extension.
## [v2.0.2] - 2021-03-29
### Added
- `Canvas3D.getRenderObjects`.
- ``Canvas3D.getRenderObjects``.
- [WIP] Animate state interpolating, including model trajectories
### Changed

View File

@@ -2,11 +2,11 @@ audit.block_doi
database_code.depnum_ccdc_archive
database_code.depnum_ccdc_fiz
database_code.ICSD
database_code.MDF
database_code.NBS
database_code.CSD
database_code.COD
database_code.icsd
database_code.mdf
database_code.nbs
database_code.csd
database_code.cod
chemical.name_systematic
chemical.name_common
@@ -24,8 +24,8 @@ atom_type_scat.dispersion_imag
atom_type_scat.source
space_group.crystal_system
space_group.name_H-M_full
space_group.IT_number
space_group.name_h-m_full
space_group.it_number
space_group_symop.operation_xyz
cell.length_a
@@ -35,14 +35,14 @@ cell.angle_alpha
cell.angle_beta
cell.angle_gamma
cell.volume
cell.formula_units_Z
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.u_iso_or_equiv
atom_site.adp_type
atom_site.occupancy
atom_site.calc_flag
@@ -52,20 +52,13 @@ atom_site.disorder_group
atom_site.site_symmetry_multiplicity
atom_site_aniso.label
atom_site_aniso.U
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
atom_site_aniso.U_su
atom_site_aniso.U_11_su
atom_site_aniso.U_22_su
atom_site_aniso.U_33_su
atom_site_aniso.U_23_su
atom_site_aniso.U_13_su
atom_site_aniso.U_12_su
atom_site_aniso.u
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
1 audit.block_doi
2 database_code.depnum_ccdc_archive
3 database_code.depnum_ccdc_fiz
4 database_code.ICSD database_code.icsd
5 database_code.MDF database_code.mdf
6 database_code.NBS database_code.nbs
7 database_code.CSD database_code.csd
8 database_code.COD database_code.cod
9 chemical.name_systematic
10 chemical.name_common
11 chemical.melting_point
12 chemical_formula.moiety
24 cell.length_a
25 cell.length_b
26 cell.length_c
27 cell.angle_alpha
28 cell.angle_beta
29 cell.angle_gamma
30 cell.volume
31 cell.formula_units_Z cell.formula_units_z
35 atom_site.fract_y
36 atom_site.fract_z
37 atom_site.U_iso_or_equiv atom_site.u_iso_or_equiv
38 atom_site.adp_type
39 atom_site.occupancy
40 atom_site.calc_flag
41 atom_site.refinement_flags
42 atom_site.disorder_assembly
43 atom_site.disorder_group
44 atom_site.site_symmetry_multiplicity
45 atom_site_aniso.label
46 atom_site_aniso.U atom_site_aniso.u
47 atom_site_aniso.U_11 atom_site_aniso.u_11
48 atom_site_aniso.U_22 atom_site_aniso.u_22
52 atom_site_aniso.U_12 atom_site_aniso.u_12
53 atom_site_aniso.U_su geom_bond.atom_site_label_1
54 atom_site_aniso.U_11_su geom_bond.atom_site_label_2
55 atom_site_aniso.U_22_su geom_bond.distance
56 atom_site_aniso.U_33_su geom_bond.site_symmetry_1
57 atom_site_aniso.U_23_su geom_bond.site_symmetry_2
58 atom_site_aniso.U_13_su geom_bond.publ_flag
59 atom_site_aniso.U_12_su geom_bond.valence
60
61
geom_bond.distance
geom_bond.site_symmetry_1
geom_bond.site_symmetry_2
geom_bond.publ_flag
geom_bond.valence
62
63
64

22889
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "2.2.0-dev.1",
"version": "2.3.4",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -88,68 +88,68 @@
],
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^2.0.2",
"@graphql-codegen/cli": "^1.19.4",
"@graphql-codegen/time": "^2.0.2",
"@graphql-codegen/typescript": "^1.19.0",
"@graphql-codegen/typescript-graphql-files-modules": "^1.18.1",
"@graphql-codegen/typescript-graphql-request": "^2.0.3",
"@graphql-codegen/typescript-operations": "^1.17.12",
"@types/cors": "^2.8.8",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"@graphql-codegen/add": "^3.1.0",
"@graphql-codegen/cli": "^2.2.0",
"@graphql-codegen/time": "^3.1.0",
"@graphql-codegen/typescript": "^2.2.2",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.0",
"@graphql-codegen/typescript-graphql-request": "^4.1.4",
"@graphql-codegen/typescript-operations": "^2.1.6",
"@types/cors": "^2.8.12",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"benchmark": "^2.1.4",
"concurrently": "^5.3.0",
"cpx2": "^3.0.0",
"concurrently": "^6.3.0",
"cpx2": "^4.0.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^5.0.1",
"eslint": "^7.15.0",
"css-loader": "^6.3.0",
"eslint": "^7.32.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^9.0.1",
"graphql": "^15.4.0",
"http-server": "^0.12.3",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.3.2",
"node-sass": "^6.0.0",
"fs-extra": "^10.0.0",
"graphql": "^15.6.0",
"http-server": "^13.0.2",
"jest": "^27.2.4",
"mini-css-extract-plugin": "^2.3.0",
"node-sass": "^6.0.1",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"sass-loader": "^11.1.1",
"simple-git": "^2.25.0",
"sass-loader": "^12.1.0",
"simple-git": "^2.46.0",
"stream-browserify": "^3.0.0",
"style-loader": "^2.0.0",
"ts-jest": "^26.4.4",
"typescript": "^4.2.4",
"webpack": "^5.37.1",
"webpack-cli": "^4.7.0",
"style-loader": "^3.3.0",
"ts-jest": "^27.0.5",
"typescript": "^4.4.3",
"webpack": "^5.56.0",
"webpack-cli": "^4.8.0",
"webpack-version-file-plugin": "^0.4.0"
},
"dependencies": {
"@types/argparse": "^1.0.38",
"@types/benchmark": "^2.1.0",
"@types/compression": "1.7.0",
"@types/express": "^4.17.9",
"@types/jest": "^26.0.18",
"@types/node": "^14.14.11",
"@types/node-fetch": "^2.5.7",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/swagger-ui-dist": "3.30.0",
"argparse": "^1.0.10",
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/jest": "^27.0.2",
"@types/node": "^16.10.2",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.27",
"@types/react-dom": "^17.0.9",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"h264-mp4-encoder": "^1.0.12",
"immer": "^8.0.1",
"immer": "^9.0.6",
"immutable": "^3.8.2",
"node-fetch": "^2.6.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rxjs": "^6.6.6",
"swagger-ui-dist": "^3.37.2",
"tslib": "^2.1.0",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.0"
"node-fetch": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rxjs": "^7.3.1",
"swagger-ui-dist": "^3.52.3",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
}
}

View File

@@ -238,7 +238,7 @@ export class ViewportComponent extends PluginUIComponent {
pocketPreset = () => this.set(PocketPreset);
interactionsPreset = () => this.set(InteractionsPreset);
get showButtons () {
get showButtons() {
return this.plugin.config.get(ShowButtons);
}

View File

@@ -52,12 +52,22 @@
var collapseLeftPanel = getParam('collapse-left-panel', '[^&]+').trim() === '1';
var pdbProvider = getParam('pdb-provider', '[^&]+').trim().toLowerCase();
var emdbProvider = getParam('emdb-provider', '[^&]+').trim().toLowerCase();
var mapProvider = getParam('map-provider', '[^&]+').trim().toLowerCase();
var pixelScale = getParam('pixel-scale', '[^&]+').trim();
var pickScale = getParam('pick-scale', '[^&]+').trim();
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var viewer = new molstar.Viewer('app', {
layoutShowControls: !hideControls,
viewportShowExpand: false,
collapseLeftPanel: collapseLeftPanel,
pdbProvider: pdbProvider || 'pdbe',
emdbProvider: emdbProvider || 'pdbe',
volumeStreamingServer: (mapProvider || 'pdbe') === 'rcsb'
? 'https://maps.rcsb.org'
: 'https://www.ebi.ac.uk/pdbe/densities',
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
});
var snapshotId = getParam('snapshot-id', '[^&]+').trim();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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>
@@ -71,9 +71,11 @@ const DefaultViewerOptions = {
layoutShowLog: true,
layoutShowLeftPanel: true,
collapseLeftPanel: false,
disableAntialiasing: false,
pixelScale: 1,
enableWboit: true,
disableAntialiasing: PluginConfig.General.DisableAntialiasing.defaultValue,
pixelScale: PluginConfig.General.PixelScale.defaultValue,
pickScale: PluginConfig.General.PickScale.defaultValue,
pickPadding: PluginConfig.General.PickPadding.defaultValue,
enableWboit: PluginConfig.General.EnableWboit.defaultValue,
viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
@@ -130,6 +132,8 @@ export class Viewer {
config: [
[PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
[PluginConfig.General.PixelScale, o.pixelScale],
[PluginConfig.General.PickScale, o.pickScale],
[PluginConfig.General.PickPadding, o.pickPadding],
[PluginConfig.General.EnableWboit, o.enableWboit],
[PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
[PluginConfig.Viewport.ShowControls, o.viewportShowControls],
@@ -325,7 +329,7 @@ export class Viewer {
}
handleResize() {
this.plugin.layout.events.updated.next();
this.plugin.layout.events.updated.next(void 0);
}
}

View File

@@ -19,7 +19,7 @@ import { ensureDataAvailable, readCCD } from './util';
function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
const ionNames: string[] = [];
for (const k in ccd) {
const {chem_comp} = ccd[k];
const { chem_comp } = ccd[k];
if (chem_comp.name.value(0).toUpperCase().includes(' ION')) {
ionNames.push(chem_comp.id.value(0));
}
@@ -54,20 +54,20 @@ async function run(out: string, forceDownload = false) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Extract and save IonNames from CCD.'
});
parser.addArgument('out', {
parser.add_argument('out', {
help: 'Generated file output path.'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
interface Args {
out: string,
forceDownload?: boolean,
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(args.out, args.forceDownload);

View File

@@ -171,7 +171,7 @@ async function createBonds(
pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant
});
const bondDatabase = Database.ofTables(
const bondDatabase = Database.ofTables(
CCB_TABLE_NAME,
{ chem_comp_bond: mmCIF_chemCompBond_schema },
{ chem_comp_bond: bondTable }
@@ -265,21 +265,21 @@ const CCB_TABLE_NAME = 'CHEM_COMP_BONDS';
const CCA_TABLE_NAME = 'CHEM_COMP_ATOMS';
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
});
parser.addArgument('out', {
parser.add_argument('out', {
help: 'Generated file output path.'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of CCD and PVCD.'
});
parser.addArgument([ '--binary', '-b' ], {
action: 'storeTrue',
parser.add_argument('--binary', '-b', {
action: 'store_true',
help: 'Output as BinaryCIF.'
});
parser.addArgument(['--ccaOut', '-a'], {
parser.add_argument('--ccaOut', '-a', {
help: 'Optional generated file output path for chem_comp_atom data.',
required: false
});
@@ -289,6 +289,6 @@ interface Args {
binary?: boolean,
ccaOut?: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(args.out, args.binary, args.forceDownload, args.ccaOut);

View File

@@ -37,20 +37,20 @@ function run(args: Args) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Convert any CIF file to a BCIF file'
});
parser.addArgument([ 'src' ], {
parser.add_argument('src', {
help: 'Source CIF path'
});
parser.addArgument([ 'out' ], {
parser.add_argument('out', {
help: 'Output BCIF path'
});
parser.addArgument([ '-c', '--config' ], {
parser.add_argument('-c', '--config', {
help: 'Optional encoding strategy/precision config path',
required: false
});
parser.addArgument([ '-f', '--filter' ], {
parser.add_argument('-f', '--filter', {
help: 'Optional filter whitelist/blacklist path',
required: false
});
@@ -61,7 +61,7 @@ interface Args {
config?: string
filter?: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
if (args) {
run(args);

View File

@@ -124,15 +124,15 @@ async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
const csvFile = parsed.result;
const fieldNamesCol = csvFile.table.getColumn('0');
if (!fieldNamesCol) throw 'error getting fields columns';
if (!fieldNamesCol) throw new Error('error getting fields columns');
const fieldNames = fieldNamesCol.toStringArray();
const filter: Filter = {};
fieldNames.forEach((name, i) => {
const [ category, field ] = name.split('.');
const [category, field] = name.split('.');
// console.log(category, field)
if (!filter[ category ]) filter[ category ] = {};
filter[ category ][ field ] = true;
if (!filter[category]) filter[category] = {};
filter[category][field] = true;
});
return filter;
}
@@ -178,44 +178,44 @@ const CIF_CORE_ATTR_PATH = `${DIC_DIR}/templ_attr.cif`;
const CIF_CORE_ATTR_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/templ_attr.cif';
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Create schema from mmcif dictionary (v50 plus IHM and entity_branch extensions, downloaded from wwPDB)'
});
parser.addArgument([ '--preset', '-p' ], {
defaultValue: '',
parser.add_argument('--preset', '-p', {
default: '',
choices: ['', 'mmCIF', 'CCD', 'BIRD', 'CifCore'],
help: 'Preset name'
});
parser.addArgument([ '--name', '-n' ], {
defaultValue: '',
parser.add_argument('--name', '-n', {
default: '',
help: 'Schema name'
});
parser.addArgument([ '--out', '-o' ], {
parser.add_argument('--out', '-o', {
help: 'Generated schema output path, if not given printed to stdout'
});
parser.addArgument([ '--targetFormat', '-tf' ], {
defaultValue: 'typescript-molstar',
parser.add_argument('--targetFormat', '-tf', {
default: 'typescript-molstar',
choices: ['typescript-molstar', 'json-internal'],
help: 'Target format'
});
parser.addArgument([ '--dicPath', '-d' ], {
defaultValue: '',
parser.add_argument('--dicPath', '-d', {
default: '',
help: 'Path to dictionary'
});
parser.addArgument([ '--fieldNamesPath', '-fn' ], {
defaultValue: '',
parser.add_argument('--fieldNamesPath', '-fn', {
default: '',
help: 'Field names to include'
});
parser.addArgument([ '--forceDicDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDicDownload', '-f', {
action: 'store_true',
help: 'Force download of dictionaries'
});
parser.addArgument([ '--moldataImportPath', '-mip' ], {
defaultValue: 'molstar/lib/mol-data',
parser.add_argument('--moldataImportPath', '-mip', {
default: 'molstar/lib/mol-data',
help: 'mol-data import path (for typescript target only)'
});
parser.addArgument([ '--addAliases', '-aa' ], {
action: 'storeTrue',
parser.add_argument('--addAliases', '-aa', {
action: 'store_true',
help: 'Add field name/path aliases'
});
interface Args {
@@ -230,7 +230,7 @@ interface Args {
moldataImportPath: string
addAliases: boolean
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
const FORCE_DIC_DOWNLOAD = args.forceDicDownload;

View File

@@ -34,6 +34,8 @@ export function getFieldType(type: string, description: string, values?: string[
case 'seq-one-letter-code':
case 'author':
case 'orcid_id':
case 'pdbx_PDB_obsoleted_db_id':
case 'pdbx_related_db_id':
case 'sequence_dep':
case 'pdb_id':
case 'emd_id':
@@ -79,7 +81,7 @@ export function getFieldType(type: string, description: string, values?: string[
case 'List(Real,Real)':
case 'List(Real,Real,Real,Real)':
case 'Date':
case 'Datetime':
case 'DateTime':
case 'Tag':
case 'Implied':
return wrapContainer('str', ',', description, container);
@@ -187,7 +189,7 @@ function getContainer(d: Data.CifFrame, imports: Imports, ctx: FrameData) {
function getCode(d: Data.CifFrame, imports: Imports, ctx: FrameData): [string, string[] | undefined, string | undefined ] | undefined {
const code = getField('item_type', 'code', d, imports, ctx) || getField('type', 'contents', d, imports, ctx);
if (code) {
return [ code.str(0), getEnums(d, imports, ctx), getContainer(d, imports, ctx) ];
return [code.str(0), getEnums(d, imports, ctx), getContainer(d, imports, ctx)];
} else {
console.log(`item_type.code or type.contents not found for '${d.header}'`);
}
@@ -232,29 +234,26 @@ const FORCE_INT_FIELDS = [
'_struct_sheet_range.end_auth_seq_id',
];
/**
* Note that name and mapped name must share a prefix. This is not always the case in
* the cifCore dictionary, but for downstream code to work a container field with the
* same prefix as the member fields must be given here and in the field names filter
* list.
*/
const FORCE_MATRIX_FIELDS_MAP: { [k: string]: string } = {
'atom_site_aniso.U_11': 'U',
'atom_site_aniso.U_22': 'U',
'atom_site_aniso.U_33': 'U',
'atom_site_aniso.U_23': 'U',
'atom_site_aniso.U_13': 'U',
'atom_site_aniso.U_12': 'U',
'atom_site_aniso.U_11_su': 'U_su',
'atom_site_aniso.U_22_su': 'U_su',
'atom_site_aniso.U_33_su': 'U_su',
'atom_site_aniso.U_23_su': 'U_su',
'atom_site_aniso.U_13_su': 'U_su',
'atom_site_aniso.U_12_su': 'U_su',
'atom_site_aniso.u_11': 'u', // is matrix_u in the the dic
'atom_site_aniso.u_22': 'u',
'atom_site_aniso.u_33': 'u',
'atom_site_aniso.u_23': 'u',
'atom_site_aniso.u_13': 'u',
'atom_site_aniso.u_12': 'u',
};
const FORCE_MATRIX_FIELDS = Object.keys(FORCE_MATRIX_FIELDS_MAP);
const EXTRA_ALIASES: Database['aliases'] = {
'atom_site_aniso.U': [
'atom_site_anisotrop_U'
],
'atom_site_aniso.U_su': [
'atom_site_aniso_U_esd',
'atom_site_anisotrop_U_esd',
'atom_site_aniso.matrix_u': [
'atom_site_anisotrop_U',
'atom_site_aniso.U'
],
};
@@ -317,7 +316,7 @@ export function generateSchema(frames: CifFrame[], imports: Imports = new Map())
frames.forEach(d => {
// category definitions in mmCIF start with '_' and don't include a '.'
// category definitions in cifCore don't include a '.'
if (d.header[0] === '_' || d.header.includes('.')) return;
if (d.header[0] === '_' || d.header.includes('.')) return;
const categoryName = d.header.toLowerCase();
// console.log(d.header, d.categoryNames, d.categories)
let descriptionField: Data.CifField | undefined;
@@ -372,7 +371,7 @@ export function generateSchema(frames: CifFrame[], imports: Imports = new Map())
const parent_name = item_linked.getField('parent_name');
if (child_name && parent_name) {
for (let i = 0; i < item_linked.rowCount; ++i) {
const childName = child_name.str(i);
const childName: string = child_name.str(i);
const parentName = parent_name.str(i);
if (childName in links && links[childName] !== parentName) {
console.log(`${childName} linked to ${links[childName]}, ignoring link to ${parentName}`);

View File

@@ -8,7 +8,7 @@ import { Database, Filter, Column } from './schema';
import { indentString } from '../../../mol-util/string';
import { FieldPath } from '../../../mol-io/reader/cif/schema';
function header (name: string, info: string, moldataImportPath: string) {
function header(name: string, info: string, moldataImportPath: string) {
return `/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
@@ -22,7 +22,7 @@ import { Database, Column } from '${moldataImportPath}/db';
import Schema = Column.Schema;`;
}
function footer (name: string) {
function footer(name: string) {
return `
export type ${name}_Schema = typeof ${name}_Schema;
export interface ${name}_Database extends Database<${name}_Schema> {};`;
@@ -89,7 +89,7 @@ function doc(description: string, spacesCount: number) {
].join('\n');
}
export function generate (name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
export function generate(name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
const codeLines: string[] = [];
if (fields) {
@@ -128,7 +128,7 @@ export function generate (name: string, info: string, schema: Database, fields:
codeLines.push('');
codeLines.push(`export const ${name}_Aliases = {`);
Object.keys(schema.aliases).forEach(path => {
const [ table, columnName ] = path.split('.');
const [table, columnName] = path.split('.');
if (fields && !fields[table]) return;
if (fields && !fields[table][columnName]) return;

View File

@@ -10,8 +10,8 @@ export function parseImportGet(s: string): Import[] {
// [{'save':hi_ang_Fox_coeffs 'file':templ_attr.cif} {'save':hi_ang_Fox_c0 'file':templ_enum.cif}]
// [{"file":'templ_enum.cif' "save":'H_M_ref'}]
return s.trim().substring(2, s.length - 2).split(/}[ \n\t]*{/g).map(s => {
const save = s.match(/('save'|"save"):([^ \t\n]+)/);
const file = s.match(/('file'|"file"):([^ \t\n]+)/);
const save = s.match(/('save'|"save"):([^ \t\n{}]+)/);
const file = s.match(/('file'|"file"):([^ \t\n{}]+)/);
return {
save: save ? save[0].substr(7).replace(/['"]/g, '') : undefined,
file: file ? file[0].substr(7).replace(/['"]/g, '') : undefined

View File

@@ -51,13 +51,13 @@ export function ListCol(subType: 'int' | 'str' | 'float' | 'coord', separator: s
export type Filter = { [ table: string ]: { [ column: string ]: true } }
export function mergeFilters (...filters: Filter[]) {
export function mergeFilters(...filters: Filter[]) {
const n = filters.length;
const mergedFilter: Filter = {};
const fields: Map<string, number> = new Map();
filters.forEach(filter => {
Object.keys(filter).forEach(category => {
Object.keys(filter[ category ]).forEach(field => {
Object.keys(filter[category]).forEach(field => {
const key = `${category}.${field}`;
const value = fields.get(key) || 0;
fields.set(key, value + 1);

View File

@@ -70,21 +70,21 @@ export const LipidNames = new Set(${lipidNames.replace(/"/g, "'").replace(/,/g,
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Create lipid params (from martini lipids itp)'
});
parser.addArgument([ '--out', '-o' ], {
parser.add_argument('--out', '-o', {
help: 'Generated lipid params output path, if not given printed to stdout'
});
parser.addArgument([ '--forceDownload', '-f' ], {
action: 'storeTrue',
parser.add_argument('--forceDownload', '-f', {
action: 'store_true',
help: 'Force download of martini lipids itp'
});
interface Args {
out: string
forceDownload: boolean
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
const FORCE_DOWNLOAD = args.forceDownload;

View File

@@ -63,7 +63,7 @@ export function printSecStructure(model: Model) {
const count = residues._rowCount;
let rI = 0;
while (rI < count) {
let start = rI;
const start = rI;
while (rI < count && key[start] === key[rI]) rI++;
rI--;
@@ -230,21 +230,21 @@ async function runFile(filename: string, args: Args) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Print info about a structure, mainly to test and showcase the mol-model module'
});
parser.addArgument(['--download', '-d'], { help: 'Pdb entry id' });
parser.addArgument(['--file', '-f'], { help: 'filename' });
parser.add_argument('--download', '-d', { help: 'Pdb entry id' });
parser.add_argument('--file', '-f', { help: 'filename' });
parser.addArgument(['--models'], { help: 'print models info', action: 'storeTrue' });
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(['--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' });
parser.add_argument('--models', { help: 'print models info', action: 'store_true' });
parser.add_argument('--seq', { help: 'print sequence', action: 'store_true' });
parser.add_argument('--units', { help: 'print units', action: 'store_true' });
parser.add_argument('--sym', { help: 'print symmetry', action: 'store_true' });
parser.add_argument('--rings', { help: 'print rings', action: 'store_true' });
parser.add_argument('--intraBonds', { help: 'print intra unit bonds', action: 'store_true' });
parser.add_argument('--interBonds', { help: 'print inter unit bonds', action: 'store_true' });
parser.add_argument('--mod', { help: 'print modified residues', action: 'store_true' });
parser.add_argument('--sec', { help: 'print secoundary structure', action: 'store_true' });
interface Args {
download?: string,
file?: string,
@@ -260,7 +260,7 @@ interface Args {
mod?: boolean,
sec?: boolean,
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
if (args.download) runDL(args.download, args);
else if (args.file) runFile(args.file, args);

View File

@@ -38,7 +38,7 @@ function print(volume: Volume) {
}
async function doMesh(volume: Volume, filename: string) {
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) } )).run();
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, volume, Theme.createEmpty(), { isoValue: Volume.IsoValue.absolute(1.5) })).run();
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
// Export the mesh in OBJ format.
@@ -75,13 +75,13 @@ async function run(url: string, meshFilename: string) {
}
const parser = new argparse.ArgumentParser({
addHelp: true,
add_help: true,
description: 'Info about VolumeData from mol-model module'
});
parser.addArgument([ '--emdb', '-e' ], {
parser.add_argument('--emdb', '-e', {
help: 'EMDB id, for example 8116',
});
parser.addArgument([ '--mesh' ], {
parser.add_argument('--mesh', {
help: 'Mesh filename',
required: true
});
@@ -89,6 +89,6 @@ interface Args {
emdb?: string,
mesh: string
}
const args: Args = parser.parseArgs();
const args: Args = parser.parse_args();
run(`https://ds.litemol.org/em/emd-${args.emdb}/cell?detail=4`, args.mesh);

View File

@@ -38,7 +38,7 @@ type MappingRow = Table.Row<S.mapping>;
function writeDomain(enc: CifWriter.Encoder, domain: DomainAnnotation | undefined) {
if (!domain) return;
enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () => CifWriter.Category.ofTable(domain.domains) });
enc.writeCategory({ name: `pdbx_${domain.name}_domain_annotation`, instance: () => CifWriter.Category.ofTable(domain.domains) });
enc.writeCategory({ name: `pdbx_${domain.name}_domain_mapping`, instance: () => CifWriter.Category.ofTable(domain.mappings) });
}

View File

@@ -15,7 +15,7 @@ async function getMappings(id: string) {
};
let PORT = process.env.port || 1338;
const PORT = process.env.port || 1338;
const app = express();

View File

@@ -105,7 +105,7 @@ class LightingDemo {
...this.plugin.canvas3d!.props.postprocessing,
...props.postprocessing
},
}});
} });
}
async load({ url, format = 'mmcif', isBinary = true, assemblyId = '' }: LoadParams, radius: number, bias: number) {

View File

@@ -250,7 +250,7 @@ class MolStarProteopediaWrapper {
setBackground(color: number) {
if (!this.plugin.canvas3d) return;
const renderer = this.plugin.canvas3d.props.renderer;
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {

View File

@@ -39,22 +39,34 @@ interface ANVILContext {
};
export const ANVILParams = {
numberOfSpherePoints: PD.Numeric(140, { min: 35, max: 700, step: 1 }, { description: 'Number of spheres/directions to test for membrane placement. Original value is 350.' }),
numberOfSpherePoints: PD.Numeric(175, { min: 35, max: 700, step: 1 }, { description: 'Number of spheres/directions to test for membrane placement. Original value is 350.' }),
stepSize: PD.Numeric(1, { min: 0.25, max: 4, step: 0.25 }, { description: 'Thickness of membrane slices that will be tested' }),
minThickness: PD.Numeric(20, { min: 10, max: 30, step: 1}, { description: 'Minimum membrane thickness used during refinement' }),
maxThickness: PD.Numeric(40, { min: 30, max: 50, step: 1}, { description: 'Maximum membrane thickness used during refinement' }),
minThickness: PD.Numeric(20, { min: 10, max: 30, step: 1 }, { description: 'Minimum membrane thickness used during refinement' }),
maxThickness: PD.Numeric(40, { min: 30, max: 50, step: 1 }, { description: 'Maximum membrane thickness used during refinement' }),
asaCutoff: PD.Numeric(40, { min: 10, max: 100, step: 1 }, { description: 'Relative ASA cutoff above which residues will be considered' }),
adjust: PD.Numeric(14, { min: 0, max: 30, step: 1 }, { description: 'Minimum length of membrane-spanning regions (original values: 14 for alpha-helices and 5 for beta sheets). Set to 0 to not optimize membrane thickness.' })
adjust: PD.Numeric(14, { min: 0, max: 30, step: 1 }, { description: 'Minimum length of membrane-spanning regions (original values: 14 for alpha-helices and 5 for beta sheets). Set to 0 to not optimize membrane thickness.' }),
tmdetDefinition: PD.Boolean(false, { description: `Use TMDET's classification of membrane-favoring amino acids. TMDET's classification shows better performance on porins and other beta-barrel structures.` })
};
export type ANVILParams = typeof ANVILParams
export type ANVILProps = PD.Values<ANVILParams>
/** ANVIL-specific (not general) definition of membrane-favoring amino acids */
const ANVIL_DEFINITION = new Set(['ALA', 'CYS', 'GLY', 'HIS', 'ILE', 'LEU', 'MET', 'PHE', 'SER', 'TRP', 'VAL']);
/** TMDET-specific (not general) definition of membrane-favoring amino acids */
const TMDET_DEFINITION = new Set(['LEU', 'ILE', 'VAL', 'PHE', 'MET', 'GLY', 'TRP', 'TYR']);
/**
* Implements:
* Membrane positioning for high- and low-resolution protein structures through a binary classification approach
* Guillaume Postic, Yassine Ghouzam, Vincent Guiraud, and Jean-Christophe Gelly
* Protein Engineering, Design & Selection, 2015, 15
* doi: 10.1093/protein/gzv063
*
* ANVIL is derived from TMDET, the corresponding classification of hydrophobic amino acids is provided as optional parameter:
* Gabor E. Tusnady, Zsuzsanna Dosztanyi and Istvan Simon
* Transmembrane proteins in the Protein Data Bank: identification and classification
* Bioinformatics, 2004, 2964-2972
* doi: 10.1093/bioinformatics/bth340
*/
export function computeANVIL(structure: Structure, props: ANVILProps) {
return Task.create('Compute Membrane Orientation', async runtime => {
@@ -87,6 +99,11 @@ async function initialize(structure: Structure, props: ANVILProps, accessibleSur
const offsets = new Array<number>();
const exposed = new Array<number>();
const hydrophobic = new Array<boolean>();
const definition = props.tmdetDefinition ? TMDET_DEFINITION : ANVIL_DEFINITION;
function isPartOfEntity(l: StructureElement.Location): boolean {
return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.valueKind(l.unit.residueIndex[l.element]) === 0;
}
const vec = v3zero();
for (let i = 0, il = structure.units.length; i < il; ++i) {
@@ -98,8 +115,8 @@ async function initialize(structure: Structure, props: ANVILProps, accessibleSur
const eI = elements[j];
l.element = eI;
// consider only amino acids
if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein) {
// consider only amino acids in chains
if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein || !isPartOfEntity(l)) {
continue;
}
@@ -121,7 +138,7 @@ async function initialize(structure: Structure, props: ANVILProps, accessibleSur
offsets.push(structure.serialMapping.getSerialIndex(l.unit, l.element));
if (AccessibleSurfaceArea.getValue(l, accessibleSurfaceArea) / MaxAsa[label_comp_id(l)] > asaCutoff) {
exposed.push(structure.serialMapping.getSerialIndex(l.unit, l.element));
hydrophobic.push(isHydrophobic(label_comp_id(l)));
hydrophobic.push(isHydrophobic(definition, label_comp_id(l)));
}
}
}
@@ -166,7 +183,7 @@ export async function calculate(runtime: RuntimeContext, structure: Structure, p
}
const normalVector = v3zero();
const center = v3zero();
const center = v3zero();
v3sub(normalVector, membrane.planePoint1, membrane.planePoint2);
v3normalize(normalVector, normalVector);
@@ -344,7 +361,7 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array
// collect all residues in membrane layer
for (let k = 0, kl = offsets.length; k < kl; k++) {
const unit = units[unitIndices[offsets[k]]];
if (!Unit.isAtomic(unit)) throw 'Property only available for atomic models.';
if (!Unit.isAtomic(unit)) notAtomic();
const elementIndex = elementIndices[offsets[k]];
authAsymId = unit.model.atomicHierarchy.chains.auth_asym_id.value(unit.chainIndex[elementIndex]);
@@ -365,7 +382,7 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array
for (let k = 0, kl = offsets.length; k < kl; k++) {
const unit = units[unitIndices[offsets[k]]];
if (!Unit.isAtomic(unit)) throw 'Property only available for atomic models.';
if (!Unit.isAtomic(unit)) notAtomic();
const elementIndex = elementIndices[offsets[k]];
authAsymId = unit.model.atomicHierarchy.chains.auth_asym_id.value(unit.chainIndex[elementIndex]);
@@ -387,7 +404,7 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array
}
lastAuthSeqId = authSeqId;
endOffset = k;
} else {
} else {
lastAuthSeqId++;
endOffset++;
}
@@ -428,6 +445,10 @@ function membraneSegments(ctx: ANVILContext, membrane: MembraneCandidate): Array
return refinedSegments;
}
function notAtomic(): never {
throw new Error('Property only available for atomic models.');
}
/** Filter for membrane residues and calculate the final extent of the membrane layer */
function adjustExtent(ctx: ANVILContext, membrane: MembraneCandidate, centroid: Vec3): number {
const { offsets, structure } = ctx;
@@ -455,11 +476,11 @@ function adjustExtent(ctx: ANVILContext, membrane: MembraneCandidate, centroid:
}
function qValue(currentStats: HphobHphil, initialStats: HphobHphil): number {
if(initialStats.hphob < 1) {
if (initialStats.hphob < 1) {
initialStats.hphob = 0.1;
}
if(initialStats.hphil < 1) {
if (initialStats.hphil < 1) {
initialStats.hphil += 1;
}
@@ -484,7 +505,7 @@ function generateSpherePoints(ctx: ANVILContext, numberOfSpherePoints: number):
const { centroid, extent } = ctx;
const points = [];
let oldPhi = 0, h, theta, phi;
for(let k = 1, kl = numberOfSpherePoints + 1; k < kl; k++) {
for (let k = 1, kl = numberOfSpherePoints + 1; k < kl; k++) {
h = -1 + 2 * (k - 1) / (2 * numberOfSpherePoints - 1);
theta = Math.acos(h);
phi = (k === 1 || k === numberOfSpherePoints) ? 0 : (oldPhi + 3.6 / Math.sqrt(2 * numberOfSpherePoints * (1 - h * h))) % (2 * Math.PI);
@@ -566,11 +587,9 @@ namespace HphobHphil {
}
}
/** ANVIL-specific (not general) definition of membrane-favoring amino acids */
const HYDROPHOBIC_AMINO_ACIDS = new Set(['ALA', 'CYS', 'GLY', 'HIS', 'ILE', 'LEU', 'MET', 'PHE', 'SER', 'TRP', 'VAL']);
/** Returns true if ANVIL considers this as amino acid that favors being embedded in a membrane */
export function isHydrophobic(label_comp_id: string): boolean {
return HYDROPHOBIC_AMINO_ACIDS.has(label_comp_id);
/** Returns true if the definition considers this as membrane-favoring amino acid */
export function isHydrophobic(definition: Set<string>, label_comp_id: string): boolean {
return definition.has(label_comp_id);
}
/** Accessible surface area used for normalization. ANVIL uses 'Total-Side REL' values from NACCESS, from: Hubbard, S. J., & Thornton, J. M. (1993). naccess. Computer Program, Department of Biochemistry and Molecular Biology, University College London, 2(1). */

View File

@@ -52,7 +52,7 @@ export const ANVILMembraneOrientation = PluginBehavior.create<{ autoAttach: bool
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
@@ -150,7 +150,7 @@ export const MembraneOrientationPreset = StructureRepresentationPresetProvider({
params: () => StructureRepresentationPresetProvider.CommonParams,
async apply(ref, params, plugin) {
const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
const structure = structureCell?.obj?.data;
const structure = structureCell?.obj?.data;
if (!structureCell || !structure) return {};
if (!MembraneOrientationProvider.get(structure).value) {

View File

@@ -33,7 +33,7 @@ export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Valu
if (ctx.structure && info) {
const colors = distinctColors(info.packingsCount);
let hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hcl = Hcl.fromColor(Hcl(), colors[info.packingIndex]);
const hue = [Math.max(0, hcl[0] - 35), Math.min(360, hcl[0] + 35)] as [number, number];
@@ -48,7 +48,7 @@ export function CellPackGenerateColorTheme(ctx: ThemeDataContext, props: PD.Valu
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800, maxCount: 75
}
}}, { minLabel: 'Min', maxLabel: 'Max' });
} }, { minLabel: 'Min', maxLabel: 'Max' });
legend = palette.legend;
const modelColor = new Map<number, Color>();
for (let i = 0, il = models.length; i < il; ++i) {

View File

@@ -49,7 +49,7 @@ function ResampleControlPoints(points: NumberArray, segmentLength: number) {
// controlPoints.Insert(0, controlPoints[0] + (controlPoints[0] - controlPoints[1]) / 2.0f);
// controlPoints.Add(controlPoints[nP - 1] + (controlPoints[nP - 1] - controlPoints[nP - 2]) / 2.0f);
let resampledControlPoints: Vec3[] = [];
const resampledControlPoints: Vec3[] = [];
// resampledControlPoints.Add(controlPoints[0]);
// resampledControlPoints.Add(controlPoints[1]);
@@ -111,7 +111,7 @@ function GetSmoothNormals(points: Vec3[]) {
let p1 = points[1];
let p2 = points[2];
const p21 = Vec3.sub(tmpV1, p2, p1);
const p01 = Vec3.sub(tmpV2, p0, p1);
const p01 = Vec3.sub(tmpV2, p0, p1);
const p0121 = Vec3.cross(tmpV3, p01, p21);
Vec3.normalize(prevV, p0121);
smoothNormals.push(Vec3.clone(prevV));
@@ -179,7 +179,7 @@ function GetMiniFrame(points: Vec3[], normals: Vec3[]) {
const v1t = Vec3.scale(mfTmpV5, v1, (2.0 / c1) * Vec3.dot(v1, frames[i].t));
const tan_L_i = Vec3.sub(mfTmpV6, frames[i].t, v1t);
// # compute reflection vector of R_2
const v2 = Vec3.sub(mfTmpV7, t2, tan_L_i);
const v2 = Vec3.sub(mfTmpV7, t2, tan_L_i);
const c2 = Vec3.dot(v2, v2);
// compute r_(i+1) = R_2 * r_i^L
const v2l = Vec3.scale(mfTmpV8, v1, (2.0 / c2) * Vec3.dot(v2, ref_L_i));
@@ -195,7 +195,7 @@ export function getMatFromResamplePoints(points: NumberArray, segmentLength: num
let new_points: Vec3[] = [];
if (resample) new_points = ResampleControlPoints(points, segmentLength);
else {
for (let idx = 0; idx < points.length / 3; ++idx){
for (let idx = 0; idx < points.length / 3; ++idx) {
new_points.push(Vec3.fromArray(Vec3.zero(), points, idx * 3));
}
}
@@ -211,7 +211,7 @@ export function getMatFromResamplePoints(points: NumberArray, segmentLength: num
if (d >= segmentLength) {
// use twist or random?
const quat = Quat.rotationTo(Quat.zero(), Vec3.create(0, 0, 1), frames[i].t); // Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),new_normal[i]);//Quat.rotationTo(Quat.zero(), Vec3.create(0,0,1),direction);new_normal
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random() * 3.60 ); // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
const rq = Quat.setAxisAngle(Quat.zero(), frames[i].t, Math.random() * 3.60); // Quat.setAxisAngle(Quat.zero(),direction, Math.random()*3.60 );//Quat.identity();//
const m = Mat4.fromQuat(Mat4.zero(), Quat.multiply(Quat.zero(), rq, quat)); // Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),quat1,quat2));//Mat4.fromQuat(Mat4.zero(),quat);//Mat4.identity();//Mat4.fromQuat(Mat4.zero(),Quat.multiply(Quat.zero(),rq,quat));
// let pos:Vec3 = Vec3.add(Vec3.zero(),pti1,pti)
// pos = Vec3.scale(pos,pos,1.0/2.0);

View File

@@ -73,9 +73,9 @@ export interface Ingredient {
export interface IngredientSource {
pdb: string;
bu?: string; /** biological unit e.g AU,BU1,etc.. */
bu?: string; /** biological unit e.g AU,BU1,etc.. */
selection?: string; /** NGL selection or :A or :B etc.. */
model?: string; /** model number e.g 0,1,2... */
model?: string; /** model number e.g 0,1,2... */
transform: {
center: boolean;
translate?: Vec3;

View File

@@ -46,7 +46,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
const modelIndex = (ingredient.source.model) ? parseInt(ingredient.source.model) : 0;
const surface = (ingredient.ingtype) ? (ingredient.ingtype === 'transmembrane') : false;
let trajectory = trajCache.get(id);
let assets: Asset.Wrapper[] = [];
const assets: Asset.Wrapper[] = [];
if (!trajectory) {
if (file) {
if (file.name.endsWith('.cif')) {
@@ -68,7 +68,7 @@ async function getModel(plugin: PluginContext, id: string, ingredient: Ingredien
throw new Error(`unsupported file type '${file.name}'`);
}
} else if (id.match(/^[1-9][a-zA-Z0-9]{3,3}$/i)) {
if (surface){
if (surface) {
try {
const data = await getFromOPM(plugin, id, assetManager);
assets.push(data.asset);
@@ -108,7 +108,7 @@ async function getStructure(plugin: PluginContext, model: Model, source: Ingredi
structure = await plugin.runTask(StructureSymmetry.buildAssembly(structure, assembly));
}
let query;
if (source.selection){
if (source.selection) {
const asymIds: string[] = source.selection.replace(' ', '').replace(':', '').split('or');
query = MS.struct.modifier.union([
MS.struct.generator.atomGroups({
@@ -157,9 +157,9 @@ function getCurveTransforms(ingredient: Ingredient) {
const n = ingredient.nbCurve || 0;
const instances: Mat4[] = [];
let segmentLength = 3.4;
if (ingredient.uLength){
if (ingredient.uLength) {
segmentLength = ingredient.uLength;
} else if (ingredient.radii){
} else if (ingredient.radii) {
segmentLength = ingredient.radii[0].radii
? ingredient.radii[0].radii[0] * 2.0
: 3.4;
@@ -177,7 +177,7 @@ function getCurveTransforms(ingredient: Ingredient) {
continue;
}
// test for resampling
let distance: number = Vec3.distance(_points[0], _points[1]);
const distance: number = Vec3.distance(_points[0], _points[1]);
if (distance >= segmentLength + 2.0) {
console.info(distance);
resampling = true;
@@ -196,7 +196,7 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
for (let i = 0, il = transforms.length; i < il; ++i) {
const id = `${i + 1}`;
const op = SymmetryOperator.create(id, transforms[i], { assembly: { id, operId: i, operList: [ id ] } });
const op = SymmetryOperator.create(id, transforms[i], { assembly: { id, operId: i, operList: [id] } });
for (const unit of units) {
builder.addWithOperator(unit, op);
}
@@ -333,7 +333,7 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
structure = await getCurve(plugin, name, ingredient, getCurveTransforms(ingredient), model);
} else {
let bu: string|undefined = source.bu ? source.bu : undefined;
if (bu){
if (bu) {
if (bu === 'AU') {
bu = undefined;
} else {
@@ -343,22 +343,22 @@ async function getIngredientStructure(plugin: PluginContext, ingredient: Ingredi
structure = await getStructure(plugin, model, source, { assembly: bu });
// transform with offset and pcp
let legacy: boolean = true;
if (ingredient.offset || ingredient.principalAxis){
if (ingredient.offset || ingredient.principalAxis) {
legacy = false;
const structureMean = getStructureMean(structure);
Vec3.negate(structureMean, structureMean);
const m1: Mat4 = Mat4.identity();
Mat4.setTranslation(m1, structureMean);
structure = Structure.transform(structure, m1);
if (ingredient.offset){
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())){
if (ingredient.offset) {
if (!Vec3.exactEquals(ingredient.offset, Vec3.zero())) {
const m: Mat4 = Mat4.identity();
Mat4.setTranslation(m, ingredient.offset);
structure = Structure.transform(structure, m);
}
}
if (ingredient.principalAxis){
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)){
if (ingredient.principalAxis) {
if (!Vec3.exactEquals(ingredient.principalAxis, Vec3.unitZ)) {
const q: Quat = Quat.identity();
Quat.rotationTo(q, ingredient.principalAxis, Vec3.unitZ);
const m: Mat4 = Mat4.fromQuat(Mat4.zero(), q);
@@ -387,7 +387,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
structures.push(ingredientStructure.structure);
assets.push(...ingredientStructure.assets);
const c = ingredients[iName].color;
if (c){
if (c) {
colors.push(Color.fromNormalizedRgb(c[0], c[1], c[2]));
} else {
skipColors = true;
@@ -402,7 +402,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
for (const s of structures) {
if (ctx.shouldUpdate) await ctx.update(`${s.label}`);
let maxInvariantId = 0;
let maxChainGroupId = 0;
const maxChainGroupId = 0;
for (const u of s.units) {
const invariantId = u.invariantId + offsetInvariantId;
const chainGroupId = u.chainGroupId + offsetChainGroupId;
@@ -415,7 +415,7 @@ export function createStructureFromCellPack(plugin: PluginContext, packing: Cell
if (ctx.shouldUpdate) await ctx.update(`${name} - structure`);
const structure = Structure.create(units);
for( let i = 0, il = structure.models.length; i < il; ++i) {
for (let i = 0, il = structure.models.length; i < il; ++i) {
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
}
return { structure, assets, colors: skipColors ? undefined : colors };
@@ -454,7 +454,7 @@ async function loadMembrane(plugin: PluginContext, name: string, state: State, p
break;
}
}
if (!file){
if (!file) {
// check for cif directly
const cifileName = `${name}.cif`;
for (const f of params.ingredients) {
@@ -544,7 +544,7 @@ async function loadPackings(plugin: PluginContext, runtime: RuntimeContext, stat
representation: params.preset.representation,
};
await CellpackPackingPreset.apply(packing, packingParams, plugin);
if ( packings[i].location === 'surface' && params.membrane){
if (packings[i].location === 'surface' && params.membrane) {
await loadMembrane(plugin, packings[i].name, state, params);
}
}

View File

@@ -84,7 +84,7 @@ const StructureFromCellpack = PluginStateTransform.BuiltIn({
},
dispose({ b, cache }) {
const assets = (cache as any).assets as Asset.Wrapper[];
if(assets) {
if (assets) {
for (const a of assets) a.dispose();
}
@@ -115,12 +115,12 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
// TODO: optimze
// TODO: think of ways how to fast-track changes to this for animations
const model = a.data;
let initial_structure = Structure.ofModel(model);
const initial_structure = Structure.ofModel(model);
const structures: Structure[] = [];
let structure: Structure = initial_structure;
// the list of asambly *?
const symmetry = ModelSymmetry.Provider.get(model);
if (symmetry && symmetry.assemblies.length !== 0){
if (symmetry && symmetry.assemblies.length !== 0) {
for (const a of symmetry.assemblies) {
const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
structures.push(s);
@@ -137,7 +137,7 @@ const StructureFromAssemblies = PluginStateTransform.BuiltIn({
offsetInvariantId += maxInvariantId + 1;
}
structure = builder.getStructure();
for( let i = 0, il = structure.models.length; i < il; ++i) {
for (let i = 0, il = structure.models.length; i < il; ++i) {
Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
}
}

View File

@@ -41,7 +41,7 @@ export async function getFromPdb(plugin: PluginContext, pdbId: string, assetMana
return { mmcif: cif.blocks[0], asset };
}
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager){
export async function getFromOPM(plugin: PluginContext, pdbId: string, assetManager: AssetManager) {
const asset = await plugin.runTask(assetManager.resolve(Asset.getUrlAsset(assetManager, `https://opm-assets.storage.googleapis.com/pdb/${pdbId.toLowerCase()}.pdb`), 'string'));
return { pdb: await parsePDBfile(plugin, asset.data, pdbId), asset };
}

View File

@@ -41,10 +41,10 @@ export const DnatcoConfalPyramidsPreset = StructureRepresentationPresetProvider(
let pyramidsRepr;
if (representations)
pyramidsRepr = builder.buildRepresentation(update, pyramids, { type: ConfalPyramidsRepresentationProvider, typeParams, color: ConfalPyramidsColorThemeProvider }, { tag: 'confal-pyramdis' } );
pyramidsRepr = builder.buildRepresentation(update, pyramids, { type: ConfalPyramidsRepresentationProvider, typeParams, color: ConfalPyramidsColorThemeProvider }, { tag: 'confal-pyramdis' });
await update.commit({ revertOnError: true });
return { components: { ...components, pyramids }, representations: { ...representations, pyramidsRepr } };
return { components: { ...components, pyramids }, representations: { ...representations, pyramidsRepr } };
}
});

View File

@@ -27,7 +27,7 @@ const ColorMapping: ReadonlyMap<ConformerClasses, Color> = new Map([
['B', Color(0xC8CFFF)],
['BII', Color(0x0059DA)],
['miB', Color(0x3BE8FB)],
['Z', Color(0x01F60E)],
['Z', Color(0x01F60E)],
['IC', Color(0xFA5CFB)],
['OPN', Color(0xE90000)],
['SYN', Color(0xFFFF01)],
@@ -165,8 +165,8 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
legend: TableLegend(iterableToArray(ColorMapping.entries()).map(([conformer, color]) => {
return [conformer, color] as [string, Color];
}).concat([
[ 'Error', ErrorColor ],
[ 'Unknown', DefaultColor ]
['Error', ErrorColor],
['Unknown', DefaultColor]
]))
};
}

View File

@@ -20,10 +20,10 @@ import { Structure, StructureProperties, Unit } from '../../../mol-model/structu
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
import { StructureGroup, UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
import { VisualUpdateState } from '../../../mol-repr/util';
import { VisualContext } from '../../../mol-repr/visual';
import { getAltResidueLociFromId } from '../../../mol-repr/structure/visual/util/common';
import { getAltResidueLociFromId, StructureGroup } from '../../../mol-repr/structure/visual/util/common';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
import { NullLocation } from '../../../mol-model/location';

View File

@@ -95,7 +95,7 @@ export namespace ConfalPyramidsUtil {
const first = residueInfoFromLocation(locFirst);
const second = residueInfoFromLocation(locSecond);
const model_id = this.hasMultipleModels ? `-m${modelNum}` : '';
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : '');
const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : '');
const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : '';
const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : '';

View File

@@ -61,7 +61,7 @@ function getColumns(block: G3dDataBlock) {
objectForEach(data, (hs, h) => {
objectForEach(hs, (chs, ch) => {
const entity_id = `${ch}-${h}`;
const l = chs.start.length;
const l = chs.start.length;
if (l === 0) return;
let x = chs.x[0];

View File

@@ -53,7 +53,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
max[j] = Math.max(a[i + j], max[j]);
}
}
return [ min, max ];
return [min, max];
}
private addBuffer(buffer: ArrayBuffer, componentType: number, type: string, count: number, target: number, min?: any, max?: any, normalized?: boolean) {
@@ -108,7 +108,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
indexArray = indices!.slice(0, drawCount);
}
const [ vertexMin, vertexMax ] = GlbExporter.vec3MinMax(vertexArray);
const [vertexMin, vertexMax] = GlbExporter.vec3MinMax(vertexArray);
let vertexBuffer = vertexArray.buffer;
let normalBuffer = normalArray.buffer;
@@ -126,11 +126,8 @@ export class GlbExporter extends MeshExporter<GlbData> {
};
}
private addColorBuffer(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array) {
private addColorBuffer(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined) {
const groupCount = values.uGroupCount.ref.value;
const colorType = values.dColorType.ref.value;
const uColor = values.uColor.ref.value;
const tColor = values.tColor.ref.value.array;
const uAlpha = values.uAlpha.ref.value;
const dTransparency = values.dTransparency.ref.value;
const tTransparency = values.tTransparency.ref.value;
@@ -138,38 +135,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
const colorArray = new Uint8Array(vertexCount * 4);
for (let i = 0; i < vertexCount; ++i) {
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(uColor, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group': {
const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
color = Color.fromArray(tColor, group * 3);
break;
}
case 'groupInstance': {
const group = isGeoTexture ? GlbExporter.getGroup(groups, i) : groups[i];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
}
case 'vertex':
color = Color.fromArray(tColor, i * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * vertexCount + i) * 3);
break;
case 'volume':
color = Color.fromArray(interpolatedColors!, i * 3);
break;
case 'volumeInstance':
color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + i) * 3);
break;
default: throw new Error('Unsupported color type.');
}
let color = GlbExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, i);
let alpha = uAlpha;
if (dTransparency) {
@@ -201,7 +167,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
let interpolatedColors: Uint8Array;
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
const stride = isGeoTexture ? 4 : 3;
interpolatedColors = GlbExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
@@ -235,7 +201,7 @@ export class GlbExporter extends MeshExporter<GlbData> {
// create a color buffer if needed
if (instanceIndex === 0 || !sameColorBuffer) {
colorAccessorIndex = this.addColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors!);
colorAccessorIndex = this.addColorBuffer(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors);
}
// glTF mesh
@@ -307,14 +273,14 @@ export class GlbExporter extends MeshExporter<GlbData> {
if (padding) {
chunk.push(padding.buffer);
}
return [ chunk, 8 + byteLength ];
return [chunk, 8 + byteLength];
};
const jsonString = JSON.stringify(gltf);
const jsonBuffer = new Uint8Array(jsonString.length);
asciiWrite(jsonBuffer, jsonString);
const [ jsonChunk, jsonChunkLength ] = createChunk(0x4E4F534A, [jsonBuffer.buffer], jsonBuffer.length, 0x20);
const [ binaryChunk, binaryChunkLength ] = createChunk(0x004E4942, this.binaryBuffer, binaryBufferLength, 0x00);
const [jsonChunk, jsonChunkLength] = createChunk(0x4E4F534A, [jsonBuffer.buffer], jsonBuffer.length, 0x20);
const [binaryChunk, binaryChunkLength] = createChunk(0x004E4942, this.binaryBuffer, binaryBufferLength, 0x00);
const glbBufferLength = 12 + jsonChunkLength + binaryChunkLength;
const header = new ArrayBuffer(12);

View File

@@ -102,7 +102,7 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
}
const framebuffer = webgl.namedFramebuffers[GeoExportName];
const [ width, height ] = colorTexDim;
const [width, height] = colorTexDim;
const colorGrid = new Uint8Array(width * height * 4);
framebuffer.bind();
@@ -194,6 +194,57 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
}
}
protected static getColor(values: BaseValues, groups: Float32Array | Uint8Array, vertexCount: number, instanceIndex: number, isGeoTexture: boolean, interpolatedColors: Uint8Array | undefined, vertexIndex: number): Color {
const groupCount = values.uGroupCount.ref.value;
const colorType = values.dColorType.ref.value;
const uColor = values.uColor.ref.value;
const tColor = values.tColor.ref.value.array;
const dOverpaint = values.dOverpaint.ref.value;
const tOverpaint = values.tOverpaint.ref.value.array;
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(uColor, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
color = Color.fromArray(tColor, group * 3);
break;
}
case 'groupInstance': {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
}
case 'vertex':
color = Color.fromArray(tColor, vertexIndex * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * vertexCount + vertexIndex) * 3);
break;
case 'volume':
color = Color.fromArray(interpolatedColors!, vertexIndex * 3);
break;
case 'volumeInstance':
color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + vertexIndex) * 3);
break;
default: throw new Error('Unsupported color type.');
}
if (dOverpaint) {
const group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex];
const overpaintColor = Color.fromArray(tOverpaint, (instanceIndex * groupCount + group) * 4);
const overpaintAlpha = tOverpaint[(instanceIndex * groupCount + group) * 4 + 3] / 255;
color = Color.interpolate(color, overpaintColor, overpaintAlpha);
}
return color;
}
protected abstract addMeshWithColors(input: AddMeshInput): void;
private async addMesh(values: MeshValues, webgl: WebGLContext, ctx: RuntimeContext) {
@@ -306,7 +357,7 @@ export abstract class MeshExporter<D extends RenderObjectExportData> implements
}
const framebuffer = webgl.namedFramebuffers[GeoExportName];
const [ width, height ] = values.uGeoTexDim.ref.value;
const [width, height] = values.uGeoTexDim.ref.value;
const vertices = new Float32Array(width * height * 4);
const normals = new Float32Array(width * height * 4);
const groups = webgl.isWebGL2 ? new Uint8Array(width * height * 4) : new Float32Array(width * height * 4);

View File

@@ -79,14 +79,13 @@ export class ObjExporter extends MeshExporter<ObjData> {
const groupCount = values.uGroupCount.ref.value;
const colorType = values.dColorType.ref.value;
const tColor = values.tColor.ref.value.array;
const uAlpha = values.uAlpha.ref.value;
const dTransparency = values.dTransparency.ref.value;
const tTransparency = values.tTransparency.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
let interpolatedColors: Uint8Array;
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
interpolatedColors = ObjExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
ObjExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
@@ -129,38 +128,8 @@ export class ObjExporter extends MeshExporter<ObjData> {
// face
for (let i = 0; i < drawCount; i += 3) {
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(values.uColor.ref.value, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group': {
const group = isGeoTexture ? ObjExporter.getGroup(groups, i) : groups[indices![i]];
color = Color.fromArray(tColor, group * 3);
break;
}
case 'groupInstance': {
const group = isGeoTexture ? ObjExporter.getGroup(groups, i) : groups[indices![i]];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
}
case 'vertex':
color = Color.fromArray(tColor, indices![i] * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * vertexCount + indices![i]) * 3);
break;
case 'volume':
color = Color.fromArray(interpolatedColors!, (isGeoTexture ? i : indices![i]) * 3);
break;
case 'volumeInstance':
color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + (isGeoTexture ? i : indices![i])) * 3);
break;
default: throw new Error('Unsupported color type.');
}
const v = isGeoTexture ? i : indices![i];
const color = ObjExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
let alpha = uAlpha;
if (dTransparency) {

View File

@@ -70,14 +70,13 @@ def Material "material${materialKey}"
const groupCount = values.uGroupCount.ref.value;
const colorType = values.dColorType.ref.value;
const tColor = values.tColor.ref.value.array;
const uAlpha = values.uAlpha.ref.value;
const dTransparency = values.dTransparency.ref.value;
const tTransparency = values.tTransparency.ref.value;
const aTransform = values.aTransform.ref.value;
const instanceCount = values.uInstanceCount.ref.value;
let interpolatedColors: Uint8Array;
let interpolatedColors: Uint8Array | undefined;
if (colorType === 'volume' || colorType === 'volumeInstance') {
interpolatedColors = UsdzExporter.getInterpolatedColors(mesh!.vertices, mesh!.vertexCount, values, stride, colorType, webgl!);
UsdzExporter.quantizeColors(interpolatedColors, mesh!.vertexCount);
@@ -132,38 +131,8 @@ def Material "material${materialKey}"
// color
const faceIndicesByMaterial = new Map<number, number[]>();
for (let i = 0; i < drawCount; i += 3) {
let color: Color;
switch (colorType) {
case 'uniform':
color = Color.fromNormalizedArray(values.uColor.ref.value, 0);
break;
case 'instance':
color = Color.fromArray(tColor, instanceIndex * 3);
break;
case 'group': {
const group = isGeoTexture ? UsdzExporter.getGroup(groups, i) : groups[indices![i]];
color = Color.fromArray(tColor, group * 3);
break;
}
case 'groupInstance': {
const group = isGeoTexture ? UsdzExporter.getGroup(groups, i) : groups[indices![i]];
color = Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3);
break;
}
case 'vertex':
color = Color.fromArray(tColor, indices![i] * 3);
break;
case 'vertexInstance':
color = Color.fromArray(tColor, (instanceIndex * vertexCount + indices![i]) * 3);
break;
case 'volume':
color = Color.fromArray(interpolatedColors!, (isGeoTexture ? i : indices![i]) * 3);
break;
case 'volumeInstance':
color = Color.fromArray(interpolatedColors!, (instanceIndex * vertexCount + (isGeoTexture ? i : indices![i])) * 3);
break;
default: throw new Error('Unsupported color type.');
}
const v = isGeoTexture ? i : indices![i];
const color = UsdzExporter.getColor(values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, v);
let alpha = uAlpha;
if (dTransparency) {

View File

@@ -62,7 +62,7 @@ export namespace PDBePreferredAssembly {
if (model.customProperties.has(Descriptor)) return true;
let asmName: string | undefined = fromCifData(model);
if (asmName === void 0 && params.PDBe_apiSourceJson) {
if (asmName === void 0 && params.PDBe_apiSourceJson) {
const data = await params.PDBe_apiSourceJson(model);
if (!data) return false;
asmName = asmNameFromJson(model, data);

View File

@@ -52,7 +52,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.params.showTooltip = p.showTooltip;
this.ctx.customModelProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);

View File

@@ -87,7 +87,7 @@ export function StructureQualityReportColorTheme(ctx: ThemeDataContext, props: P
};
}
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
export const StructureQualityReportColorThemeProvider: ColorTheme.Provider<Params, 'pdbe-structure-quality-report'> = {
name: 'pdbe-structure-quality-report',
label: 'Structure Quality Report',
category: ColorTheme.Category.Validation,

View File

@@ -73,7 +73,7 @@ namespace StructureQualityReport {
}
export function fromCif(ctx: CustomProperty.Context, model: Model, props: StructureQualityReportProps): StructureQualityReport | undefined {
let info = PropertyWrapper.tryGetInfoFromCif('pdbe_structure_quality_report', model);
const info = PropertyWrapper.tryGetInfoFromCif('pdbe_structure_quality_report', model);
if (!info) return;
const data = getCifData(model);
const issueMap = createIssueMapFromCif(model, data.residues, data.groups);

View File

@@ -47,7 +47,7 @@ export const RCSBAssemblySymmetry = PluginBehavior.create<{ autoAttach: boolean
}
update(p: { autoAttach: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
return updated;
@@ -85,7 +85,7 @@ export const InitAssemblySymmetry3D = StateAction.build({
const assemblySymmetryData = AssemblySymmetryDataProvider.get(a.data).value;
const symmetryIndex = assemblySymmetryData ? AssemblySymmetry.firstNonC1(assemblySymmetryData) : -1;
await AssemblySymmetryProvider.attach(propCtx, a.data, { symmetryIndex });
} catch(e) {
} catch (e) {
plugin.log.error(`Assembly Symmetry: ${e}`);
return;
}

View File

@@ -130,7 +130,7 @@ export function getSymmetrySelectParam(structure?: Structure) {
for (let i = 0, il = assemblySymmetryData.length; i < il; ++i) {
const { symbol, kind } = assemblySymmetryData[i];
if (symbol !== 'C1') {
options.push([ i, `${i + 1}: ${symbol} ${kind}` ]);
options.push([i, `${i + 1}: ${symbol} ${kind}`]);
}
}
if (options.length > 1) {

View File

@@ -310,7 +310,7 @@ function setSymbolTransform(t: Mat4, symbol: string, axes: AssemblySymmetry.Rota
}
}
const unitCircleDirections = (function() {
const unitCircleDirections = (function () {
const dirs: Vec3[] = [];
const circle = polygon(12, false, 1);
for (let i = 0, il = circle.length; i < il; i += 3) {

View File

@@ -7,7 +7,7 @@
import { CollapsableState, CollapsableControls } from '../../../mol-plugin-ui/base';
import { ApplyActionControl } from '../../../mol-plugin-ui/state/apply-action';
import { InitAssemblySymmetry3D, AssemblySymmetry3D, AssemblySymmetryPreset, tryCreateAssemblySymmetry } from './behavior';
import { AssemblySymmetryProvider, AssemblySymmetryProps, AssemblySymmetryDataProvider, AssemblySymmetry } from './prop';
import { AssemblySymmetryProvider, AssemblySymmetryProps, AssemblySymmetryDataProvider, AssemblySymmetry } from './prop';
import { ParameterControls } from '../../../mol-plugin-ui/controls/parameters';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { StructureHierarchyManager } from '../../../mol-plugin-state/manager/structure/hierarchy';

View File

@@ -1,12 +1,13 @@
schema: https://data.rcsb.org/graphql
documents: './src/extensions/rcsb/graphql/symmetry.gql.ts'
generates:
'./src/extensions/rcsb/graphql/types.ts':
plugins:
- add: '/* eslint-disable */'
- time
- typescript
- typescript-operations
config:
immutableTypes: true
skipTypename: true
'./src/extensions/rcsb/graphql/types.ts':
plugins:
- add:
content: '/* eslint-disable */'
- time
- typescript
- typescript-operations
config:
immutableTypes: true
skipTypename: true

File diff suppressed because it is too large Load Diff

View File

@@ -63,7 +63,7 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
}
update(p: { autoAttach: boolean, showTooltip: boolean }) {
let updated = this.params.autoAttach !== p.autoAttach;
const updated = this.params.autoAttach !== p.autoAttach;
this.params.autoAttach = p.autoAttach;
this.params.showTooltip = p.showTooltip;
this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);

View File

@@ -112,7 +112,7 @@ namespace ValidationReport {
}
export async function obtain(ctx: CustomProperty.Context, model: Model, props: ValidationReportProps): Promise<CustomProperty.Data<ValidationReport>> {
switch(props.source.name) {
switch (props.source.name) {
case 'file': return open(ctx, model, props.source.params);
case 'server': return fetch(ctx, model, props.source.params);
}
@@ -208,8 +208,8 @@ function createInterUnitClashes(structure: Structure, clashes: ValidationReport[
for (let i = 0, il = clashes.edgeCount * 2; i < il; ++i) {
// TODO create lookup
let indexA = SortedArray.indexOf(elementsA, a[i]);
let indexB = SortedArray.indexOf(elementsB, b[i]);
const indexA = SortedArray.indexOf(elementsA, a[i]);
const indexB = SortedArray.indexOf(elementsB, b[i]);
if (indexA !== -1 && indexB !== -1) {
unitA.conformation.position(a[i], pA);
@@ -250,8 +250,8 @@ function createIntraUnitClashes(unit: Unit.Atomic, clashes: ValidationReport['cl
for (let i = 0, il = edgeCount * 2; i < il; ++i) {
// TODO create lookup
let indexA = SortedArray.indexOf(elements, a[i]);
let indexB = SortedArray.indexOf(elements, b[i]);
const indexA = SortedArray.indexOf(elements, a[i]);
const indexB = SortedArray.indexOf(elements, b[i]);
if (indexA !== -1 && indexB !== -1) {
unit.conformation.position(a[i], pA);
@@ -431,7 +431,7 @@ function parseValidationReportXml(xml: XMLDocument, model: Model): ValidationRep
const groups = xml.getElementsByTagName('ModelledSubgroup');
for (let i = 0, il = groups.length; i < il; ++i) {
const g = groups[ i ];
const g = groups[i];
const ga = g.attributes;
const pdbx_PDB_model_num = parseInt(getItem(ga, 'model'));

View File

@@ -16,7 +16,7 @@ import { RepresentationContext, RepresentationParamsGetter, Representation } fro
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
import { VisualContext } from '../../../mol-repr/visual';
import { createLinkCylinderMesh, LinkCylinderParams, LinkStyle } from '../../../mol-repr/structure/visual/util/link';
import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../../../mol-repr/structure/units-visual';
import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../../../mol-repr/structure/units-visual';
import { VisualUpdateState } from '../../../mol-repr/util';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { ClashesProvider, IntraUnitClashes, InterUnitClashes, ValidationReport } from './prop';
@@ -28,6 +28,7 @@ import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
import { Sphere3D } from '../../../mol-math/geometry';
import { bondLabel } from '../../../mol-theme/label';
import { getUnitKindsParam } from '../../../mol-repr/structure/params';
import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
//
@@ -106,7 +107,7 @@ function getIntraClashLabel(structure: Structure, unit: Unit.Atomic, clashes: In
function IntraClashLoci(structure: Structure, unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
return DataLoci('intra-clashes', { unit, clashes }, elements,
(boundingSphere: Sphere3D) => getIntraClashBoundingSphere(unit, clashes, elements, boundingSphere),
(boundingSphere: Sphere3D) => getIntraClashBoundingSphere(unit, clashes, elements, boundingSphere),
() => getIntraClashLabel(structure, unit, clashes, elements));
}
@@ -124,7 +125,7 @@ function getIntraClashLoci(pickingId: PickingId, structureGroup: StructureGroup,
}
function eachIntraClash(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
let changed = false;
const changed = false;
// TODO
return changed;
}
@@ -225,7 +226,7 @@ function getInterClashLabel(structure: Structure, clashes: InterUnitClashes, ele
function InterClashLoci(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
return DataLoci('inter-clashes', clashes, elements,
(boundingSphere: Sphere3D) => getInterClashBoundingSphere(structure, clashes, elements, boundingSphere),
(boundingSphere: Sphere3D) => getInterClashBoundingSphere(structure, clashes, elements, boundingSphere),
() => getInterClashLabel(structure, clashes, elements));
}
@@ -239,7 +240,7 @@ function getInterClashLoci(pickingId: PickingId, structure: Structure, id: numbe
}
function eachInterClash(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
let changed = false;
const changed = false;
// TODO
return changed;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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>
@@ -27,6 +27,10 @@ interface ICamera {
readonly fogNear: number,
}
const tmpPos1 = Vec3();
const tmpPos2 = Vec3();
const tmpClip = Vec4();
class Camera implements ICamera {
readonly view: Mat4 = Mat4.identity();
readonly projection: Mat4 = Mat4.identity();
@@ -34,7 +38,7 @@ class Camera implements ICamera {
readonly inverseProjectionView: Mat4 = Mat4.identity();
private pixelScale: number
get pixelRatio () {
get pixelRatio() {
const dpr = (typeof window !== 'undefined') ? window.devicePixelRatio : 1;
return dpr * this.pixelScale;
}
@@ -155,14 +159,32 @@ class Camera implements ICamera {
}
}
/** Transform point into 2D window coordinates. */
project(out: Vec4, point: Vec3) {
return cameraProject(out, point, this.viewport, this.projectionView);
}
unproject(out: Vec3, point: Vec3) {
/**
* Transform point from screen space to 3D coordinates.
* The point must have `x` and `y` set to 2D window coordinates
* and `z` between 0 (near) and 1 (far); the optional `w` is not used.
*/
unproject(out: Vec3, point: Vec3 | Vec4) {
return cameraUnproject(out, point, this.viewport, this.inverseProjectionView);
}
/** World space pixel size at given `point` */
getPixelSize(point: Vec3) {
// project -> unproject of `point` does not exactly return the same
// to get a sufficiently accurate measure we unproject the original
// clip position in addition to the one shifted bey one pixel
this.project(tmpClip, point);
this.unproject(tmpPos1, tmpClip);
tmpClip[0] += 1;
this.unproject(tmpPos2, tmpClip);
return Vec3.distance(tmpPos1, tmpPos2);
}
constructor(state?: Partial<Camera.Snapshot>, viewport = Viewport.create(0, 0, 128, 128), props: Partial<{ pixelScale: number }> = {}) {
this.viewport = viewport;
this.pixelScale = props.pixelScale || 1;
@@ -178,7 +200,7 @@ namespace Camera {
/**
* Sets an offseted view in a larger frustum. This is useful for
* - multi-window or multi-monitor/multi-machine setups
* - jittering the camera position for
* - jittering the camera position for sampling
*/
export interface ViewOffset {
enabled: boolean,

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -55,14 +55,11 @@ namespace Viewport {
//
const NEAR_RANGE = 0;
const FAR_RANGE = 1;
const tmpVec4 = Vec4();
/** Transform point into 2D window coordinates. */
export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
export function cameraProject(out: Vec4, point: Vec3, viewport: Viewport, projectionView: Mat4) {
const { x, y, width, height } = viewport;
// clip space -> NDC -> window coordinates, implicit 1.0 for w component
Vec4.set(tmpVec4, point[0], point[1], point[2], 1.0);
@@ -78,27 +75,28 @@ export function cameraProject (out: Vec4, point: Vec3, viewport: Viewport, proje
tmpVec4[2] /= w;
}
// transform into window coordinates, set fourth component is (1/clip.w) as in gl_FragCoord.w
out[0] = vX + vWidth / 2 * tmpVec4[0] + (0 + vWidth / 2);
out[1] = vY + vHeight / 2 * tmpVec4[1] + (0 + vHeight / 2);
out[2] = (FAR_RANGE - NEAR_RANGE) / 2 * tmpVec4[2] + (FAR_RANGE + NEAR_RANGE) / 2;
// transform into window coordinates, set fourth component to 1 / clip.w as in gl_FragCoord.w
out[0] = (tmpVec4[0] + 1) * width * 0.5 + x;
out[1] = (1 - tmpVec4[1]) * height * 0.5 + y; // flip Y
out[2] = (tmpVec4[2] + 1) * 0.5;
out[3] = w === 0 ? 0 : 1 / w;
return out;
}
/**
* Transform point from screen space to 3D coordinates.
* The point must have x and y set to 2D window coordinates and z between 0 (near) and 1 (far).
* The point must have `x` and `y` set to 2D window coordinates
* and `z` between 0 (near) and 1 (far); the optional `w` is not used.
*/
export function cameraUnproject (out: Vec3, point: Vec3, viewport: Viewport, inverseProjectionView: Mat4) {
const { x: vX, y: vY, width: vWidth, height: vHeight } = viewport;
export function cameraUnproject(out: Vec3, point: Vec3 | Vec4, viewport: Viewport, inverseProjectionView: Mat4) {
const { x, y, width, height } = viewport;
const x = point[0] - vX;
const y = (vHeight - point[1] - 1) - vY;
const z = point[2];
const px = point[0] - x;
const py = (height - point[1] - 1) - y;
const pz = point[2];
out[0] = (2 * x) / vWidth - 1;
out[1] = (2 * y) / vHeight - 1;
out[2] = 2 * z - 1;
out[0] = (2 * px) / width - 1;
out[1] = (2 * py) / height - 1;
out[2] = 2 * pz - 1;
return Vec3.transformMat4(out, out, inverseProjectionView);
}

View File

@@ -23,7 +23,7 @@ import { Camera } from './camera';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { DebugHelperParams } from './helper/bounding-sphere-helper';
import { SetUtils } from '../mol-util/set';
import { Canvas3dInteractionHelper } from './helper/interaction-events';
import { Canvas3dInteractionHelper, Canvas3dInteractionHelperParams } from './helper/interaction-events';
import { PostprocessingParams } from './passes/postprocessing';
import { MultiSampleHelper, MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
import { PickData } from './passes/pick';
@@ -38,6 +38,7 @@ import { StereoCamera, StereoCameraParams } from './camera/stereo';
import { Helper } from './helper/helper';
import { Passes } from './passes/passes';
import { shallowEqual } from '../mol-util';
import { MarkingParams } from './passes/marking';
export const Canvas3DParams = {
camera: PD.Group({
@@ -80,8 +81,10 @@ export const Canvas3DParams = {
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
marking: PD.Group(MarkingParams),
renderer: PD.Group(RendererParams),
trackball: PD.Group(TrackballControlsParams),
interaction: PD.Group(Canvas3dInteractionHelperParams),
debug: PD.Group(DebugHelperParams),
handle: PD.Group(HandleHelperParams),
};
@@ -113,23 +116,27 @@ namespace Canvas3DContext {
preserveDrawingBuffer: true,
pixelScale: 1,
pickScale: 0.25,
enableWboit: true
/** extra pixels to around target to check in case target is empty */
pickPadding: 1,
enableWboit: true,
preferWebGl1: false
};
export type Attribs = typeof DefaultAttribs
export function fromCanvas(canvas: HTMLCanvasElement, attribs: Partial<Attribs> = {}): Canvas3DContext {
const a = { ...DefaultAttribs, ...attribs };
const { antialias, preserveDrawingBuffer, pixelScale } = a;
const { antialias, preserveDrawingBuffer, pixelScale, preferWebGl1 } = a;
const gl = getGLContext(canvas, {
antialias,
preserveDrawingBuffer,
alpha: true, // the renderer requires an alpha channel
depth: true, // the renderer requires a depth buffer
premultipliedAlpha: true, // the renderer outputs PMA
preferWebGl1
});
if (gl === null) throw new Error('Could not create a WebGL rendering context');
const input = InputObserver.fromElement(canvas, { pixelScale });
const input = InputObserver.fromElement(canvas, { pixelScale, preventGestures: true });
const webgl = createContext(gl, { pixelScale });
const passes = new Passes(webgl, attribs);
@@ -222,11 +229,13 @@ interface Canvas3D {
animate(): void
/**
* Pause animation loop and optionally any rendering
* @param noDraw pause any rendering
* @param noDraw pause any rendering (drawPaused = true)
*/
pause(noDraw?: boolean): void
/** Sets drawPaused = false without starting the built in animation loop */
resume(): void
identify(x: number, y: number): PickData | undefined
mark(loci: Representation.Loci, action: MarkerAction): void
mark(loci: Representation.Loci, action: MarkerAction, noDraw?: boolean): void
getLoci(pickingId: PickingId | undefined): Representation.Loci
notifyDidDraw: boolean,
@@ -301,11 +310,10 @@ namespace Canvas3D {
const renderer = Renderer.create(webgl, p.renderer);
const helper = new Helper(webgl, scene, p);
const pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x, y, width, height });
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera);
const pickHelper = new PickHelper(webgl, renderer, scene, helper, passes.pick, { x, y, width, height }, attribs.pickPadding);
const interactionHelper = new Canvas3dInteractionHelper(identify, getLoci, input, camera, p.interaction);
const multiSampleHelper = new MultiSampleHelper(passes.multiSample);
let drawPending = false;
let cameraResetRequested = false;
let nextCameraResetDuration: number | undefined = void 0;
let nextCameraResetSnapshot: Camera.SnapshotProvider | undefined = void 0;
@@ -336,7 +344,7 @@ namespace Canvas3D {
return { loci, repr };
}
function mark(reprLoci: Representation.Loci, action: MarkerAction) {
function mark(reprLoci: Representation.Loci, action: MarkerAction, noDraw = false) {
const { repr, loci } = reprLoci;
let changed = false;
if (repr) {
@@ -346,7 +354,7 @@ namespace Canvas3D {
changed = helper.camera.mark(loci, action) || changed;
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed) {
if (changed && !noDraw) {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
@@ -373,9 +381,13 @@ namespace Canvas3D {
let didRender = false;
controls.update(currentTime);
const cameraChanged = camera.update();
const multiSampleChanged = multiSampleHelper.update(force || cameraChanged, p.multiSample);
if (resized || force || cameraChanged || multiSampleChanged) {
const shouldRender = force || cameraChanged || resized || forceNextRender;
forceNextRender = false;
const multiSampleChanged = multiSampleHelper.update(shouldRender, p.multiSample);
if (shouldRender || multiSampleChanged) {
let cam: Camera | StereoCamera = camera;
if (p.camera.stereo.name === 'on') {
stereoCamera.update();
@@ -385,7 +397,7 @@ namespace Canvas3D {
if (MultiSamplePass.isEnabled(p.multiSample)) {
multiSampleHelper.render(renderer, cam, scene, helper, true, p.transparentBackground, p);
} else {
passes.draw.render(renderer, cam, scene, helper, true, p.transparentBackground, p.postprocessing);
passes.draw.render(renderer, cam, scene, helper, true, p.transparentBackground, p.postprocessing, p.marking);
}
pickHelper.dirty = true;
didRender = true;
@@ -394,24 +406,20 @@ namespace Canvas3D {
return didRender;
}
let forceNextDraw = false;
let forceNextRender = false;
let forceDrawAfterAllCommited = false;
let currentTime = 0;
let drawPaused = false;
function draw(force?: boolean) {
if (drawPaused) return;
if (render(!!force || forceNextDraw) && notifyDidDraw) {
if (render(!!force) && notifyDidDraw) {
didDraw.next(now() - startTime as now.Timestamp);
}
forceNextDraw = false;
drawPending = false;
}
function requestDraw(force?: boolean) {
if (drawPending) return;
drawPending = true;
forceNextDraw = !!force;
forceNextRender = forceNextRender || !!force;
}
let animationFrameHandle = 0;
@@ -635,9 +643,11 @@ namespace Canvas3D {
viewport: p.viewport,
postprocessing: { ...p.postprocessing },
marking: { ...p.marking },
multiSample: { ...p.multiSample },
renderer: { ...renderer.props },
trackball: { ...controls.props },
interaction: { ...interactionHelper.props },
debug: { ...helper.debug.props },
handle: { ...helper.handle.props },
};
@@ -703,6 +713,7 @@ namespace Canvas3D {
animate,
resetTime,
pause,
resume: () => { drawPaused = false; },
identify,
mark,
getLoci,
@@ -727,7 +738,7 @@ namespace Canvas3D {
resized,
setProps: (properties, doNotRequestDraw = false) => {
const props: PartialCanvas3DProps = typeof properties === 'function'
? produce(getProps(), properties)
? produce(getProps(), properties as any)
: properties;
const cameraState: Partial<Camera.Snapshot> = Object.create(null);
@@ -769,9 +780,11 @@ namespace Canvas3D {
}
if (props.postprocessing) Object.assign(p.postprocessing, props.postprocessing);
if (props.marking) Object.assign(p.marking, props.marking);
if (props.multiSample) Object.assign(p.multiSample, props.multiSample);
if (props.renderer) renderer.setProps(props.renderer);
if (props.trackball) controls.setProps(props.trackball);
if (props.interaction) interactionHelper.setProps(props.interaction);
if (props.debug) helper.debug.setProps(props.debug);
if (props.handle) helper.handle.setProps(props.handle);
@@ -816,6 +829,8 @@ namespace Canvas3D {
};
function updateViewport() {
const oldX = x, oldY = y, oldWidth = width, oldHeight = height;
if (p.viewport.name === 'canvas') {
x = 0;
y = 0;
@@ -831,11 +846,11 @@ namespace Canvas3D {
height = Math.round(p.viewport.params.height * gl.drawingBufferHeight);
y = Math.round(gl.drawingBufferHeight - height - p.viewport.params.y * gl.drawingBufferHeight);
width = Math.round(p.viewport.params.width * gl.drawingBufferWidth);
// if (x + width >= gl.drawingBufferWidth) width = gl.drawingBufferWidth - x;
// if (y + height >= gl.drawingBufferHeight) height = gl.drawingBufferHeight - y - 1;
// console.log({ x, y, width, height });
}
if (oldX !== x || oldY !== y || oldWidth !== width || oldHeight !== height) {
forceNextRender = true;
}
}
function syncViewport() {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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>
@@ -10,7 +10,7 @@
import { Quat, Vec2, Vec3, EPSILON } from '../../mol-math/linear-algebra';
import { Viewport } from '../camera/util';
import { InputObserver, DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys } from '../../mol-util/input/input-observer';
import { InputObserver, DragInput, WheelInput, PinchInput, ButtonsType, ModifiersKeys, GestureInput } from '../../mol-util/input/input-observer';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Camera } from '../camera';
import { absMax } from '../../mol-math/misc';
@@ -49,6 +49,9 @@ export const TrackballControlsParams = {
minDistance: PD.Numeric(0.01, {}, { isHidden: true }),
maxDistance: PD.Numeric(1e150, {}, { isHidden: true }),
gestureScaleFactor: PD.Numeric(1, {}, { isHidden: true }),
maxWheelDelta: PD.Numeric(0.02, {}, { isHidden: true }),
bindings: PD.Value(DefaultTrackballBindings, { isHidden: true }),
/**
@@ -91,6 +94,7 @@ namespace TrackballControls {
const interactionEndSub = input.interactionEnd.subscribe(onInteractionEnd);
const wheelSub = input.wheel.subscribe(onWheel);
const pinchSub = input.pinch.subscribe(onPinch);
const gestureSub = input.gesture.subscribe(onGesture);
let _isInteracting = false;
@@ -390,25 +394,33 @@ namespace TrackballControls {
_isInteracting = false;
}
function onWheel({ x, y, dx, dy, dz, buttons, modifiers }: WheelInput) {
function onWheel({ x, y, spinX, spinY, dz, buttons, modifiers }: WheelInput) {
if (outsideViewport(x, y)) return;
const delta = absMax(dx, dy, dz);
let delta = absMax(spinX * 0.075, spinY * 0.075, dz * 0.0001);
if (delta < -p.maxWheelDelta) delta = -p.maxWheelDelta;
else if (delta > p.maxWheelDelta) delta = p.maxWheelDelta;
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_zoomEnd[1] += delta * 0.0001;
_zoomEnd[1] += delta;
}
if (Binding.match(p.bindings.scrollFocus, buttons, modifiers)) {
_focusEnd[1] += delta * 0.0001;
_focusEnd[1] += delta;
}
}
function onPinch({ fraction, buttons, modifiers }: PinchInput) {
function onPinch({ fractionDelta, buttons, modifiers }: PinchInput) {
if (Binding.match(p.bindings.scrollZoom, buttons, modifiers)) {
_isInteracting = true;
_zoomEnd[1] += (fraction - 1) * 0.1;
_zoomEnd[1] += p.gestureScaleFactor * fractionDelta;
}
}
function onGesture({ deltaScale }: GestureInput) {
_isInteracting = true;
_zoomEnd[1] += p.gestureScaleFactor * deltaScale;
}
function dispose() {
if (disposed) return;
disposed = true;
@@ -416,6 +428,7 @@ namespace TrackballControls {
dragSub.unsubscribe();
wheelSub.unsubscribe();
pinchSub.unsubscribe();
gestureSub.unsubscribe();
interactionEndSub.unsubscribe();
}

View File

@@ -121,7 +121,7 @@ export class BoundingSphereHelper {
}
get props() { return this._props as Readonly<DebugHelperProps>; }
setProps (props: Partial<DebugHelperProps>) {
setProps(props: Partial<DebugHelperProps>) {
Object.assign(this._props, props);
if (this.isEnabled) this.update();
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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>
@@ -11,6 +11,8 @@ import { InputObserver, ModifiersKeys, ButtonsType } from '../../mol-util/input/
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { Camera } from '../camera';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Bond } from '../../mol-model/structure';
type Canvas3D = import('../canvas3d').Canvas3D
type HoverEvent = import('../canvas3d').Canvas3D.HoverEvent
@@ -19,6 +21,17 @@ type ClickEvent = import('../canvas3d').Canvas3D.ClickEvent
const enum InputEvent { Move, Click, Drag }
const tmpPosA = Vec3();
const tmpPos = Vec3();
const tmpNorm = Vec3();
export const Canvas3dInteractionHelperParams = {
maxFps: PD.Numeric(30, { min: 10, max: 60, step: 10 }),
preferAtomPixelPadding: PD.Numeric(3, { min: 0, max: 20, step: 1 }, { description: 'Number of extra pixels at which to prefer atoms over bonds.' }),
};
export type Canvas3dInteractionHelperParams = typeof Canvas3dInteractionHelperParams
export type Canvas3dInteractionHelperProps = PD.Values<Canvas3dInteractionHelperParams>
export class Canvas3dInteractionHelper {
private ev = RxEventHelper.create();
@@ -48,6 +61,12 @@ export class Canvas3dInteractionHelper {
private button: ButtonsType.Flag = ButtonsType.create(0);
private modifiers: ModifiersKeys = ModifiersKeys.None;
readonly props: Canvas3dInteractionHelperProps;
setProps(props: Partial<Canvas3dInteractionHelperProps>) {
Object.assign(this.props, props);
}
private identify(e: InputEvent, t: number) {
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
@@ -70,7 +89,7 @@ export class Canvas3dInteractionHelper {
}
if (e === InputEvent.Click) {
const loci = this.getLoci(this.id);
const loci = this.getLoci(this.id, this.position);
this.events.click.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, page: Vec2.create(this.endX, this.endY), position: this.position });
this.prevLoci = loci;
return;
@@ -78,13 +97,13 @@ export class Canvas3dInteractionHelper {
if (!this.inside || this.currentIdentifyT !== t || !xyChanged || this.outsideViewport(this.endX, this.endY)) return;
const loci = this.getLoci(this.id);
const loci = this.getLoci(this.id, this.position);
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, page: Vec2.create(this.endX, this.endY), position: this.position });
this.prevLoci = loci;
}
tick(t: number) {
if (this.inside && t - this.prevT > 1000 / this.maxFps) {
if (this.inside && t - this.prevT > 1000 / this.props.maxFps) {
this.prevT = t;
this.currentIdentifyT = t;
this.identify(this.isInteracting ? InputEvent.Drag : InputEvent.Move, t);
@@ -144,18 +163,41 @@ export class Canvas3dInteractionHelper {
);
}
private getLoci(pickingId: PickingId | undefined, position: Vec3 | undefined) {
const { repr, loci } = this.lociGetter(pickingId);
if (position && repr && Bond.isLoci(loci) && loci.bonds.length === 2) {
const { aUnit, aIndex } = loci.bonds[0];
aUnit.conformation.position(aUnit.elements[aIndex], tmpPosA);
Vec3.sub(tmpNorm, this.camera.state.position, this.camera.state.target);
Vec3.projectPointOnPlane(tmpPos, position, tmpNorm, tmpPosA);
const pixelSize = this.camera.getPixelSize(tmpPos);
let radius = repr.theme.size.size(loci.bonds[0]) * (repr.props.sizeFactor ?? 1);
if (repr.props.lineSizeAttenuation === false) {
// divide by two to get radius
radius *= pixelSize / 2;
}
radius += this.props.preferAtomPixelPadding * pixelSize;
if (Vec3.distance(tmpPos, tmpPosA) < radius) {
return { repr, loci: Bond.toFirstStructureElementLoci(loci) };
}
}
return { repr, loci };
}
dispose() {
this.ev.dispose();
}
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], private input: InputObserver, private camera: Camera, private maxFps: number = 30) {
input.drag.subscribe(({x, y, buttons, button, modifiers }) => {
constructor(private canvasIdentify: Canvas3D['identify'], private lociGetter: Canvas3D['getLoci'], private input: InputObserver, private camera: Camera, props: Partial<Canvas3dInteractionHelperProps> = {}) {
this.props = { ...PD.getDefaultValues(Canvas3dInteractionHelperParams), ...props };
input.drag.subscribe(({ x, y, buttons, button, modifiers }) => {
this.isInteracting = true;
// console.log('drag');
this.drag(x, y, buttons, button, modifiers);
});
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
input.move.subscribe(({ x, y, inside, buttons, button, modifiers }) => {
if (!inside || this.isInteracting) return;
// console.log('move');
this.move(x, y, buttons, button, modifiers);
@@ -166,7 +208,7 @@ export class Canvas3dInteractionHelper {
this.leave();
});
input.click.subscribe(({x, y, buttons, button, modifiers }) => {
input.click.subscribe(({ x, y, buttons, button, modifiers }) => {
if (this.outsideViewport(x, y)) return;
// console.log('click');
this.click(x, y, buttons, button, modifiers);

View File

@@ -26,6 +26,7 @@ import { copy_frag } from '../../mol-gl/shader/copy.frag';
import { StereoCamera } from '../camera/stereo';
import { WboitPass } from './wboit';
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
import { MarkingPass, MarkingProps } from './marking';
const DepthMergeSchema = {
...QuadSchema,
@@ -57,8 +58,8 @@ const CopySchema = {
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
};
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
const CopyShaderCode = ShaderCode('copy', quad_vert, copy_frag);
type CopyRenderable = ComputeRenderable<Values<typeof CopySchema>>
function getCopyRenderable(ctx: WebGLContext, colorTexture: Texture): CopyRenderable {
const values: Values<typeof CopySchema> = {
@@ -92,6 +93,7 @@ export class DrawPass {
private copyFboPostprocessing: CopyRenderable
private wboit: WboitPass | undefined
private readonly marking: MarkingPass
readonly postprocessing: PostprocessingPass
private readonly antialiasing: AntialiasingPass
@@ -122,6 +124,7 @@ export class DrawPass {
this.depthMerge = getDepthMergeRenderable(webgl, this.depthTexturePrimitives, this.depthTextureVolumes, this.packedDepth);
this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
this.marking = new MarkingPass(webgl, width, height);
this.postprocessing = new PostprocessingPass(webgl, this);
this.antialiasing = new AntialiasingPass(webgl, this);
@@ -162,6 +165,7 @@ export class DrawPass {
this.wboit.setSize(width, height);
}
this.marking.setSize(width, height);
this.postprocessing.setSize(width, height);
this.antialiasing.setSize(width, height);
}
@@ -281,10 +285,11 @@ export class DrawPass {
renderer.renderBlendedTransparent(scene.primitives, camera, null);
}
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
private _render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps, markingProps: MarkingProps) {
const volumeRendering = scene.volumes.renderables.length > 0;
const postprocessingEnabled = PostprocessingPass.isEnabled(postprocessingProps);
const antialiasingEnabled = AntialiasingPass.isEnabled(postprocessingProps);
const markingEnabled = MarkingPass.isEnabled(markingProps);
const { x, y, width, height } = camera.viewport;
renderer.setViewport(x, y, width, height);
@@ -309,6 +314,22 @@ export class DrawPass {
this.drawTarget.bind();
}
if (markingEnabled) {
const markingDepthTest = markingProps.ghostEdgeStrength < 1;
if (markingDepthTest) {
this.marking.depthTarget.bind();
renderer.clear(false);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
renderer.clear(false);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(markingProps);
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
}
if (helper.debug.isEnabled) {
helper.debug.syncVisibility();
renderer.renderBlended(helper.debug.scene, camera, null);
@@ -338,15 +359,16 @@ export class DrawPass {
this.webgl.gl.flush();
}
render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
render(renderer: Renderer, camera: Camera | StereoCamera, scene: Scene, helper: Helper, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps, markingProps: MarkingProps) {
renderer.setTransparentBackground(transparentBackground);
renderer.setDrawingBufferSize(this.colorTarget.getWidth(), this.colorTarget.getHeight());
renderer.setPixelRatio(this.webgl.pixelRatio);
if (StereoCamera.is(camera)) {
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps);
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps);
this._render(renderer, camera.left, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
this._render(renderer, camera.right, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
} else {
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps);
this._render(renderer, camera, scene, helper, toDrawingBuffer, transparentBackground, postprocessingProps, markingProps);
}
}

View File

@@ -17,11 +17,13 @@ import { Viewport } from '../camera/util';
import { PixelData } from '../../mol-util/image';
import { Helper } from '../helper/helper';
import { CameraHelper, CameraHelperParams } from '../helper/camera-helper';
import { MarkingParams } from './marking';
export const ImageParams = {
transparentBackground: PD.Boolean(false),
multiSample: PD.Group(MultiSampleParams),
postprocessing: PD.Group(PostprocessingParams),
marking: PD.Group(MarkingParams),
cameraHelper: PD.Group(CameraHelperParams),
};
@@ -85,7 +87,7 @@ export class ImagePass {
this.multiSampleHelper.render(this.renderer, this._camera, this.scene, this.helper, false, this.props.transparentBackground, this.props);
this._colorTarget = this.multiSamplePass.colorTarget;
} else {
this.drawPass.render(this.renderer, this._camera, this.scene, this.helper, false, this.props.transparentBackground, this.props.postprocessing);
this.drawPass.render(this.renderer, this._camera, this.scene, this.helper, false, this.props.transparentBackground, this.props.postprocessing, this.props.marking);
this._colorTarget = this.drawPass.getColorTarget(this.props.postprocessing);
}
}

View File

@@ -0,0 +1,194 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
import { ShaderCode } from '../../mol-gl/shader-code';
import { WebGLContext } from '../../mol-gl/webgl/context';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { Texture } from '../../mol-gl/webgl/texture';
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { ValueCell } from '../../mol-util';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { overlay_frag } from '../../mol-gl/shader/marking/overlay.frag';
import { Viewport } from '../camera/util';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Color } from '../../mol-util/color';
import { edge_frag } from '../../mol-gl/shader/marking/edge.frag';
export const MarkingParams = {
enabled: PD.Boolean(false),
highlightEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(1.0, 0.4, 0.6), 1.0)),
selectEdgeColor: PD.Color(Color.darken(Color.fromNormalizedRgb(0.2, 1.0, 0.1), 1.0)),
edgeScale: PD.Numeric(1, { min: 1, max: 3, step: 1 }, { description: 'Thickness of the edge.' }),
ghostEdgeStrength: PD.Numeric(0.3, { min: 0, max: 1, step: 0.1 }, { description: 'Opacity of the hidden edges that are covered by other geometry. When set to 1, one less geometry render pass is done.' }),
innerEdgeFactor: PD.Numeric(1.5, { min: 0, max: 3, step: 0.1 }, { description: 'Factor to multiply the inner edge color with - for added contrast.' }),
};
export type MarkingProps = PD.Values<typeof MarkingParams>
export class MarkingPass {
static isEnabled(props: MarkingProps) {
return props.enabled;
}
readonly depthTarget: RenderTarget
readonly maskTarget: RenderTarget
private readonly edgesTarget: RenderTarget
private readonly edge: EdgeRenderable
private readonly overlay: OverlayRenderable
constructor(private webgl: WebGLContext, width: number, height: number) {
this.depthTarget = webgl.createRenderTarget(width, height);
this.maskTarget = webgl.createRenderTarget(width, height);
this.edgesTarget = webgl.createRenderTarget(width, height);
this.edge = getEdgeRenderable(webgl, this.maskTarget.texture);
this.overlay = getOverlayRenderable(webgl, this.edgesTarget.texture);
}
private setEdgeState(viewport: Viewport) {
const { gl, state } = this.webgl;
state.enable(gl.SCISSOR_TEST);
state.enable(gl.BLEND);
state.blendFunc(gl.ONE, gl.ONE);
state.blendEquation(gl.FUNC_ADD);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
state.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
private setOverlayState(viewport: Viewport) {
const { gl, state } = this.webgl;
state.enable(gl.SCISSOR_TEST);
state.enable(gl.BLEND);
state.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
state.blendEquation(gl.FUNC_ADD);
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
const { x, y, width, height } = viewport;
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
}
setSize(width: number, height: number) {
const w = this.depthTarget.getWidth();
const h = this.depthTarget.getHeight();
if (width !== w || height !== h) {
this.depthTarget.setSize(width, height);
this.maskTarget.setSize(width, height);
this.edgesTarget.setSize(width, height);
ValueCell.update(this.edge.values.uTexSizeInv, Vec2.set(this.edge.values.uTexSizeInv.ref.value, 1 / width, 1 / height));
ValueCell.update(this.overlay.values.uTexSizeInv, Vec2.set(this.overlay.values.uTexSizeInv.ref.value, 1 / width, 1 / height));
}
}
update(props: MarkingProps) {
const { highlightEdgeColor, selectEdgeColor, edgeScale, innerEdgeFactor, ghostEdgeStrength } = props;
const { values: edgeValues } = this.edge;
const _edgeScale = Math.round(edgeScale * this.webgl.pixelRatio);
if (edgeValues.dEdgeScale.ref.value !== _edgeScale) {
ValueCell.update(edgeValues.dEdgeScale, _edgeScale);
this.edge.update();
}
const { values: overlayValues } = this.overlay;
ValueCell.update(overlayValues.uHighlightEdgeColor, Color.toVec3Normalized(overlayValues.uHighlightEdgeColor.ref.value, highlightEdgeColor));
ValueCell.update(overlayValues.uSelectEdgeColor, Color.toVec3Normalized(overlayValues.uSelectEdgeColor.ref.value, selectEdgeColor));
ValueCell.update(overlayValues.uInnerEdgeFactor, innerEdgeFactor);
ValueCell.update(overlayValues.uGhostEdgeStrength, ghostEdgeStrength);
}
render(viewport: Viewport, target: RenderTarget | undefined) {
this.edgesTarget.bind();
this.setEdgeState(viewport);
this.edge.render();
if (target) {
target.bind();
} else {
this.webgl.unbindFramebuffer();
}
this.setOverlayState(viewport);
this.overlay.render();
}
}
//
const EdgeSchema = {
...QuadSchema,
tMaskTexture: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
uTexSizeInv: UniformSpec('v2'),
dEdgeScale: DefineSpec('number'),
};
const EdgeShaderCode = ShaderCode('edge', quad_vert, edge_frag);
type EdgeRenderable = ComputeRenderable<Values<typeof EdgeSchema>>
function getEdgeRenderable(ctx: WebGLContext, maskTexture: Texture): EdgeRenderable {
const width = maskTexture.getWidth();
const height = maskTexture.getHeight();
const values: Values<typeof EdgeSchema> = {
...QuadValues,
tMaskTexture: ValueCell.create(maskTexture),
uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
dEdgeScale: ValueCell.create(1),
};
const schema = { ...EdgeSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', EdgeShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
//
const OverlaySchema = {
...QuadSchema,
tEdgeTexture: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
uTexSizeInv: UniformSpec('v2'),
uHighlightEdgeColor: UniformSpec('v3'),
uSelectEdgeColor: UniformSpec('v3'),
uGhostEdgeStrength: UniformSpec('f'),
uInnerEdgeFactor: UniformSpec('f'),
};
const OverlayShaderCode = ShaderCode('overlay', quad_vert, overlay_frag);
type OverlayRenderable = ComputeRenderable<Values<typeof OverlaySchema>>
function getOverlayRenderable(ctx: WebGLContext, edgeTexture: Texture): OverlayRenderable {
const width = edgeTexture.getWidth();
const height = edgeTexture.getHeight();
const values: Values<typeof OverlaySchema> = {
...QuadValues,
tEdgeTexture: ValueCell.create(edgeTexture),
uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
uHighlightEdgeColor: ValueCell.create(Vec3()),
uSelectEdgeColor: ValueCell.create(Vec3()),
uGhostEdgeStrength: ValueCell.create(0),
uInnerEdgeFactor: ValueCell.create(0),
};
const schema = { ...OverlaySchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', OverlayShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}

View File

@@ -22,9 +22,9 @@ import { Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
import { Helper } from '../helper/helper';
import { StereoCamera } from '../camera/stereo';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { compose_frag } from '../../mol-gl/shader/compose.frag';
import { MarkingProps } from './marking';
const ComposeSchema = {
...QuadSchema,
@@ -55,7 +55,11 @@ export const MultiSampleParams = {
};
export type MultiSampleProps = PD.Values<typeof MultiSampleParams>
type Props = { multiSample: MultiSampleProps, postprocessing: PostprocessingProps }
type Props = {
multiSample: MultiSampleProps
postprocessing: PostprocessingProps
marking: MarkingProps
}
export class MultiSamplePass {
static isEnabled(props: MultiSampleProps) {
@@ -119,7 +123,7 @@ export class MultiSamplePass {
//
// This manual approach to MSAA re-renders the scene once for
// each sample with camera jitter and accumulates the results.
const offsetList = JitterVectors[ Math.max(0, Math.min(props.multiSample.sampleLevel, 5)) ];
const offsetList = JitterVectors[Math.max(0, Math.min(props.multiSample.sampleLevel, 5))];
const { x, y, width, height } = camera.viewport;
const baseSampleWeight = 1.0 / offsetList.length;
@@ -144,7 +148,7 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight);
// render scene
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing);
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
// compose rendered scene with compose target
composeTarget.bind();
@@ -186,7 +190,7 @@ export class MultiSamplePass {
//
// This manual approach to MSAA re-renders the scene once for
// each sample with camera jitter and accumulates the results.
const offsetList = JitterVectors[ Math.max(0, Math.min(props.multiSample.sampleLevel, 5)) ];
const offsetList = JitterVectors[Math.max(0, Math.min(props.multiSample.sampleLevel, 5))];
if (sampleIndex === -2 || sampleIndex >= offsetList.length) return -2;
@@ -194,7 +198,7 @@ export class MultiSamplePass {
const sampleWeight = 1.0 / offsetList.length;
if (sampleIndex === -1) {
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing);
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, drawPass.getColorTarget(props.postprocessing).texture);
compose.update();
@@ -222,7 +226,7 @@ export class MultiSamplePass {
camera.update();
// render scene
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing);
drawPass.render(renderer, camera, scene, helper, false, transparentBackground, props.postprocessing, props.marking);
// compose rendered scene with compose target
composeTarget.bind();
@@ -240,7 +244,7 @@ export class MultiSamplePass {
compose.render();
sampleIndex += 1;
if (sampleIndex >= offsetList.length ) break;
if (sampleIndex >= offsetList.length) break;
}
}
@@ -274,33 +278,33 @@ export class MultiSamplePass {
const JitterVectors = [
[
[ 0, 0 ]
[0, 0]
],
[
[ 4, 4 ], [ -4, -4 ]
[4, 4], [-4, -4]
],
[
[ -2, -6 ], [ 6, -2 ], [ -6, 2 ], [ 2, 6 ]
[-2, -6], [6, -2], [-6, 2], [2, 6]
],
[
[ 1, -3 ], [ -1, 3 ], [ 5, 1 ], [ -3, -5 ],
[ -5, 5 ], [ -7, -1 ], [ 3, 7 ], [ 7, -7 ]
[1, -3], [-1, 3], [5, 1], [-3, -5],
[-5, 5], [-7, -1], [3, 7], [7, -7]
],
[
[ 1, 1 ], [ -1, -3 ], [ -3, 2 ], [ 4, -1 ],
[ -5, -2 ], [ 2, 5 ], [ 5, 3 ], [ 3, -5 ],
[ -2, 6 ], [ 0, -7 ], [ -4, -6 ], [ -6, 4 ],
[ -8, 0 ], [ 7, -4 ], [ 6, 7 ], [ -7, -8 ]
[1, 1], [-1, -3], [-3, 2], [4, -1],
[-5, -2], [2, 5], [5, 3], [3, -5],
[-2, 6], [0, -7], [-4, -6], [-6, 4],
[-8, 0], [7, -4], [6, 7], [-7, -8]
],
[
[ -4, -7 ], [ -7, -5 ], [ -3, -5 ], [ -5, -4 ],
[ -1, -4 ], [ -2, -2 ], [ -6, -1 ], [ -4, 0 ],
[ -7, 1 ], [ -1, 2 ], [ -6, 3 ], [ -3, 3 ],
[ -7, 6 ], [ -3, 6 ], [ -5, 7 ], [ -1, 7 ],
[ 5, -7 ], [ 1, -6 ], [ 6, -5 ], [ 4, -4 ],
[ 2, -3 ], [ 7, -2 ], [ 1, -1 ], [ 4, -1 ],
[ 2, 1 ], [ 6, 2 ], [ 0, 4 ], [ 4, 4 ],
[ 2, 5 ], [ 7, 5 ], [ 5, 6 ], [ 3, 7 ]
[-4, -7], [-7, -5], [-3, -5], [-5, -4],
[-1, -4], [-2, -2], [-6, -1], [-4, 0],
[-7, 1], [-1, 2], [-6, 3], [-3, 3],
[-7, 6], [-3, 6], [-5, 7], [-1, 7],
[5, -7], [1, -6], [6, -5], [4, -4],
[2, -3], [7, -2], [1, -1], [4, -1],
[2, 1], [6, 2], [0, 4], [4, 4],
[2, 5], [7, 5], [5, 6], [3, 7]
]
];

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -11,6 +11,7 @@ import { WebGLContext } from '../../mol-gl/webgl/context';
import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { Vec3 } from '../../mol-math/linear-algebra';
import { spiral2d } from '../../mol-math/misc';
import { decodeFloatRGB, unpackRGBAToDepth } from '../../mol-util/float-packing';
import { Camera, ICamera } from '../camera';
import { StereoCamera } from '../camera/stereo';
@@ -88,6 +89,7 @@ export class PickPass {
this.groupPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'pickGroup');
// printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
this.depthPickTarget.bind();
this.renderVariant(renderer, camera, scene, helper, 'depth');
@@ -111,6 +113,8 @@ export class PickHelper {
private pickHeight: number
private halfPickWidth: number
private spiral: [number, number][]
private setupBuffers() {
const bufferSize = this.pickWidth * this.pickHeight * 4;
if (!this.objectBuffer || this.objectBuffer.length !== bufferSize) {
@@ -138,6 +142,8 @@ export class PickHelper {
this.setupBuffers();
}
this.spiral = spiral2d(Math.round(this.pickScale * this.pickPadding));
}
private syncBuffers() {
@@ -177,6 +183,7 @@ export class PickHelper {
renderer.setTransparentBackground(false);
renderer.setDrawingBufferSize(this.pickPass.objectPickTarget.getWidth(), this.pickPass.objectPickTarget.getHeight());
renderer.setPixelRatio(this.pickScale);
if (StereoCamera.is(camera)) {
renderer.setViewport(pickX, pickY, halfPickWidth, pickHeight);
@@ -192,7 +199,7 @@ export class PickHelper {
this.dirty = false;
}
identify(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
private identifyInternal(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
const { webgl, pickScale } = this;
if (webgl.isContextLost) return;
@@ -251,7 +258,14 @@ export class PickHelper {
return { id: { objectId, instanceId, groupId }, position };
}
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private helper: Helper, private pickPass: PickPass, viewport: Viewport) {
identify(x: number, y: number, camera: Camera | StereoCamera): PickData | undefined {
for (const d of this.spiral) {
const pickData = this.identifyInternal(x + d[0], y + d[1], camera);
if (pickData) return pickData;
}
}
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private helper: Helper, private pickPass: PickPass, viewport: Viewport, readonly pickPadding = 1) {
this.setViewport(viewport.x, viewport.y, viewport.width, viewport.height);
}
}

View File

@@ -154,10 +154,10 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
}
function getBlurKernel(kernelSize: number): number[] {
let sigma = kernelSize / 3.0;
let halfKernelSize = Math.floor((kernelSize + 1) / 2);
const sigma = kernelSize / 3.0;
const halfKernelSize = Math.floor((kernelSize + 1) / 2);
let kernel = [];
const kernel = [];
for (let x = 0; x < halfKernelSize; x++) {
kernel.push((1.0 / ((Math.sqrt(2 * Math.PI)) * sigma)) * Math.exp(-x * x / (2 * sigma * sigma)));
}
@@ -166,7 +166,7 @@ function getBlurKernel(kernelSize: number): number[] {
}
function getSamples(vectorSamples: Vec3[], nSamples: number): number[] {
let samples = [];
const samples = [];
for (let i = 0; i < nSamples; i++) {
let scale = (i * i + 2.0 * i + 1) / (nSamples * nSamples);
scale = 0.1 + scale * (1.0 - 0.1);
@@ -241,7 +241,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
export const PostprocessingParams = {
occlusion: PD.MappedStatic('on', {
on: PD.Group({
samples: PD.Numeric(32, {min: 1, max: 256, step: 1}),
samples: PD.Numeric(32, { min: 1, max: 256, step: 1 }),
radius: PD.Numeric(5, { min: 0, max: 10, step: 0.1 }, { description: 'Final radius is 2^x.' }),
bias: PD.Numeric(0.8, { min: 0, max: 3, step: 0.1 }),
blurKernelSize: PD.Numeric(15, { min: 1, max: 25, step: 2 }),
@@ -314,7 +314,7 @@ export class PostprocessingPass {
this.randomHemisphereVector = [];
for (let i = 0; i < 256; i++) {
let v = Vec3();
const v = Vec3();
v[0] = Math.random() * 2.0 - 1.0;
v[1] = Math.random() * 2.0 - 1.0;
v[2] = Math.random();
@@ -376,7 +376,7 @@ export class PostprocessingPass {
const outlinesEnabled = props.outline.name === 'on';
const occlusionEnabled = props.occlusion.name === 'on';
let invProjection = Mat4.identity();
const invProjection = Mat4.identity();
Mat4.invert(invProjection, camera.projection);
if (props.occlusion.name === 'on') {

View File

@@ -25,8 +25,8 @@ import { Viewport } from '../camera/util';
import { isDebugMode } from '../../mol-util/debug';
export const SmaaParams = {
edgeThreshold:PD.Numeric(0.1, { min: 0.05, max: 0.15, step: 0.01 }),
maxSearchSteps:PD.Numeric(16, { min: 0, max: 32, step: 1 }),
edgeThreshold: PD.Numeric(0.1, { min: 0.05, max: 0.15, step: 0.01 }),
maxSearchSteps: PD.Numeric(16, { min: 0, max: 32, step: 1 }),
};
export type SmaaProps = PD.Values<typeof SmaaParams>

View File

@@ -12,7 +12,7 @@ export function setCanvasSize(canvas: HTMLCanvasElement, width: number, height:
}
/** Resize canvas to container element taking `devicePixelRatio` into account */
export function resizeCanvas (canvas: HTMLCanvasElement, container: HTMLElement, scale = 1) {
export function resizeCanvas(canvas: HTMLCanvasElement, container: HTMLElement, scale = 1) {
let width = window.innerWidth;
let height = window.innerHeight;
if (container !== document.body) {

View File

@@ -100,7 +100,7 @@ describe('table', () => {
n: Column.ofArray({ array: ['row1', 'row2'], schema: Column.Schema.str }),
});
const s = { x: Column.Schema.int, y: Column.Schema.int };
const picked = Table.pickColumns(s, t, { y: Column.ofArray({ array: [3, 4], schema: Column.Schema.int })});
const picked = Table.pickColumns(s, t, { y: Column.ofArray({ array: [3, 4], schema: Column.Schema.int }) });
expect(picked._columns).toEqual(['x', 'y']);
expect(picked._rowCount).toEqual(2);
expect(picked.x.toArray()).toEqual([10, -1]);

View File

@@ -33,21 +33,21 @@ describe('linked list', () => {
expect(list.count).toBe(5);
});
it ('remove', () => {
it('remove', () => {
const list = create([1, 2, 3, 4]);
let fst = list.removeFirst();
const fst = list.removeFirst();
expect(fst).toBe(1);
expect(list.last!.value).toBe(4);
expect(list.count).toBe(3);
expect(toArray(list)).toEqual([2, 3, 4]);
let last = list.removeLast();
const last = list.removeLast();
expect(last).toBe(4);
expect(list.last!.value).toBe(3);
expect(list.count).toBe(2);
expect(toArray(list)).toEqual([2, 3]);
let n3 = list.find(3)!;
const n3 = list.find(3)!;
list.remove(n3);
expect(list.first!.value).toBe(2);
expect(list.last!.value).toBe(2);

View File

@@ -16,7 +16,7 @@ export const start = Tuple.fst;
export const end = Tuple.snd;
export const min = Tuple.fst;
export function max(i: Tuple) { return Tuple.snd(i) - 1; }
export function size(i: Tuple) { return Tuple.snd(i) - Tuple.fst(i); }
export const size = Tuple.diff;
export const hashCode = Tuple.hashCode;
export const toString = Tuple.toString;

View File

@@ -19,7 +19,7 @@ export const ofBounds = I.ofBounds;
export function ofSortedArray(xs: Nums): OrderedSetImpl {
if (!xs.length) return Empty;
// check if the array is just a range
if (xs[xs.length - 1] - xs[0] + 1 === xs.length) return I.ofRange(xs[0], xs[xs.length - 1]);
if (S.isRange(xs)) return I.ofRange(xs[0], xs[xs.length - 1]);
return xs as any;
}

View File

@@ -22,9 +22,10 @@ export function ofRange(min: number, max: number) {
return ret;
}
export function is(xs: any): xs is Nums { return xs && (Array.isArray(xs) || !!xs.buffer); }
export function isRange(xs: Nums) { return xs[xs.length - 1] - xs[0] + 1 === xs.length; }
export function start(xs: Nums) { return xs[0]; }
export function end(xs: Nums) { return xs[xs.length - 1] + 1; }
export function end(xs: Nums) { return xs[xs.length - 1] + 1; }
export function min(xs: Nums) { return xs[0]; }
export function max(xs: Nums) { return xs[xs.length - 1]; }
export function size(xs: Nums) { return xs.length; }
@@ -59,9 +60,11 @@ export function getAt(xs: Nums, i: number) { return xs[i]; }
export function areEqual(a: Nums, b: Nums) {
if (a === b) return true;
const aSize = a.length;
let aSize = a.length;
if (aSize !== b.length || a[0] !== b[0] || a[aSize - 1] !== b[aSize - 1]) return false;
for (let i = 0; i < aSize; i++) {
if (isRange(a)) return true;
aSize--;
for (let i = 1; i < aSize; i++) {
if (a[i] !== b[i]) return false;
}
return true;
@@ -340,7 +343,7 @@ export function deduplicate(xs: Nums) {
}
export function indicesOf(a: Nums, b: Nums): Nums {
if (a === b) return ofSortedArray(createRangeArray(0, a.length - 1));
if (areEqual(a, b)) return ofSortedArray(createRangeArray(0, a.length - 1));
const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
let i = sI, j = sJ;

View File

@@ -17,6 +17,7 @@ namespace SortedArray {
/** create sorted array [min, max) (it does NOT contain the max value) */
export const ofBounds: <T extends number = number>(min: T, max: T) => SortedArray<T> = (min, max) => Impl.ofRange(min, max - 1) as any;
export const is: <T extends number = number>(v: any) => v is SortedArray<T> = Impl.is as any;
export const isRange: <T extends number = number>(array: ArrayLike<number>) => boolean = Impl.isRange as any;
export const has: <T extends number = number>(array: SortedArray<T>, x: T) => boolean = Impl.has as any;
/** Returns the index of `x` in `set` or -1 if not found. */

View File

@@ -15,7 +15,7 @@ interface IntTuple { '@type': 'int-tuple' }
namespace IntTuple {
export const Zero: IntTuple = 0 as any;
const { _int32, _float64, _int32_1, _float64_1 } = (function() {
const { _int32, _float64, _int32_1, _float64_1 } = (function () {
const data = new ArrayBuffer(8);
const data_1 = new ArrayBuffer(8);
return {
@@ -36,6 +36,12 @@ namespace IntTuple {
return _float64[0] as any;
}
/** snd - fst */
export function diff(t: IntTuple) {
_float64[0] = t as any;
return _int32[1] - _int32[0];
}
export function fst(t: IntTuple): number {
_float64[0] = t as any;
return _int32[0];

View File

@@ -8,14 +8,14 @@ import { ChunkedArray } from '../chunked-array';
describe('Chunked Array', () => {
it('creation', () => {
const arr = ChunkedArray.create<number, 2>(Array, 2, 2);
const arr = ChunkedArray.create<number, 2>(Array, 2, 2);
ChunkedArray.add2(arr, 1, 2);
ChunkedArray.add2(arr, 3, 4);
expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]);
});
it('initial', () => {
const arr = ChunkedArray.create(Int32Array, 2, 6, new Int32Array([1, 2, 3, 4]));
const arr = ChunkedArray.create(Int32Array, 2, 6, new Int32Array([1, 2, 3, 4]));
ChunkedArray.add2(arr, 4, 3);
ChunkedArray.add2(arr, 2, 1);
ChunkedArray.add2(arr, 5, 6);
@@ -23,13 +23,13 @@ describe('Chunked Array', () => {
});
it('add many', () => {
const arr = ChunkedArray.create<number, 2>(Array, 2, 2);
const arr = ChunkedArray.create<number, 2>(Array, 2, 2);
ChunkedArray.addMany(arr, [1, 2, 3, 4]);
expect(ChunkedArray.compact(arr)).toEqual([1, 2, 3, 4]);
});
it('resize', () => {
const arr = ChunkedArray.create<number, 2>(Int32Array, 2, 2);
const arr = ChunkedArray.create<number, 2>(Int32Array, 2, 2);
ChunkedArray.add2(arr, 1, 2);
ChunkedArray.add2(arr, 3, 4);
ChunkedArray.add2(arr, 5, 6);
@@ -39,7 +39,7 @@ describe('Chunked Array', () => {
});
it('resize-fraction', () => {
const arr = ChunkedArray.create<number, 2>(Int32Array, 2, 2.5);
const arr = ChunkedArray.create<number, 2>(Int32Array, 2, 2.5);
ChunkedArray.add2(arr, 1, 2);
ChunkedArray.add2(arr, 3, 4);
ChunkedArray.add2(arr, 5, 6);

View File

@@ -36,7 +36,7 @@ namespace ChunkedArray {
}
function allocateNext(array: ChunkedArray<any, any>) {
let nextSize = array.growBy * array.elementSize;
const nextSize = array.growBy * array.elementSize;
array.currentSize = nextSize;
array.currentIndex = 0;
array.currentChunk = new array.ctor(nextSize);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2021 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>
@@ -70,6 +70,15 @@ export function sortedCantorPairing(a: number, b: number) {
return a < b ? cantorPairing(a, b) : cantorPairing(b, a);
}
export function invertCantorPairing(out: [number, number], z: number) {
const w = Math.floor((Math.sqrt(8 * z + 1) - 1) / 2);
const t = (w * w + w) / 2;
const y = z - t;
out[0] = w - y;
out[1] = y;
return out;
}
/**
* 32 bit FNV-1a hash, see http://isthe.com/chongo/tech/comp/fnv/
*/

View File

@@ -52,8 +52,8 @@ export namespace BaseGeometry {
if (!transform) transform = createIdentityTransform();
const locationIterator = LocationIterator(1, transform.instanceCount.ref.value, 1, () => NullLocation, false, () => false);
const theme: Theme = {
color: UniformColorTheme({}, { value: colorValue}),
size: UniformSizeTheme({}, { value: sizeValue})
color: UniformColorTheme({}, { value: colorValue }),
size: UniformSizeTheme({}, { value: sizeValue })
};
return { transform, locationIterator, theme };
}

View File

@@ -6,7 +6,7 @@
import { ValueCell } from '../../../mol-util';
import { Mat4, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { transformPositionArray, GroupMapping, createGroupMapping } from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';

View File

@@ -9,7 +9,7 @@ import { LocationIterator, PositionLocation } from '../../../mol-geo/util/locati
import { RenderableState } from '../../../mol-gl/renderable';
import { DirectVolumeValues } from '../../../mol-gl/renderable/direct-volume';
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Texture } from '../../../mol-gl/webgl/texture';
import { createNullTexture, Texture } from '../../../mol-gl/webgl/texture';
import { Box3D, Sphere3D } from '../../../mol-math/geometry';
import { Mat4, Vec2, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { Theme } from '../../../mol-theme/theme';
@@ -129,7 +129,15 @@ export namespace DirectVolume {
}
export function createEmpty(directVolume?: DirectVolume): DirectVolume {
return {} as DirectVolume; // TODO
const bbox = Box3D();
const gridDimension = Vec3();
const transform = Mat4.identity();
const unitToCartn = Mat4.identity();
const cellDim = Vec3();
const texture = createNullTexture();
const stats = Grid.One.stats;
const packedGroup = false;
return create(bbox, gridDimension, transform, unitToCartn, cellDim, texture, stats, packedGroup, directVolume);
}
export function createRenderModeParam(stats?: Grid['stats']) {

View File

@@ -7,7 +7,7 @@
import { hashFnv32a } from '../../../mol-data/util';
import { LocationIterator } from '../../../mol-geo/util/location-iterator';
import { RenderableState } from '../../../mol-gl/renderable';
import { calculateTransformBoundingSphere, TextureImage } from '../../../mol-gl/renderable/util';
import { calculateTransformBoundingSphere, createTextureImage, TextureImage } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Vec2, Vec4, Vec3 } from '../../../mol-math/linear-algebra';
import { Theme } from '../../../mol-theme/theme';
@@ -113,7 +113,10 @@ namespace Image {
}
export function createEmpty(image?: Image): Image {
return {} as Image; // TODO
const imageTexture = createTextureImage(0, 4, Uint8Array);
const corners = image ? image.cornerBuffer.ref.value : new Float32Array(8 * 3);
const groupTexture = createTextureImage(0, 4, Uint8Array);
return create(imageTexture, corners, groupTexture, image);
}
export const Params = {

View File

@@ -6,7 +6,7 @@
import { ValueCell } from '../../../mol-util';
import { Mat4, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { transformPositionArray, GroupMapping, createGroupMapping } from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -164,7 +164,7 @@ export namespace Lines {
export const Params = {
...BaseGeometry.Params,
sizeFactor: PD.Numeric(1.5, { min: 0, max: 10, step: 0.1 }),
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
lineSizeAttenuation: PD.Boolean(false),
};
export type Params = typeof Params

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -9,20 +9,75 @@ import { Vec2 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
export type MarkerData = {
uMarker: ValueCell<number>,
tMarker: ValueCell<TextureImage<Uint8Array>>
uMarkerTexDim: ValueCell<Vec2>
dMarkerType: ValueCell<string>,
markerAverage: ValueCell<number>
markerStatus: ValueCell<number>
}
const MarkerCountLut = new Uint8Array(0x0303 + 1);
MarkerCountLut[0x0001] = 1;
MarkerCountLut[0x0002] = 1;
MarkerCountLut[0x0003] = 1;
MarkerCountLut[0x0100] = 1;
MarkerCountLut[0x0200] = 1;
MarkerCountLut[0x0300] = 1;
MarkerCountLut[0x0101] = 2;
MarkerCountLut[0x0201] = 2;
MarkerCountLut[0x0301] = 2;
MarkerCountLut[0x0102] = 2;
MarkerCountLut[0x0202] = 2;
MarkerCountLut[0x0302] = 2;
MarkerCountLut[0x0103] = 2;
MarkerCountLut[0x0203] = 2;
MarkerCountLut[0x0303] = 2;
/**
* Calculates the average number of entries that have any marker flag set.
*
* For alternative implementations and performance tests see
* `src\perf-tests\markers-average.ts`.
*/
export function getMarkersAverage(array: Uint8Array, count: number): number {
if (count === 0) return 0;
const view = new Uint32Array(array.buffer, 0, array.buffer.byteLength >> 2);
const viewEnd = (count - 4) >> 2;
const backStart = 4 * viewEnd;
let sum = 0;
for (let i = 0; i < viewEnd; ++i) {
const v = view[i];
sum += MarkerCountLut[v & 0xFFFF] + MarkerCountLut[v >> 16];
}
for (let i = backStart; i < count; ++i) {
sum += array[i] && 1;
}
return sum / count;
}
export function createMarkers(count: number, markerData?: MarkerData): MarkerData {
const markers = createTextureImage(Math.max(1, count), 1, Uint8Array, markerData && markerData.tMarker.ref.value.array);
const average = getMarkersAverage(markers.array, count);
const status = average === 0 ? 0 : -1;
if (markerData) {
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, markers);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
ValueCell.updateIfChanged(markerData.dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, average);
ValueCell.updateIfChanged(markerData.markerStatus, status);
return markerData;
} else {
return {
uMarker: ValueCell.create(0),
tMarker: ValueCell.create(markers),
uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
markerAverage: ValueCell.create(average),
markerStatus: ValueCell.create(status),
dMarkerType: ValueCell.create('uniform'),
};
}
}
@@ -30,13 +85,21 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
const emptyMarkerTexture = { array: new Uint8Array(1), width: 1, height: 1 };
export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
if (markerData) {
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, emptyMarkerTexture);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(1, 1));
ValueCell.updateIfChanged(markerData.dMarkerType, 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, 0);
ValueCell.updateIfChanged(markerData.markerStatus, 0);
return markerData;
} else {
return {
uMarker: ValueCell.create(0),
tMarker: ValueCell.create(emptyMarkerTexture),
uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
markerAverage: ValueCell.create(0),
markerStatus: ValueCell.create(0),
dMarkerType: ValueCell.create('uniform'),
};
}
}

View File

@@ -21,7 +21,6 @@ const tmpCylinderCenter = Vec3();
const tmpCylinderMat = Mat4();
const tmpCylinderMatRot = Mat4();
const tmpCylinderScale = Vec3();
const tmpCylinderMatScale = Mat4();
const tmpCylinderStart = Vec3();
const tmpUp = Vec3();
@@ -32,9 +31,9 @@ function setCylinderMat(m: Mat4, start: Vec3, dir: Vec3, length: number, matchDi
// direction so the triangles of adjacent cylinder will line up
if (matchDir) Vec3.matchDirection(tmpUp, up, tmpCylinderMatDir);
else Vec3.copy(tmpUp, up);
Mat4.fromScaling(tmpCylinderMatScale, Vec3.set(tmpCylinderScale, 1, length, 1));
Vec3.set(tmpCylinderScale, 1, length, 1);
Vec3.makeRotation(tmpCylinderMatRot, tmpUp, tmpCylinderMatDir);
Mat4.mul(m, tmpCylinderMatRot, tmpCylinderMatScale);
Mat4.scale(m, tmpCylinderMatRot, tmpCylinderScale);
return Mat4.setTranslation(m, tmpCylinderCenter);
}

View File

@@ -36,7 +36,7 @@ const torsionVector = Vec3();
export function addRibbon(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number) {
const { currentGroup, vertices, normals, indices, groups } = state;
let vertexCount = vertices.elementCount;
const vertexCount = vertices.elementCount;
let offsetLength = 0;
if (arrowHeight > 0) {

View File

@@ -85,7 +85,7 @@ function addCap(offset: number, state: MeshBuilder.State, controlPoints: ArrayLi
export function addSheet(state: MeshBuilder.State, controlPoints: ArrayLike<number>, normalVectors: ArrayLike<number>, binormalVectors: ArrayLike<number>, linearSegments: number, widthValues: ArrayLike<number>, heightValues: ArrayLike<number>, arrowHeight: number, startCap: boolean, endCap: boolean) {
const { currentGroup, vertices, normals, indices, groups } = state;
let vertexCount = vertices.elementCount;
const vertexCount = vertices.elementCount;
let offsetLength = 0;
if (arrowHeight > 0) {

View File

@@ -39,7 +39,7 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
const { min } = box;
const [ xn, yn ] = gridDim;
const [xn, yn] = gridDim;
const { width, height } = getVolumeTexture2dLayout(gridDim);
// console.log({ width, height, dim });
@@ -104,7 +104,7 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
const d = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (d > p) continue;
let s = p - d;
const s = p - d;
const index = getIndex(xi, yi, zi);
data[index] += r * s;
data[index + 1] += g * s;

View File

@@ -61,8 +61,8 @@ export namespace MeshBuilder {
v3triangleNormal(tmpV, a, b, c);
for (let i = 0; i < 3; ++i) {
caAdd3(normals, tmpV[0], tmpV[1], tmpV[2]); // normal
caAdd(groups, currentGroup); // group
caAdd3(normals, tmpV[0], tmpV[1], tmpV[2]); // normal
caAdd(groups, currentGroup); // group
}
caAdd3(indices, offset, offset + 1, offset + 2);
}
@@ -152,7 +152,7 @@ export namespace MeshBuilder {
});
}
export function getMesh (state: State): Mesh {
export function getMesh(state: State): Mesh {
const { vertices, normals, indices, groups, mesh } = state;
const vb = ChunkedArray.compact(vertices, true) as Float32Array;
const ib = ChunkedArray.compact(indices, true) as Uint32Array;

View File

@@ -8,13 +8,13 @@
import { ValueCell } from '../../../mol-util';
import { Vec3, Mat4, Mat3, Vec4 } from '../../../mol-math/linear-algebra';
import { Sphere3D } from '../../../mol-math/geometry';
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} 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, PositionLocation } from '../../util/location-iterator';
import { createColors } from '../color-data';
import { ChunkedArray, hashFnv32a } from '../../../mol-data/util';
import { ChunkedArray, hashFnv32a, invertCantorPairing, sortedCantorPairing } from '../../../mol-data/util';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Theme } from '../../../mol-theme/theme';
@@ -25,6 +25,8 @@ import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { createEmptyClipping } from '../clipping-data';
import { RenderableState } from '../../../mol-gl/renderable';
import { arraySetAdd } from '../../../mol-util/array';
import { degToRad } from '../../../mol-math/misc';
export interface Mesh {
readonly kind: 'mesh',
@@ -332,10 +334,10 @@ export namespace Mesh {
mesh.vertexCount = newVertexCount;
mesh.triangleCount = newTriangleCount;
ValueCell.update(vertexBuffer, newVb) as ValueCell<Float32Array>;
ValueCell.update(groupBuffer, newGb) as ValueCell<Float32Array>;
ValueCell.update(indexBuffer, newIb) as ValueCell<Uint32Array>;
ValueCell.update(normalBuffer, newNb) as ValueCell<Float32Array>;
ValueCell.update(vertexBuffer, newVb);
ValueCell.update(groupBuffer, newGb);
ValueCell.update(indexBuffer, newIb);
ValueCell.update(normalBuffer, newNb);
// keep some original data, e.g., for geometry export
(mesh.meta.originalData as OriginalData) = { indexBuffer: ib, vertexCount, triangleCount };
@@ -345,6 +347,276 @@ export namespace Mesh {
//
function getNeighboursMap(mesh: Mesh) {
const { vertexCount, triangleCount } = mesh;
const elements = mesh.indexBuffer.ref.value;
const neighboursMap: number[][] = [];
for (let i = 0; i < vertexCount; ++i) {
neighboursMap[i] = [];
}
for (let i = 0; i < triangleCount; ++i) {
const v1 = elements[i * 3];
const v2 = elements[i * 3 + 1];
const v3 = elements[i * 3 + 2];
arraySetAdd(neighboursMap[v1], v2);
arraySetAdd(neighboursMap[v1], v3);
arraySetAdd(neighboursMap[v2], v1);
arraySetAdd(neighboursMap[v2], v3);
arraySetAdd(neighboursMap[v3], v1);
arraySetAdd(neighboursMap[v3], v2);
}
return neighboursMap;
}
function getEdgeCounts(mesh: Mesh) {
const { triangleCount } = mesh;
const elements = mesh.indexBuffer.ref.value;
const edgeCounts = new Map<number, number>();
const add = (a: number, b: number) => {
const z = sortedCantorPairing(a, b);
const c = edgeCounts.get(z) || 0;
edgeCounts.set(z, c + 1);
};
for (let i = 0; i < triangleCount; ++i) {
const a = elements[i * 3];
const b = elements[i * 3 + 1];
const c = elements[i * 3 + 2];
add(a, b); add(a, c); add(b, c);
}
return edgeCounts;
}
function getBorderVertices(edgeCounts: Map<number, number>) {
const borderVertices = new Set<number>();
const pair: [number, number] = [0, 0];
edgeCounts.forEach((c, z) => {
if (c === 1) {
invertCantorPairing(pair, z);
borderVertices.add(pair[0]);
borderVertices.add(pair[1]);
}
});
return borderVertices;
}
function getBorderNeighboursMap(neighboursMap: number[][], borderVertices: Set<number>, edgeCounts: Map<number, number>) {
const borderNeighboursMap = new Map<number, number[]>();
const add = (v: number, nb: number) => {
if (borderNeighboursMap.has(v)) arraySetAdd(borderNeighboursMap.get(v)!, nb);
else borderNeighboursMap.set(v, [nb]);
};
borderVertices.forEach(v => {
const neighbours = neighboursMap[v];
for (const nb of neighbours) {
if (borderVertices.has(nb) && edgeCounts.get(sortedCantorPairing(v, nb)) === 1) {
add(v, nb);
}
}
});
return borderNeighboursMap;
}
function trimEdges(mesh: Mesh, neighboursMap: number[][]) {
const { indexBuffer, triangleCount } = mesh;
const ib = indexBuffer.ref.value;
// new
const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
let newTriangleCount = 0;
for (let i = 0; i < triangleCount; ++i) {
const a = ib[i * 3];
const b = ib[i * 3 + 1];
const c = ib[i * 3 + 2];
if (neighboursMap[a].length === 2 ||
neighboursMap[b].length === 2 ||
neighboursMap[c].length === 2) continue;
ChunkedArray.add3(index, a, b, c);
newTriangleCount += 1;
}
const newIb = ChunkedArray.compact(index);
mesh.triangleCount = newTriangleCount;
ValueCell.update(indexBuffer, newIb);
return mesh;
}
function fillEdges(mesh: Mesh, neighboursMap: number[][], borderNeighboursMap: Map<number, number[]>, maxLengthSquared: number) {
const { vertexBuffer, indexBuffer, normalBuffer, triangleCount } = mesh;
const vb = vertexBuffer.ref.value;
const ib = indexBuffer.ref.value;
const nb = normalBuffer.ref.value;
// new
const index = ChunkedArray.create(Uint32Array, 3, 1024, triangleCount);
let newTriangleCount = 0;
for (let i = 0; i < triangleCount; ++i) {
ChunkedArray.add3(index, ib[i * 3], ib[i * 3 + 1], ib[i * 3 + 2]);
newTriangleCount += 1;
}
const vA = Vec3();
const vB = Vec3();
const vC = Vec3();
const vD = Vec3();
const vAB = Vec3();
const vAC = Vec3();
const vAD = Vec3();
const vABC = Vec3();
const vAN = Vec3();
const vN = Vec3();
const AngleThreshold = degToRad(120);
const added = new Set<number>();
const indices = Array.from(borderNeighboursMap.keys())
.filter(v => borderNeighboursMap.get(v)!.length < 2)
.map(v => {
const bnd = borderNeighboursMap.get(v)!;
Vec3.fromArray(vA, vb, v * 3);
Vec3.fromArray(vB, vb, bnd[0] * 3);
Vec3.fromArray(vC, vb, bnd[1] * 3);
Vec3.sub(vAB, vB, vA);
Vec3.sub(vAC, vC, vA);
return [v, Vec3.angle(vAB, vAC)];
});
// start with the smallest angle
indices.sort(([, a], [, b]) => a - b);
for (const [v, angle] of indices) {
if (added.has(v) || angle > AngleThreshold) continue;
const nbs = borderNeighboursMap.get(v)!;
if (neighboursMap[nbs[0]].includes(nbs[1]) &&
!borderNeighboursMap.get(nbs[0])?.includes(nbs[1])
) continue;
Vec3.fromArray(vA, vb, v * 3);
Vec3.fromArray(vB, vb, nbs[0] * 3);
Vec3.fromArray(vC, vb, nbs[1] * 3);
Vec3.sub(vAB, vB, vA);
Vec3.sub(vAC, vC, vA);
Vec3.add(vABC, vAB, vAC);
if (Vec3.squaredDistance(vA, vB) >= maxLengthSquared) continue;
let add = false;
for (const nb of neighboursMap[v]) {
if (nbs.includes(nb)) continue;
Vec3.fromArray(vD, vb, nb * 3);
Vec3.sub(vAD, vD, vA);
if (Vec3.dot(vABC, vAD) < 0) {
add = true;
break;
}
}
if (!add) continue;
Vec3.fromArray(vAN, nb, v * 3);
Vec3.triangleNormal(vN, vA, vB, vC);
if (Vec3.dot(vN, vAN) > 0) {
ChunkedArray.add3(index, v, nbs[0], nbs[1]);
} else {
ChunkedArray.add3(index, nbs[1], nbs[0], v);
}
added.add(v); added.add(nbs[0]); added.add(nbs[1]);
newTriangleCount += 1;
}
const newIb = ChunkedArray.compact(index);
mesh.triangleCount = newTriangleCount;
ValueCell.update(indexBuffer, newIb);
return mesh;
}
function laplacianEdgeSmoothing(mesh: Mesh, borderNeighboursMap: Map<number, number[]>, options: { iterations: number, lambda: number }) {
const { iterations, lambda } = options;
const a = Vec3();
const b = Vec3();
const c = Vec3();
const t = Vec3();
const mu = -lambda;
let dst = new Float32Array(mesh.vertexBuffer.ref.value.length);
const step = (f: number) => {
const pos = mesh.vertexBuffer.ref.value;
dst.set(pos);
borderNeighboursMap.forEach((nbs, v) => {
if (nbs.length !== 2) return;
Vec3.fromArray(a, pos, v * 3);
Vec3.fromArray(b, pos, nbs[0] * 3);
Vec3.fromArray(c, pos, nbs[1] * 3);
const wab = 1 / Vec3.distance(a, b);
const wac = 1 / Vec3.distance(a, c);
Vec3.scale(b, b, wab);
Vec3.scale(c, c, wac);
Vec3.add(t, b, c);
Vec3.scale(t, t, 1 / (wab + wac));
Vec3.sub(t, t, a);
Vec3.scale(t, t, f);
Vec3.add(t, a, t);
Vec3.toArray(t, dst, v * 3);
});
const tmp = mesh.vertexBuffer.ref.value;
ValueCell.update(mesh.vertexBuffer, dst);
dst = tmp;
};
for (let k = 0; k < iterations; ++k) {
step(lambda);
step(mu);
}
}
export function smoothEdges(mesh: Mesh, options: { iterations: number, maxNewEdgeLength: number }) {
trimEdges(mesh, getNeighboursMap(mesh));
for (let k = 0; k < 10; ++k) {
const oldTriangleCount = mesh.triangleCount;
const edgeCounts = getEdgeCounts(mesh);
const neighboursMap = getNeighboursMap(mesh);
const borderVertices = getBorderVertices(edgeCounts);
const borderNeighboursMap = getBorderNeighboursMap(neighboursMap, borderVertices, edgeCounts);
fillEdges(mesh, neighboursMap, borderNeighboursMap, options.maxNewEdgeLength * options.maxNewEdgeLength);
if (mesh.triangleCount === oldTriangleCount) break;
}
const edgeCounts = getEdgeCounts(mesh);
const neighboursMap = getNeighboursMap(mesh);
const borderVertices = getBorderVertices(edgeCounts);
const borderNeighboursMap = getBorderNeighboursMap(neighboursMap, borderVertices, edgeCounts);
laplacianEdgeSmoothing(mesh, borderNeighboursMap, { iterations: options.iterations, lambda: 0.5 });
return mesh;
}
//
export const Params = {
...BaseGeometry.Params,
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
@@ -387,9 +659,6 @@ export namespace Mesh {
function createValues(mesh: Mesh, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): MeshValues {
const { instanceCount, groupCount } = locationIt;
if (instanceCount !== transform.instanceCount.ref.value) {
throw new Error('instanceCount values in TransformData and LocationIterator differ');
}
const positionIt = createPositionIterator(mesh, transform);
const color = createColors(locationIt, positionIt, theme.color);

View File

@@ -1,12 +1,12 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 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, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { transformPositionArray, GroupMapping, createGroupMapping } from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -117,12 +117,19 @@ export namespace Points {
//
export const StyleTypes = {
'square': 'Square',
'circle': 'Circle',
'fuzzy': 'Fuzzy',
};
export type StyleTypes = keyof typeof StyleTypes;
export const StyleTypeNames = Object.keys(StyleTypes) as StyleTypes[];
export const Params = {
...BaseGeometry.Params,
sizeFactor: PD.Numeric(1.5, { min: 0, max: 10, step: 0.1 }),
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
pointSizeAttenuation: PD.Boolean(false),
pointFilledCircle: PD.Boolean(false),
pointEdgeBleach: PD.Numeric(0.2, { min: 0, max: 1, step: 0.05 }),
pointStyle: PD.Select('square', PD.objectToOptions(StyleTypes)),
};
export type Params = typeof Params
@@ -189,8 +196,7 @@ export namespace Points {
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor),
dPointSizeAttenuation: ValueCell.create(props.pointSizeAttenuation),
dPointFilledCircle: ValueCell.create(props.pointFilledCircle),
uPointEdgeBleach: ValueCell.create(props.pointEdgeBleach),
dPointStyle: ValueCell.create(props.pointStyle),
};
}
@@ -204,8 +210,7 @@ export namespace Points {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor);
ValueCell.updateIfChanged(values.dPointSizeAttenuation, props.pointSizeAttenuation);
ValueCell.updateIfChanged(values.dPointFilledCircle, props.pointFilledCircle);
ValueCell.updateIfChanged(values.uPointEdgeBleach, props.pointEdgeBleach);
ValueCell.updateIfChanged(values.dPointStyle, props.pointStyle);
}
function updateBoundingSphere(values: PointsValues, points: Points) {
@@ -229,10 +234,7 @@ export namespace Points {
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = state.opaque && (
!props.pointFilledCircle ||
(props.pointFilledCircle && props.pointEdgeBleach === 0)
);
state.opaque = state.opaque && props.pointStyle !== 'fuzzy';
state.writeDepth = state.opaque;
}
}

View File

@@ -98,7 +98,7 @@ export function createTextureSize(sizes: TextureImage<Uint8Array>, type: SizeTyp
/** Creates size texture with size for each instance/unit */
export function createInstanceSize(locationIt: LocationIterator, sizeFn: LocationSize, sizeData?: SizeData): SizeData {
const { instanceCount} = locationIt;
const { instanceCount } = locationIt;
const sizes = createTextureImage(Math.max(1, instanceCount), 3, Uint8Array, sizeData && sizeData.tSize.ref.value.array);
locationIt.reset();
while (locationIt.hasNext && !locationIt.isNextNewInstance) {

View File

@@ -163,9 +163,6 @@ export namespace Spheres {
function createValues(spheres: Spheres, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): SpheresValues {
const { instanceCount, groupCount } = locationIt;
if (instanceCount !== transform.instanceCount.ref.value) {
throw new Error('instanceCount values in TransformData and LocationIterator differ');
}
const positionIt = createPositionIterator(spheres, transform);
const color = createColors(locationIt, positionIt, theme.color);

View File

@@ -10,7 +10,7 @@ import { createTextureImage, TextureImage } from '../../../mol-gl/renderable/uti
const TextAtlasCache: { [k: string]: FontAtlas } = {};
export function getFontAtlas (props: Partial<FontAtlasProps>) {
export function getFontAtlas(props: Partial<FontAtlasProps>) {
const hash = JSON.stringify(props);
if (TextAtlasCache[hash] === undefined) {
TextAtlasCache[hash] = new FontAtlas(props);
@@ -69,7 +69,7 @@ export class FontAtlas {
private readonly maxWidth: number
private readonly middle: number
constructor (props: Partial<FontAtlasProps> = {}) {
constructor(props: Partial<FontAtlasProps> = {}) {
const p = { ...PD.getDefaultValues(FontAtlasParams), ...props };
this.props = p;
@@ -110,7 +110,7 @@ export class FontAtlas {
this.placeholder = this.get(String.fromCharCode(0xFFFD));
}
get (char: string) {
get(char: string) {
if (this.mapped[char] === undefined) {
this.draw(char);
@@ -144,7 +144,7 @@ export class FontAtlas {
return this.mapped[char];
}
draw (char: string) {
draw(char: string) {
const h = this.lineHeight;
const ctx = this.scratchContext;
const data = this.scratchData;

View File

@@ -69,7 +69,7 @@ export namespace TextBuilder {
} else if (attachment.startsWith('middle')) {
yShift = bHeight / 2;
} else {
yShift = 0; // "bottom"
yShift = 0; // "bottom"
}
// horizontal
if (attachment.endsWith('right')) {
@@ -77,7 +77,7 @@ export namespace TextBuilder {
} else if (attachment.endsWith('center')) {
xShift = bWidth / 2;
} else {
xShift = 0; // "left"
xShift = 0; // "left"
}
if (tether) {

View File

@@ -206,9 +206,6 @@ export namespace Text {
function createValues(text: Text, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): TextValues {
const { instanceCount, groupCount } = locationIt;
if (instanceCount !== transform.instanceCount.ref.value) {
throw new Error('instanceCount values in TransformData and LocationIterator differ');
}
const positionIt = createPositionIterator(text, transform);
const color = createColors(locationIt, positionIt, theme.color);

View File

@@ -259,7 +259,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
const { min } = box;
const [ dx, dy, dz ] = gridDim;
const [dx, dy, dz] = gridDim;
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
// console.log({ width, height, texCols, dim, resolution });

View File

@@ -19,7 +19,7 @@ import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { TextureMeshValues } from '../../../mol-gl/renderable/texture-mesh';
import { calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Texture } from '../../../mol-gl/webgl/texture';
import { createNullTexture, Texture } from '../../../mol-gl/webgl/texture';
import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
import { createEmptyClipping } from '../clipping-data';
import { NullLocation } from '../../../mol-model/location';
@@ -97,7 +97,11 @@ export namespace TextureMesh {
}
export function createEmpty(textureMesh?: TextureMesh): TextureMesh {
return {} as TextureMesh; // TODO
const vt = textureMesh ? textureMesh.vertexTexture.ref.value : createNullTexture();
const gt = textureMesh ? textureMesh.groupTexture.ref.value : createNullTexture();
const nt = textureMesh ? textureMesh.normalTexture.ref.value : createNullTexture();
const bs = textureMesh ? textureMesh.boundingSphere : Sphere3D();
return create(0, 0, vt, gt, nt, bs, textureMesh);
}
export const Params = {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -43,12 +43,13 @@ export function createTransform(transformArray: Float32Array, instanceCount: num
if (transformData) {
ValueCell.update(transformData.matrix, transformData.matrix.ref.value);
ValueCell.update(transformData.transform, transformArray);
const transform = transformData.transform.ref.value.length >= instanceCount * 16 ? transformData.transform.ref.value : new Float32Array(instanceCount * 16);
transform.set(transformArray);
ValueCell.update(transformData.transform, transform);
ValueCell.updateIfChanged(transformData.uInstanceCount, instanceCount);
ValueCell.updateIfChanged(transformData.instanceCount, instanceCount);
const aTransform = transformData.aTransform.ref.value.length >= instanceCount * 16 ? transformData.aTransform.ref.value : new Float32Array(instanceCount * 16);
aTransform.set(transformArray);
ValueCell.update(transformData.aTransform, aTransform);
// Note that this sets `extraTransform` to identity transforms
@@ -59,14 +60,11 @@ export function createTransform(transformArray: Float32Array, instanceCount: num
ValueCell.update(transformData.aInstance, fillSerial(aInstance, instanceCount));
ValueCell.update(transformData.hasReflection, hasReflection);
updateTransformData(transformData);
return transformData;
} else {
return {
aTransform: ValueCell.create(new Float32Array(transformArray)),
transformData = {
aTransform: ValueCell.create(new Float32Array(instanceCount * 16)),
matrix: ValueCell.create(Mat4.identity()),
transform: ValueCell.create(transformArray),
transform: ValueCell.create(new Float32Array(transformArray)),
extraTransform: ValueCell.create(fillIdentityTransform(new Float32Array(instanceCount * 16), instanceCount)),
uInstanceCount: ValueCell.create(instanceCount),
instanceCount: ValueCell.create(instanceCount),
@@ -74,6 +72,9 @@ export function createTransform(transformArray: Float32Array, instanceCount: num
hasReflection: ValueCell.create(hasReflection),
};
}
updateTransformData(transformData);
return transformData;
}
const identityTransform = new Float32Array(16);

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