Compare commits

...

303 Commits

Author SHA1 Message Date
Alexander Rose
447d068bf1 3.8.1 2022-05-14 11:59:03 -07:00
Alexander Rose
7e1642a4a3 changelog 2022-05-14 11:53:55 -07:00
Alexander Rose
7781267e78 package updates 2022-05-14 11:53:18 -07:00
Alexander Rose
f824fdcfed improve scene marker/opacity average calculation 2022-05-14 11:43:24 -07:00
Alexander Rose
79dd441967 Merge pull request #436 from JonStargaryen/master
Fix Polymer Chain Instance Coloring
2022-05-09 22:41:33 -07:00
Sebastian Bittrich
9dcf9c0785 CHANGELOG 2022-05-09 15:58:22 -07:00
Sebastian Bittrich
83569462c6 fix instance coloring 2022-05-09 15:53:26 -07:00
Alexander Rose
947e169c3a Merge pull request #434 from JonStargaryen/master
proposed fix for headless-gl issues
2022-05-06 22:29:40 -07:00
Sebastian Bittrich
e6b36c52d1 CHANGELOG 2022-05-06 09:11:40 -07:00
Sebastian Bittrich
7fed3b84fa Merge remote-tracking branch 'upstream/master' 2022-05-06 09:08:53 -07:00
Sebastian Bittrich
cbc941f193 simplify array uniform check 2022-05-06 09:08:22 -07:00
Alexander Rose
a7ef0fb85f add interesting pdb entry 2022-05-05 22:41:16 -07:00
Alexander Rose
d10c36eaf5 fix issues with marking camera/handle helper 2022-05-05 22:40:52 -07:00
Sebastian Bittrich
e4c3a66753 proposed fix for headless-gl issues 2022-05-04 11:50:42 -07:00
Alexander Rose
f58f2cdc90 3.8.0 2022-04-30 16:11:07 -07:00
Alexander Rose
8f2676e91e schema updates 2022-04-30 16:05:30 -07:00
Alexander Rose
89d397898e changelog 2022-04-30 15:24:26 -07:00
Alexander Rose
2dc32be9ee Merge pull request #427 from molstar/transparent-object-outline
Transparent object outline
2022-04-30 15:08:46 -07:00
Alexander Rose
769220bd82 Merge pull request #421 from molstar/color-theme-typing
better color theme typing
2022-04-30 15:06:06 -07:00
Alexander Rose
c75aa5dd52 improve transparency & outlines
- proper per-group transparency with wboit off
- fixed outlines with transparent backgound
2022-04-30 12:53:14 -07:00
Alexander Rose
88f1cfd8c4 fix ignoreLight for direct-volume with webgl1 2022-04-30 09:24:39 -07:00
Alexander Rose
108279c1aa add clearDepth & blendColor to WebGLState 2022-04-30 08:42:01 -07:00
Alexander Rose
8150490aac Merge branch 'master' of https://github.com/molstar/molstar into transparent-object-outline 2022-04-30 08:39:16 -07:00
Alexander Rose
9497aa6362 package updates 2022-04-30 08:38:07 -07:00
Alexander Rose
3769da48a1 handle building GridLookup3D with zero cell size 2022-04-29 20:18:40 -07:00
Alexander Rose
3c21fcd53a fix FormatRegistry.isApplicable
- was returning true for unregistered formats
2022-04-29 20:16:09 -07:00
Alexander Rose
102ef2795d handle outlines for per-group transparency
- fix allowTransparentBackfaces for per-group transparency
2022-04-23 23:02:11 -07:00
Alexander Rose
0befa253c2 add WebGLState.depthFunc 2022-04-23 20:07:41 -07:00
Alexander Rose
87189cee58 support outlines for transparent objects
- cleanup of renderer and draw pass
2022-04-23 15:19:01 -07:00
Alexander Rose
3284f13fc6 fix spec of emptyDepthTexture in Renderer 2022-04-23 15:17:20 -07:00
Alexander Rose
70d219b120 improve Renderer.renderPick typing 2022-04-23 15:13:38 -07:00
Alexander Rose
a5ed3a08ea add Scene.getOpacityAverage 2022-04-23 15:12:16 -07:00
Alexander Rose
3665e7e999 expose RenderTarget.depthRenderbuffer 2022-04-23 15:10:40 -07:00
Alexander Rose
9b583b23ae add Renderbuffer.detachFramebuffer 2022-04-23 15:10:16 -07:00
Alexander Rose
d602415e98 check if WEBGL_depth_texture is supported 2022-04-23 15:09:44 -07:00
Alexander Rose
2c49a423e2 type assertion 2022-04-21 19:47:13 -07:00
Alexander Rose
8a266e70c8 fix handling of struct_conf mmCIF field (#425) 2022-04-21 19:46:50 -07:00
Alexander Rose
0df3bcd65d document supported file formats 2022-04-19 17:43:33 -07:00
dsehnal
f5ecf5648e 3.7.0 2022-04-13 20:04:01 +02:00
dsehnal
821f82fc3f changelog 2022-04-13 20:01:04 +02:00
David Sehnal
92305fe628 Merge pull request #420 from molstar/transparentBackfaces
improve transparentBackfaces handling
2022-04-13 19:57:29 +02:00
David Sehnal
17fe57b8a5 Merge pull request #423 from jpattle/option-to-disable-drag-overlay
Added an option to disable the drag overlay
2022-04-13 16:31:21 +02:00
Jason Pattle
47433a51d3 Added an option to the components spec to allow disabling of the drag-and-drop overlap for Molstar that loads files into the viewer. By default the drag overlay is enabled, just as it was before 2022-04-13 15:00:03 +01:00
Alexander Rose
e090827ced Merge branch 'master' of https://github.com/molstar/molstar into transparentBackfaces 2022-04-10 13:20:22 -07:00
Alexander Rose
856e6a8b74 use ColorTypeLocation as default
- for backwards compatibility
- most used
2022-04-09 19:27:06 -07:00
Alexander Rose
a813b4d40e Merge branch 'master' of https://github.com/molstar/molstar into color-theme-typing 2022-04-09 16:53:17 -07:00
dsehnal
98afc27442 fix react typing 2022-04-09 21:24:14 +02:00
Alexander Rose
9d4f28a395 fix type deps 2022-04-09 11:36:17 -07:00
Alexander Rose
50266d9a56 package updates 2022-04-09 11:20:28 -07:00
Alexander Rose
602a532cf2 better color theme typing 2022-04-09 10:38:03 -07:00
Alexander Rose
b23d610c94 improve transparentBackfaces handling
- off: don't show (default)
- on: show with transparency
- opaque: show fully opaque
2022-04-09 10:17:41 -07:00
Alexander Rose
119c43d527 fix xrayShaded for texture-mesh geometries 2022-04-09 10:04:06 -07:00
Alexander Rose
124feeb790 color theme tweaks
- add optional grid property
- handle dColorType_direct for non direct-volume shaders
2022-04-09 09:57:31 -07:00
dsehnal
2c0e7e84da 3.6.2 2022-04-05 17:59:48 +02:00
dsehnal
0d1e105343 changelog 2022-04-05 17:57:14 +02:00
dsehnal
f040c89ab3 React 18 friendly useBehavior hook 2022-04-05 17:51:13 +02:00
David Sehnal
5e9d8298ef Merge pull request #415 from JonStargaryen/master
ModelServer Ligand Export: Fix Alternate Locations & Mismatching Atoms
2022-04-05 17:49:16 +02:00
Sebastian Bittrich
7766ca2793 CHANGELOG 2022-04-04 11:36:39 -07:00
Sebastian Bittrich
fb2f22f120 Merge remote-tracking branch 'upstream/master' 2022-04-04 11:33:01 -07:00
Sebastian Bittrich
146fed3504 CHANGELOG 2022-04-04 11:32:51 -07:00
Sebastian Bittrich
0b7a6e3375 cleanup 2022-04-04 10:55:44 -07:00
Sebastian Bittrich
f1fbdeaca0 handle missing atoms (ignore hydrogen, fail for heavy) 2022-04-04 10:49:40 -07:00
Sebastian Bittrich
ee7e37f6bc handle deuterated part 2 2022-04-04 09:45:42 -07:00
Sebastian Bittrich
861f665ab3 handle deuterated 2022-04-04 09:17:39 -07:00
dsehnal
456de23ad4 remove console.log 2022-04-03 14:45:41 +02:00
dsehnal
6d3578c17e fix alpha orbitals example 2022-04-03 14:08:31 +02:00
dsehnal
57da7267e2 3.6.1 2022-04-03 13:50:46 +02:00
dsehnal
578b764406 fix react 18 ui: use hooks for overlay components 2022-04-03 13:48:05 +02:00
dsehnal
f65a38a085 TransformControlBase fix 2022-04-03 13:00:09 +02:00
dsehnal
d187757bbc 3.6.0 2022-04-03 11:19:12 +02:00
dsehnal
df83b24cf4 changelog 2022-04-03 11:16:16 +02:00
dsehnal
8e31ce0f5b react 18 support 2022-04-03 11:15:47 +02:00
David Sehnal
4f69eb7963 Merge pull request #414 from molstar/lowercase-str-column
lowercase column schema
2022-04-03 10:53:08 +02:00
Alexander Rose
4b9009216b mmcif schema: use lowercase instead of uppercase 2022-04-02 22:49:57 -07:00
Alexander Rose
895076c837 Merge branch 'master' of https://github.com/molstar/molstar into lowercase-str-column 2022-04-02 16:31:15 -07:00
Alexander Rose
6e398ee64a gh action 2022-04-02 16:13:24 -07:00
Alexander Rose
c2177272b5 gh action 2022-04-02 16:07:32 -07:00
Alexander Rose
4877de5839 re-add gl support to gh action 2022-04-02 15:55:22 -07:00
Alexander Rose
3cb9d10126 fix shader tests & cleanup shader code 2022-04-02 15:52:39 -07:00
Alexander Rose
23c53cd9fb fix disable-wboit Viewer GET param 2022-04-02 15:42:33 -07:00
Alexander Rose
3471743a63 changelog 2022-04-02 15:26:25 -07:00
Alexander Rose
1b0b1809ef handle case-insensitve mmcif fields
- support upper/lower case transforms
- handle case transform for CifField to Column
2022-04-02 15:24:01 -07:00
dsehnal
0833cffead fix undefined value handling and update alised type 2022-04-02 21:48:15 +02:00
Alexander Rose
a0a8ae88b7 Merge branch 'master' of https://github.com/molstar/molstar into lowercase-str-column 2022-04-02 12:27:40 -07:00
David Sehnal
e415cbeca4 Merge pull request #413 from molstar/allowTransparentBackfaces
add allowTransparentBackfaces parameter
2022-04-02 21:00:43 +02:00
dsehnal
4c15c93381 lowercase column schema 2022-04-02 20:54:35 +02:00
Alexander Rose
bd19822112 add allowTransparentBackfaces parameter
- for mesh, spheres, cylinders, texture-mesh geometries
2022-04-01 19:18:24 -07:00
Sebastian Bittrich
b87beb4a6e better msg for UNL 2022-04-01 16:58:32 -07:00
Sebastian Bittrich
62a58facb2 better error msg for unknown components 2022-04-01 15:51:00 -07:00
Sebastian Bittrich
5fa8178df7 fix handling of alternate locations in ligand-encoder#_getAtoms 2022-04-01 11:47:59 -07:00
David Sehnal
d6043e7d1f Merge pull request #411 from JonStargaryen/master
fix volume streaming for entries with multiple contour lists
2022-04-01 13:35:57 +02:00
Sebastian Bittrich
8e432dfbb4 CHANGELOG 2022-03-30 09:27:40 -07:00
Sebastian Bittrich
324ab3744b fix volume streaming for entries with multiple contour lists 2022-03-30 09:19:36 -07:00
Alexander Rose
33ee4d0418 Merge pull request #409 from JonStargaryen/master
Fix unit mapping in bondedAtomicPairs MolScript query
2022-03-29 18:11:59 -07:00
Alexander Rose
cbb104ccba Merge branch 'master' into master 2022-03-29 18:11:37 -07:00
Alexander Rose
0bda5461ae Merge pull request #403 from molstar/pdb-conect
improve pdb parsing
2022-03-29 18:01:56 -07:00
Sebastian Bittrich
4096a03de1 CHANGELOG 2022-03-29 11:40:46 -07:00
Sebastian Bittrich
fbee5f83df fix mapping issues in bondedAtomicPairs 2022-03-29 11:34:53 -07:00
Alexander Rose
84a1b19850 improve Model.hasSecondaryStructure
- check ModelSecondaryStructure.Provider.isApplicable
2022-03-28 18:51:54 -07:00
Alexander Rose
1df5bd6d03 make use of PDB TER record 2022-03-28 18:47:00 -07:00
Alexander Rose
8bd4221a85 improve pdb parsing
- handle non unique atom and chain names
- fixes #156
2022-03-26 11:26:20 -07:00
Alexander Rose
4d97ccdfb3 improve bonds assignment of coarse grained models
- check for IndexPairBonds and exhaustive StructConn
2022-03-26 11:22:04 -07:00
Alexander Rose
ca5e57ddbf fix aromatic rings assignment 2022-03-26 11:17:46 -07:00
Alexander Rose
ed6511799b check model and coordinates element count 2022-03-26 11:15:19 -07:00
Alexander Rose
9b1223ec15 improve webgl error/type handling 2022-03-26 11:12:28 -07:00
Alexander Rose
97210ee67a avoid calculating actionItems for isDisabled check 2022-03-26 11:11:22 -07:00
David Sehnal
308d1003ad Merge pull request #401 from molstar/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-03-25 14:07:50 +01:00
dependabot[bot]
ce9e193958 Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-25 12:50:11 +00:00
dsehnal
2a83afa8c1 3.5.0 2022-03-25 13:49:33 +01:00
dsehnal
8891fa328b cif2bcif BCIF => CIF conversion support 2022-03-25 13:45:00 +01:00
David Sehnal
a23c06c456 Merge pull request #399 from MadCatX/dnatco-changeable-colors
Make Confal pyramids colors changeable
2022-03-22 13:25:32 +01:00
Michal Malý
34e87121e1 Make Confal pyramids colors changeable 2022-03-21 09:07:49 +01:00
Alexander Rose
6e5c20f442 improve surface bounding-sphere
- use exact max radius to expand structure/unit sphere
2022-03-19 12:21:55 -07:00
Alexander Rose
8ac3bec451 improve color-smoothing
- use padded box
2022-03-19 12:20:12 -07:00
Alexander Rose
5128d0f405 improve Sphere3D.expand & Box3D.fromSphere3D
- ensure extrema are within radius
- avoid degenerate box for low number of points
2022-03-19 12:19:39 -07:00
Alexander Rose
6ae2121391 guard against zero window.devicePixelRatio 2022-03-19 11:43:41 -07:00
David Sehnal
749e0c5a47 Merge pull request #396 from MadCatX/dnatco-off-by-one
DNATCO extension: Fix off-by-one error in element lookup
2022-03-17 09:21:11 +01:00
Michal Malý
7b55ef85e1 DNATCO extension: Fix missing Confal pyramids in some structures 2022-03-17 07:47:47 +01:00
Alexander Rose
23ec35d1f9 3.4.0 2022-03-13 13:12:32 -07:00
Alexander Rose
ff089c2b9f changelog 2022-03-13 12:50:44 -07:00
Alexander Rose
1ab088718a package updates 2022-03-13 12:49:03 -07:00
Alexander Rose
0cb2e5857a Merge pull request #393 from molstar/zenodo-import
Zenodo import
2022-03-13 12:19:04 -07:00
Alexander Rose
7c5ae5d7ee tweak guessElementSymbolString 2022-03-13 12:16:12 -07:00
Alexander Rose
6e2665d98d Merge branch 'master' of https://github.com/molstar/molstar into zenodo-import 2022-03-12 18:03:45 -08:00
Alexander Rose
b3b4692237 add top format support 2022-03-12 17:46:29 -08:00
Alexander Rose
55ff1d4999 add nctraj format support 2022-03-12 14:17:58 -08:00
Alexander Rose
511c839237 add prmtop format support 2022-03-12 13:48:10 -08:00
Alexander Rose
384cd6e5d9 add trr format support 2022-03-12 13:02:50 -08:00
Alexander Rose
6fd9dcc72e split structure formats into topology & coordinates 2022-03-12 11:51:03 -08:00
Alexander Rose
12ca06fe91 fix handling of empty symmetry cell data 2022-03-12 11:44:27 -08:00
Alexander Rose
652f6c651b fix wrong element assignment 2022-03-12 10:58:33 -08:00
Alexander Rose
7dd808a772 add custom import controls 2022-03-12 10:52:43 -08:00
Alexander Rose
945e55f8a7 add formated file size 2022-03-07 22:06:22 -08:00
Alexander Rose
866a30abe5 cleanup getFileInfo 2022-03-07 22:05:11 -08:00
Alexander Rose
f0e33e1e4e fix legend of hydrophobicity color theme 2022-03-07 21:44:57 -08:00
Alexander Rose
8723ca38b4 improve saccharide detection 2022-03-07 21:31:33 -08:00
Alexander Rose
efffca0026 zenode import fixes 2022-03-06 21:57:42 -08:00
Alexander Rose
6c5eb3035f remove default record id 2022-03-06 17:55:52 -08:00
Alexander Rose
0bf385f2ca add zip file support to zenodo extension 2022-03-06 17:37:39 -08:00
Alexander Rose
9d5f51f513 improve handling of compressed files
- fix loading of some compressed files within sessions
- ignore some hidden MACOSX files
2022-03-06 17:32:56 -08:00
Alexander Rose
a1448131d8 add Zenodo import extension 2022-03-06 11:12:21 -08:00
Alexander Rose
664cacc7ac add LoadTrajectory action 2022-03-06 11:11:16 -08:00
Alexander Rose
1aec37dd05 fix 2022-03-06 11:09:51 -08:00
Alexander Rose
3ff2c0840e Merge pull request #392 from molstar/assert-unreachable
make use of assertUnreachable
2022-03-06 11:06:04 -08:00
Alexander Rose
5ca3c3ac52 Update src/mol-script/language/parser.ts 2022-03-06 11:04:58 -08:00
Alexander Rose
714ee50965 Update src/mol-script/language/parser.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-03-06 11:03:33 -08:00
Alexander Rose
ae1df3c5aa Update src/mol-script/language/parser.ts
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-03-06 11:03:28 -08:00
Alexander Rose
28afb39550 make use of assertUnreachable 2022-03-05 10:18:40 -08:00
Alexander Rose
3466a8a024 Merge pull request #389 from molstar/cif-check-present
fix handling of mmcif with empty label_asym_id
2022-03-05 09:20:19 -08:00
Alexander Rose
90db3321f5 changelog
Co-authored-by: David Sehnal <dsehnal@users.noreply.github.com>
2022-03-05 09:20:04 -08:00
Alexander Rose
bf4e5ed7c2 normalize mmcif data 2022-03-04 22:47:14 -08:00
Alexander Rose
d3b2c20c26 normalize atom_site early 2022-03-03 18:31:54 -08:00
Alexander Rose
c3afabb4b1 spelling 2022-03-02 19:50:30 -08:00
Alexander Rose
18cc9790d1 fix handling of mmcif with empty label_asym_id 2022-03-02 19:17:31 -08:00
Alexander Rose
d5ed3aa674 3.3.1 2022-02-27 18:45:45 -08:00
Alexander Rose
cfb9c9acfe changelog 2022-02-27 18:40:48 -08:00
Alexander Rose
67feef0b1d add option to ignore ions for inter-unit bonds 2022-02-27 18:40:27 -08:00
Alexander Rose
e0192ab5aa fix issue with unit boundary reuse
- do at visual level instead
2022-02-27 18:03:41 -08:00
Alexander Rose
eae7c11c55 3.3.0 2022-02-27 13:03:01 -08:00
Alexander Rose
b8251e1ade changelog 2022-02-27 12:57:51 -08:00
Alexander Rose
2ff2b9f348 schema updates 2022-02-27 12:50:48 -08:00
Alexander Rose
164e3f3343 package updates 2022-02-27 12:47:45 -08:00
Alexander Rose
4901a1bd87 rename occlusion scaleFactor to resolutionScale 2022-02-27 12:16:37 -08:00
Alexander Rose
cd194cca65 Merge pull request #385 from molstar/traj-anim
Coordinate trajectory related improvements
2022-02-27 12:04:28 -08:00
Alexander Rose
1748efbc18 Merge branch 'master' of https://github.com/molstar/molstar into traj-anim 2022-02-27 12:03:54 -08:00
Alexander Rose
4d60b40403 fix interUnitBonds parent check 2022-02-27 12:02:36 -08:00
Alexander Rose
6e5a41879f Merge pull request #386 from molstar/improve-ao
Improve AO performance
2022-02-27 11:58:50 -08:00
Alexander Rose
c5f9eb54da set default occlusion scaleFactor to 1 2022-02-26 23:51:21 -08:00
Alexander Rose
aebbfeb061 add support for lower resolution AO
- downsample depth for AO
- add scaleFactor param
- full res for image/video
2022-02-26 22:23:52 -08:00
Alexander Rose
a0a5a6b578 add encoder spec
- mostly as example
2022-02-26 17:43:37 -08:00
Alexander Rose
6bdafb85d7 Check if marking passes are needed
- add Scene.getMarkerAverage
2022-02-26 13:30:05 -08:00
Alexander Rose
0dd7debf5d reuse occlusion in multi-sample pass 2022-02-26 13:02:53 -08:00
Alexander Rose
962b9ee7af Merge pull request #378 from molstar/ar-bonds
fix visuals for aromatic/delocalized bonds
2022-02-26 11:34:42 -08:00
Alexander Rose
15bcc5df88 Merge branch 'master' into ar-bonds 2022-02-26 11:26:29 -08:00
Alexander Rose
8495d834c8 add getTripletIndices & triplets to UnitResonance 2022-02-26 11:25:35 -08:00
Alexander Rose
7282399709 fix/improve canRemap handling in IntraUnitBonds 2022-02-26 10:56:32 -08:00
Alexander Rose
780bdd6e7e don't compute InterUnitBonds when parent ones empty 2022-02-26 10:53:54 -08:00
Alexander Rose
ad08e7c67f reuse unit boundary if it has not changed too much 2022-02-26 10:52:26 -08:00
Alexander Rose
ff089964ca improve line visuals in polymerAndLigand preset 2022-02-26 10:50:02 -08:00
Alexander Rose
990191529a reuse Model.CoarseGrained for coordinate trajectories 2022-02-26 10:48:05 -08:00
Alexander Rose
ce1bec12b4 fix mononucleotides detected as polymer components 2022-02-26 10:46:51 -08:00
Alexander Rose
703ea9af53 change line geometry default scaleFactor to 2
- 3 is too big after fixing line rendering
2022-02-26 10:45:51 -08:00
David Sehnal
df0227ae1e Merge pull request #383 from molstar/batch-highlights
Resolve marking in main render loop
2022-02-24 07:46:44 +01:00
dsehnal
486d12b6ac Fix multisample pass "forceOn" 2022-02-24 07:20:03 +01:00
dsehnal
9c18375ab4 typo 2022-02-23 23:34:09 +01:00
dsehnal
e1708aed68 dispose mark buffer 2022-02-23 19:09:40 +01:00
dsehnal
a42d778b84 changelog 2022-02-23 12:04:38 +01:00
dsehnal
7692b59c7c resolve marking in main render loop insread of eagerly 2022-02-23 11:59:29 +01:00
dsehnal
82de9b36b3 addRing check tweak 2022-02-22 16:02:11 +01:00
dsehnal
49b3c8f65f ring computation algorithm fixes 2022-02-22 13:16:32 +01:00
Alexander Rose
90ad32d936 add ring example PDB IDs 2022-02-21 13:48:43 -08:00
David Sehnal
2509e91f1a Merge pull request #381 from russellp17/disable-ts-config-for-sourceMappingURL
Disable ts config that leads to sourceMappingURL comment
2022-02-21 15:20:00 +01:00
David Sehnal
80dc2219e4 Merge pull request #380 from russellp17/allow-react-16-in-peer-dependency
Allow React ^16.14.0 as peer dependency
2022-02-21 15:10:33 +01:00
Russell Parker
931cb0fa7d Disable ts config that leads to sourceMappingURL comment in transpilation 2022-02-21 08:24:09 -05:00
Russell Parker
4383f2ea90 Allow React ^16.14.0 as peer dependency 2022-02-21 08:20:55 -05:00
dsehnal
add79dc242 ring computation algorithm fix 2022-02-21 14:15:13 +01:00
Alexander Rose
b6e142f04c move delocalizedTriplets code to unit.resonance 2022-02-20 16:28:42 -08:00
Alexander Rose
105f6c3041 fix visuals for aromatic/delocalized bonds 2022-02-20 12:50:44 -08:00
Alexander Rose
4babbb65c1 fix spec 2022-02-19 17:22:25 -08:00
Alexander Rose
878159f7ed fix texture warnings (#319)
- bind real texture to tDepth in renderer
- ensure textures are not empty; init as 1x1(x1)
2022-02-19 16:59:03 -08:00
Alexander Rose
c320386019 changelog 2022-02-19 14:23:37 -08:00
Alexander Rose
1e7a0159f0 Merge pull request #377 from JonStargaryen/master
parse contour-level from emdb v3 header files
2022-02-19 14:20:48 -08:00
JonStargaryen
bbf4f1d1d3 break early 2022-02-19 11:02:30 -08:00
Alexander Rose
8cd1c69c76 css tweaks, fixes #376 2022-02-19 10:46:06 -08:00
JonStargaryen
2372a878ac parse contour-level from emdb v3 header files 2022-02-19 09:12:20 -08:00
dsehnal
9bec644997 3.2.0 2022-02-17 19:24:55 +01:00
dsehnal
650d38dff8 Store IndexPairBonds as a dynamic property 2022-02-17 19:21:49 +01:00
David Sehnal
097277e397 Merge pull request #373 from JonStargaryen/master
Add TraceOnly option for Structure Superposition
2022-02-14 19:53:51 +01:00
Sebastian Bittrich
82a4d5eedf cleanup 2022-02-14 10:46:03 -08:00
Sebastian Bittrich
2a00248812 determine trace during buildIndex 2022-02-14 10:41:10 -08:00
Sebastian Bittrich
9841c773cb reset camera after all alignments 2022-02-10 13:55:09 -08:00
Sebastian Bittrich
b21a78ad14 support shuffled atoms when aligning SIFTS trace 2022-02-10 13:49:37 -08:00
Sebastian Bittrich
7329fa597d options obj for alignAndSuperposeWithSIFTSMapping 2022-02-10 11:36:50 -08:00
Sebastian Bittrich
f1d8f0ecb4 auth 2022-02-10 11:32:21 -08:00
Sebastian Bittrich
1d127f2364 changelog 2022-02-10 11:30:55 -08:00
Sebastian Bittrich
b244405cc3 polymer-based query 2022-02-10 10:39:22 -08:00
Sebastian Bittrich
38adfe0ca6 cleanup 2022-02-10 10:29:34 -08:00
Sebastian Bittrich
6f0d798847 optional filtering for trace during alignment 2022-02-10 10:23:31 -08:00
Sebastian Bittrich
6e58bfd2b0 traceOnly param for superpos 2022-02-08 16:03:05 -08:00
David Sehnal
bc2e8d8ac4 Merge pull request #369 from molstar/pdbx_sifts-export
Better support for atom_site.pdbx_sifts_xref
2022-02-08 10:19:02 +01:00
dsehnal
7be654d47f tweak labels 2022-02-08 10:18:01 +01:00
dsehnal
bfe46e3604 pdbx_sifts_xref PR feedback 2022-02-08 10:12:21 +01:00
dsehnal
008b597fc5 changelog 2022-02-07 17:51:13 +01:00
dsehnal
c4b4f2e3b1 rename BestDatabaseSequenceMapping -> SIFTSMapping 2022-02-07 17:49:10 +01:00
dsehnal
76ee97301b atom_site.pdbx_label_index support 2022-02-07 17:44:03 +01:00
dsehnal
289dc09eae add support for atom_site.pdbx_sifts_xref export 2022-02-07 17:33:11 +01:00
Alexander Rose
f23f84f0f3 3.1.0 2022-02-06 15:56:41 -08:00
Alexander Rose
62259f3295 changelog 2022-02-06 15:51:40 -08:00
Alexander Rose
854a430a12 tweak quick-styles order 2022-02-06 15:46:40 -08:00
Alexander Rose
d27cdb5637 schema updates 2022-02-06 15:41:09 -08:00
Alexander Rose
7d12d9ee90 package udpdates 2022-02-06 15:38:09 -08:00
Alexander Rose
d70a4ff347 Merge pull request #366 from molstar/repr-defaults-tweaks
cleaner default representation style
2022-02-06 15:24:27 -08:00
Alexander Rose
49541558d1 Merge branch 'master' into repr-defaults-tweaks 2022-02-06 15:24:16 -08:00
Alexander Rose
7b00a1227c changelog 2022-02-06 15:22:50 -08:00
Alexander Rose
7800603c81 Merge pull request #367 from molstar/quick-styles
Quick styles
2022-02-06 15:16:43 -08:00
Alexander Rose
fca00c8116 variable naming 2022-02-06 15:15:55 -08:00
Alexander Rose
00fa549e44 Merge pull request #368 from JonStargaryen/master
iterate over `structure.unitSymmetryGroups`, fixes #364
2022-02-06 15:13:57 -08:00
Alexander Rose
36181b6b87 tweak quick style names 2022-02-06 15:13:15 -08:00
JonStargaryen
88a95162e9 iterate over structure.unitSymmetryGroups, fixes #364 2022-02-06 10:47:40 -08:00
Alexander Rose
9c1d59a2c8 add Quick Styles panel 2022-02-05 12:58:32 -08:00
Alexander Rose
fdd894956a fix representation preset side effects 2022-02-05 12:56:55 -08:00
Alexander Rose
eb6dc0859d cleaner default representation style 2022-02-05 12:23:28 -08:00
Alexander Rose
b99026bba2 add ignoreLight to component params 2022-02-05 12:10:56 -08:00
Alexander Rose
6fa50eb8d5 fix xrayShader & ignoreLight not working together 2022-02-05 11:57:39 -08:00
David Sehnal
e5046f15a9 Merge pull request #365 from JonStargaryen/master
Volume Streaming Error Message
2022-02-04 17:11:31 +01:00
JonStargaryen
09f1c066a0 logic 2022-02-03 17:11:50 -08:00
Sebastian Bittrich
ed2f0b34c9 volume streaming err msg 2022-02-03 16:11:10 -08:00
Alexander Rose
c7f75861de 3.0.2 2022-01-30 12:24:48 -08:00
Alexander Rose
ccd04dbc9d changelog 2022-01-30 12:19:49 -08:00
Alexander Rose
e71f8d2c10 Merge pull request #360 from molstar/fix/visual-visibility
Fix visual visibility edge case
2022-01-30 12:17:12 -08:00
dsehnal
d9b4c60239 Fix visual visibility edge case 2022-01-30 15:02:46 +01:00
Alexander Rose
103c1fca21 measurement options tweaks
- allow larger text size
- make customText essential
2022-01-29 19:30:03 -08:00
Alexander Rose
49559bf5fb citation tweak 2022-01-29 16:05:17 -08:00
Alexander Rose
26dceabf83 add citation file 2022-01-29 15:56:39 -08:00
Alexander Rose
abe506182e fix multi-instance entity label display
- fix empty elements created in extendToAllInstances
2022-01-29 11:21:48 -08:00
Alexander Rose
582a0e2a38 fix Sphere.expand for highly directional extrema 2022-01-29 11:13:19 -08:00
dsehnal
2784ccf379 3.0.1 2022-01-27 12:15:53 +01:00
dsehnal
0ad1d578fe changelog 2022-01-27 12:13:22 +01:00
David Sehnal
31fd1c9c68 Merge pull request #353 from molstar/drag-tweak
emit drag event whenever started within viewport
2022-01-27 12:12:07 +01:00
David Sehnal
9c961297a2 Merge branch 'master' into drag-tweak 2022-01-27 12:11:59 +01:00
David Sehnal
b920053349 Merge pull request #352 from molstar/var-fixes
Various fixes
2022-01-27 12:09:39 +01:00
David Sehnal
0a5c764e4a Merge pull request #351 from molstar/volume-server-data-fix
Volume server data fix
2022-01-27 11:37:31 +01:00
Alexander Rose
b9a71c83ff emit drag event whenever started within viewport 2022-01-26 21:32:14 -08:00
Alexander Rose
3255f207d0 Merge branch 'master' of https://github.com/molstar/molstar into var-fixes 2022-01-26 20:46:11 -08:00
Alexander Rose
e3b4ca8862 add entity-id and entity-source as carbonColor 2022-01-26 20:42:38 -08:00
Alexander Rose
6810793015 fix marking of InteractionsInterUnitVisual 2022-01-26 20:40:12 -08:00
Alexander Rose
1feb3c2095 fix entity-id coloring broken for non-ihm models 2022-01-26 20:38:46 -08:00
Alexander Rose
f2da6033d0 fix pdbe xray maps url 2022-01-26 20:36:52 -08:00
Alexander Rose
28bc212132 fix marking pass for transparentBackground 2022-01-26 20:35:38 -08:00
dsehnal
1a7c62eec6 remove gl from dependencies & install it on demand instead
- installing gl on M1 Mac was taking several minutes to compile on each update
2022-01-26 23:12:12 +01:00
dsehnal
de67dbacba iso-value adjustment for VolumeServer data in default Viewer 2022-01-26 17:47:23 +01:00
dsehnal
57223a0f9a Fix VolumeServer/query CLI 2022-01-26 16:49:18 +01:00
Alexander Rose
2ad0754b90 3.0.0 2022-01-23 18:12:48 -08:00
Alexander Rose
3ecb3af57b changelog 2022-01-23 18:08:01 -08:00
Alexander Rose
ec4f15f549 improve/fix InteractionsIntraUnitVisual marking 2022-01-23 17:55:51 -08:00
Alexander Rose
2458ea7b92 Merge pull request #349 from molstar/custom-theme-colors
add custom theme colors
2022-01-23 15:13:24 -08:00
Alexander Rose
c5e6bedf11 Merge branch 'master' into custom-theme-colors 2022-01-23 13:54:57 -08:00
dsehnal
8528e5a666 Support/bugfixes for atom_site.pdbx_sifts_xref categories 2022-01-23 20:46:49 +01:00
dsehnal
6ed232b3d9 fix using default values for webgl1/wboit features in the default viewer app 2022-01-23 19:25:09 +01:00
dsehnal
f8aae8cbd1 skip Coarse models in export extension 2022-01-23 16:13:02 +01:00
Alexander Rose
00c2517045 package updates 2022-01-22 14:06:26 -08:00
Alexander Rose
99b043a929 avoid circular dependency 2022-01-22 11:10:41 -08:00
Alexander Rose
5900e27e39 add custom theme colors
- element-symbol
- molecule-type
- residue-name
- secondary-structure
2022-01-22 10:51:41 -08:00
Alexander Rose
1b79d34907 fix marking of carbohydrate visuals 2022-01-22 08:53:47 -08:00
Alexander Rose
fc52e29c92 re-add using _struct_asym cat in getStructAsymMap
- needed for ihm model support
2022-01-22 08:50:29 -08:00
Alexander Rose
df23b3c0fe fix coarse model support in entity-id color theme
- add StructureProperties.coarse.entity_id
2022-01-22 08:48:07 -08:00
David Sehnal
5e25716c98 Merge pull request #334 from molstar/export-extension
Model export extension & related improvements
2022-01-18 13:23:11 +01:00
dsehnal
f70a10bc56 add info about CIF export failure 2022-01-18 13:20:53 +01:00
dsehnal
0ccb045f4e Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-18 12:30:45 +01:00
dsehnal
fbb60c9493 Treat empty string as non-present value in BinaryCIF 2022-01-17 16:21:14 +01:00
dsehnal
9f953ef51c add Model Export overlay 2022-01-17 15:47:56 +01:00
dsehnal
4871f1547c Generate structAsymMap from normalized atomic hierarchy
- Fixes issue with remapped chains
- Requires to parse PRD separately
2022-01-17 14:52:53 +01:00
dsehnal
d6413529f4 PR feedback 2022-01-17 13:58:20 +01:00
dsehnal
b6847907ca Add ModelExport to viewer/app.ts 2022-01-17 11:37:49 +01:00
dsehnal
fb54a1aed7 Merge branch 'master' of https://github.com/molstar/molstar into export-extension 2022-01-17 11:37:16 +01:00
dsehnal
c0880b647f changelog 2022-01-03 13:51:09 +01:00
dsehnal
039dc6a76b changelog 2022-01-03 13:49:59 +01:00
dsehnal
042a7625ad isWithoutOperator tweak 2022-01-03 13:46:13 +01:00
dsehnal
41827c478d open zip files with multiple entries 2022-01-03 13:42:37 +01:00
dsehnal
9a73180c3c support transformed export & structAsymMap parsing fix 2022-01-03 13:15:51 +01:00
dsehnal
333ee85fdb omit suffix for identity assembly operators 2022-01-03 12:52:05 +01:00
dsehnal
fa8ca45b6a do not include assembly categories in export if an operator has been applied 2022-01-03 12:34:37 +01:00
dsehnal
c2bae1aeb7 model export extension 2022-01-03 12:26:36 +01:00
284 changed files with 30401 additions and 26692 deletions

View File

@@ -9,12 +9,12 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 17
node-version: 16
- run: npm ci
- run: sudo apt-get install xvfb
- name: Lint
run: npm run lint
- name: Test
run: xvfb-run --auto-servernum npm run jest
run: npm install --no-save "gl@^5.0.0" && xvfb-run --auto-servernum npm run jest
- name: Build
run: npm run build

View File

@@ -6,6 +6,161 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v3.8.1] - 2022-05-14
- Fix issues with marking camera/handle helper (#433)
- Fix issues with array uniforms when running with headless-gl
- Fix Polymer Chain Instance coloring
- Improve performance of scene marker/opacity average calculation
## [v3.8.0] - 2022-04-30
- Add support for outlines around transparent objects
- Improve per-group transparency when wboit is switched off
- Improve ``ColorTheme`` typing with ``ColorType`` generic.
- Defaults to ``ColorTypeLocation``
- Set when using ``ColorTypeDirect`` or ``ColorTypeGrid``
- Fix case handling of ``struct_conf`` mmCIF enumeration field (#425)
- Fix ``allowTransparentBackfaces`` for per-group transparency
- Fix ``FormatRegistry.isApplicable`` returning true for unregistered formats
- Fix: handle building of ``GridLookup3D`` with zero cell size
- Fix ``ignoreLight`` for direct-volume rendering with webgl1
- Fix (non-black) outlines when using transparent background
## [v3.7.0] - 2022-04-13
- Fix ``xrayShaded`` for texture-mesh geometries
- [Breaking] Change ``allowTransparentBackfaces`` to ``transparentBackfaces`` with options ``off``, ``on``, ``opaque``. This was only added in 3.6.0, so allowing a breaking change here.
- ``off``: don't show (default)
- ``on``: show with transparency
- ``opaque``: show fully opaque
- Add option to disable file drop overlay.
## [v3.6.2] - 2022-04-05
- ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
- React 18 friendly ``useBehavior`` hook.
## [v3.6.1] - 2022-04-03
- Fix React18 related UI regressions.
## [v3.6.0] - 2022-04-03
- Check that model and coordinates have same element count when creating a trajectory
- Fix aromatic rings assignment: do not mix flags and planarity test
- Improve bonds assignment of coarse grained models: check for IndexPairBonds and exhaustive StructConn
- Fix unit mapping in bondedAtomicPairs MolScript query
- Improve pdb parsing: handle non unique atom and chain names (fixes #156)
- Fix volume streaming for entries with multiple contour lists
- Add ``allowTransparentBackfaces`` parameter to support double-sided rendering of transparent geometries
- Fix handling of case insensitive mmCIF enumeration fields (including entity.type)
- Fix ``disable-wboit`` Viewer GET param
- Add support for React 18.
- Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
- In Mol* 4.0, React 18 will become the default option.
## [v3.5.0] - 2022-03-25
- Fix issues with bounding-sphere & color-smoothing (mostly for small geometries)
- Support BCIF => CIF conversion in ``cif2bcif`` CLI tool
## [v3.4.0] - 2022-03-13
- Fix handling of mmcif with empty ``label_*`` fields
- Improve saccharide detection (compare against list from CCD)
- Fix legend label of hydrophobicity color theme
- Add ``LoadTrajectory`` action
- Add ``CustomImportControls`` to left panel
- Add Zenodo import extension (load structures, trajectories, volumes, and zip files)
- Fix loading of some compressed files within sessions
- Fix wrong element assignment for atoms with Charmm ion names
- Fix handling of empty symmetry cell data
- Add support for ``trr`` and ``nctraj`` coordinates files
- Add support for ``prmtop`` and ``top`` topology files
## [v3.3.1] - 2022-02-27
- Fix issue with unit boundary reuse (do at visual level instead)
- Add option to ignore ions for inter-unit bond computation
## [v3.3.0] - 2022-02-27
- Fix parsing contour-level from emdb v3 header files
- Fix invalid CSS (#376)
- Fix "texture not renderable" & "texture not bound" warnings (#319)
- Fix visual for bonds between two aromatic rings
- Fix visual for delocalized bonds (parsed from mmcif and mol2)
- Fix ring computation algorithm
- Add ``UnitResonance`` property with info about delocalized triplets
- Resolve marking in main renderer loop to improve overall performance
- Use ``throttleTime`` instead of ``debounceTime`` in sequence viewer for better responsiveness
- Change line geometry default ``scaleFactor`` to 2 (3 is too big after fixing line rendering)
- Trajectory animation performance improvements
- Reuse ``Model.CoarseGrained`` for coordinate trajectories
- Avoid calculating ``InterUnitBonds`` when ``Structure.parent`` ones are empty
- Reuse unit boundary if sphere has not changed too much
- Don't show 'inter-bond' and 'element-cross' visuals in line representations of polymerAndLigand preset
- Fix additional mononucleotides detected as polymer components
- Fix and improve ``canRemap`` handling in ``IntraUnitBonds``
- Reuse occlusion for secondary passes during multi-sampling
- Check if marking passes are needed before doing them
- Add ``resolutionScale`` parameter to allow trading quality of occlusion for performance
## [v3.2.0] - 2022-02-17
- Rename "best database mapping" to "SIFTS Mapping"
- Add schema and export support for ``atom_site.pdbx_sifts_xref_*`` fields
- Add schema export support for ``atom_site.pdbx_label_index`` field
- Add `traceOnly` parameter to chain/UniProt-based structure alignment
- Store ``IndexPairBonds`` as a dynamic property.
## [v3.1.0] - 2022-02-06
- Fix ``xrayShaded`` & ``ignoreLight`` params not working at the same time
- Add ``ignoreLight`` to component params
- Tweaks for cleaner default representation style
- Cartoon: use ``nucleotide-ring`` instead of ``nucleotide-block``
- Focus: use ``xrayShaded`` instead of opacity; adjust target size; don't show non-covalent interactions twice
- Fix representation preset side effects (changing post-processing parameters, see #363)
- Add Quick Styles panel (default, illustrative, stylized)
- Fix exported structure missing secondary-structure categories (#364)
- Fix volume streaming error message: distinguish between missing data and server error (#364)
## [v3.0.2] - 2022-01-30
- Fix color smoothing of elongated structures (by fixing ``Sphere.expand`` for spheres with highly directional extrema)
- Fix entity label not displayed when multiple instances of the same entity are highlighted
- Fix empty elements created in ``StructureElement.Loci.extendToAllInstances``
- Measurement options tweaks (allow larger ``textSize``; make ``customText`` essential)
- Fix visual visibility sync edge case when changing state snapshots
## [v3.0.1] - 2022-01-27
- Fix marking pass not working with ``transparentBackground``
- Fix pdbe xray maps url not https
- Fix entity-id color theme broken for non-IHM models
- Improve/fix marking of ``InteractionsInterUnitVisual`` (mark when all contact-feature members are given)
- Add missing "entity-id" and "enity-source" options for carbon coloring to "element-symbol" color theme
- Fix VolumeServer/query CLI
- Support automatic iso-value adjustment for VolumeServer data in ``Viewer.loadVolumeFromUrl``
- Emit drag event whenever started within viewport (not only for non-empty loci)
## [v3.0.0] - 2022-01-23
- Assembly handling tweaks:
- Do not include suffix for "identity assembly operators"
- Do not include assembly-related categories to export if the structure was composed from an assembly
- Special case for ``structAsymMap`` if Mol* asym id operator mapping is present
- Support for opening ZIP files with multiple entries
- Add Model Export extension
- Bugfix: Automatically treat empty string as "non-present" value in BinaryCIF writer.
- Fix coarse model support in entity-id color theme
- Fix marking of carbohydrate visuals (whole chain could get marked instead of single residue)
- Add custom colors to "element-symbol", "molecule-type", "residue-name", and "secondary-structure" themes
- Support/bugfixes for ``atom_site.pdbx_sifts_xref`` categories
- Improve/fix marking of ``InteractionsIntraUnitVisual`` (mark when all contact-feature members are given)
## [v3.0.0-dev.10] - 2022-01-17
- Fix ``getOperatorsForIndex``
@@ -19,7 +174,7 @@ Note that since we don't clearly distinguish between a public and private interf
- Add "camera rock" state animation
- Add support for custom colors to "molecule-type" theme
- [Breaking] Add style parameter to "illustrative" color theme
- Defaults to "entity-id" style instad of "chain-id"
- Defaults to "entity-id" style instead of "chain-id"
- Add "illustrative" representation preset
## [v3.0.0-dev.9] - 2022-01-09

72
CITATION.cff Normal file
View File

@@ -0,0 +1,72 @@
cff-version: 1.2.0
title: >-
Mol* library
message: >-
Please cite this software using the metadata from
'preferred-citation'.
authors:
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Áron Samuel
family-names: Kovács
- given-names: Ludovic
family-names: Autin
orcid: 'https://orcid.org/0000-0002-2197-191X'
- given-names: Michal
family-names: Malý
- given-names: Jiří
family-names: Černý
- given-names: Panagiotis
family-names: Tourlas
type: software
doi: 10.5281/zenodo.3947306
preferred-citation:
authors:
- given-names: David
family-names: Sehnal
orcid: 'https://orcid.org/0000-0002-0682-3089'
- given-names: Sebastian
family-names: Bittrich
orcid: 'https://orcid.org/0000-0003-3576-0387'
- given-names: Mandar
family-names: Deshpande
orcid: 'https://orcid.org/0000-0002-9043-7665'
- given-names: Radka
family-names: Svobodová
orcid: 'https://orcid.org/0000-0002-3840-8760'
- given-names: Karel
family-names: Berka
orcid: 'https://orcid.org/0000-0001-9472-2589'
- given-names: Václav
family-names: Bazgier
orcid: 'https://orcid.org/0000-0003-3393-3010'
- given-names: Sameer
family-names: Velankar
orcid: 'https://orcid.org/0000-0002-8439-5964'
- given-names: Stephen K
family-names: Burley
orcid: 'https://orcid.org/0000-0002-2487-9713'
- given-names: Jaroslav
family-names: Koča
orcid: 'https://orcid.org/0000-0002-2780-4901'
- given-names: Alexander S
family-names: Rose
orcid: 'https://orcid.org/0000-0002-0893-5551'
title: >-
Mol* Viewer: modern web app for 3D visualization
and analysis of large biomolecular structures
type: article
doi: 10.1093/nar/gkab314
journal: "Nucleic Acids Research"
issue: W1
volume: 49
year: 2021
month: 7
pages: "W431W437"

View File

@@ -120,6 +120,9 @@ and navigate to `build/viewer`
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-ions.js src/mol-model/structure/model/types/ions.ts
**Saccharide names**
node --max-old-space-size=4096 lib/commonjs/cli/chem-comp-dict/create-saccharides.js src/mol-model/structure/model/types/saccharides.ts
**GraphQL schemas**
@@ -138,7 +141,7 @@ and navigate to `build/viewer`
export NODE_PATH="lib"; node build/state-docs
**Convert any CIF to BinaryCIF**
**Convert any CIF to BinaryCIF (or vice versa)**
node lib/commonjs/servers/model/preprocess -i file.cif -ob file.bcif
@@ -148,6 +151,11 @@ Or
node lib/commonjs/cli/cif2bcif
E.g.
node lib/commonjs/cli/cif2bcif src.cif out.bcif.gz
node lib/commonjs/cli/cif2bcif src.bcif.gz out.cif
## Development
### Installation

View File

@@ -24,6 +24,11 @@ atom_site.auth_asym_id
atom_site.auth_seq_id
atom_site.pdbx_PDB_model_num
atom_site.ihm_model_id
atom_site.pdbx_label_index
atom_site.pdbx_sifts_xref_db_name
atom_site.pdbx_sifts_xref_db_acc
atom_site.pdbx_sifts_xref_db_num
atom_site.pdbx_sifts_xref_db_res
atom_site_anisotrop.id
atom_site_anisotrop.U
1 atom_sites.entry_id
24 atom_site.pdbx_PDB_model_num
25 atom_site.ihm_model_id
26 atom_site_anisotrop.id atom_site.pdbx_label_index
27 atom_site.pdbx_sifts_xref_db_name
28 atom_site.pdbx_sifts_xref_db_acc
29 atom_site.pdbx_sifts_xref_db_num
30 atom_site.pdbx_sifts_xref_db_res
31 atom_site_anisotrop.id
32 atom_site_anisotrop.U
33 atom_site_anisotrop.U_esd
34 atom_site_anisotrop.pdbx_PDB_ins_code

44
docs/file-formats.md Normal file
View File

@@ -0,0 +1,44 @@
Support file formats and their extensions.
## Structure
- MMCIF and CIFCORE (mmCIF and coreCIF schemas): cif, bcif, mmcif, mcif
- GRO: gro
- MOL: mol
- MOL2: mol2
- PDB/PDBQT: pdb, ent, pdbqt
- SDF: sdf, sd
- XYZ: xyz
## Topology
Need to be loaded together with Coordinates.
- PRMTOP: prmtop, parm7
- PSF: psf
- TOP: top
## Coordinates
Need to be loaded together with a Structure or Topology.
- DCD: dcd
- NCTRAJ: nc, nctraj
- TRR: trr
- XTC: xtc
## Volume
- CCP4/MRC/MAP: ccp4, mrc, map
- CUBE (may include a Structure): cub, cube
- DSN6/BRIX: dsn6, brix
- DX and DXBIN: dx, dxbin
- DSCIF (DensityServer CIF schema): cif, bcif
## Shape
- PLY

View File

@@ -13,6 +13,7 @@
* DZ has C1 instead of N1 (e.g. 6I4N)
* DP has N5 instead of C5 and C7 instead of N7 (e.g. 6I4N)
* Beta & Gamma peptides (e.g. 1GAC, 6PQF)
* Helices of D-amino acids (e.g. 7QDI)
* Mixed (heterogeneous) all-atom/trace-only RNA model (1JGQ)
* Polymers with residues with missing trace atoms (e.g. 2QFJ)
* Modified RNA bases (1y26, 5L4O)
@@ -34,6 +35,14 @@
* ACE (many, e.g. 5AGU, 1E1X)
* ACY in 7ABY
* NH2 (many, e.g. 6Y13)
* Ligands with many rings
* STU (e.g. 1U59) - many fused rings
* HT (e.g. 127D) - rings connected by a single bond
* J2C (e.g. 7EFJ) - rings connected by a single atom
* RBF (e.g. 7QF2) - three linearly fused rings
* TA1 (e.g. 1JFF) - many fused rings (incl. a 8-member rings)
* BPA (e.g. 1JDG) - many fused rings
* CLR (e.g. 3GKI) - four fused rings
Assembly symmetries
* 5M30 (Assembly 1, C3 local and pseudo)

47362
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.0.0-dev.10",
"version": "3.8.1",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -13,7 +13,7 @@
"scripts": {
"lint": "eslint .",
"lint-fix": "eslint . --fix",
"test": "npm run lint && jest",
"test": "npm install --no-save \"gl@^5.0.0\" && npm run lint && jest",
"jest": "jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"clean": "node ./scripts/clean.js",
@@ -92,72 +92,71 @@
"license": "MIT",
"devDependencies": {
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.3.1",
"@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/time": "^3.1.1",
"@graphql-codegen/typescript": "^2.4.2",
"@graphql-codegen/typescript": "^2.4.11",
"@graphql-codegen/typescript-graphql-files-modules": "^2.1.1",
"@graphql-codegen/typescript-graphql-request": "^4.3.3",
"@graphql-codegen/typescript-operations": "^2.2.2",
"@graphql-codegen/typescript-graphql-request": "^4.4.8",
"@graphql-codegen/typescript-operations": "^2.4.0",
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.4.0",
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"@types/jest": "^27.5.1",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.4",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"benchmark": "^2.1.4",
"concurrently": "^7.0.0",
"cpx2": "^4.1.2",
"concurrently": "^7.1.0",
"cpx2": "^4.2.0",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.5.1",
"eslint": "^8.7.0",
"css-loader": "^6.7.1",
"eslint": "^8.15.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
"graphql": "^16.2.0",
"fs-extra": "^10.1.0",
"graphql": "^16.5.0",
"http-server": "^14.1.0",
"jest": "^27.4.7",
"mini-css-extract-plugin": "~2.4.7",
"jest": "^27.5.1",
"mini-css-extract-plugin": "^2.6.0",
"path-browserify": "^1.0.1",
"raw-loader": "^4.0.2",
"sass": "^1.48.0",
"sass-loader": "^12.4.0",
"simple-git": "^2.48.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"sass": "^1.51.0",
"sass-loader": "^12.6.0",
"simple-git": "^3.7.1",
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"ts-jest": "^27.1.3",
"typescript": "^4.5.4",
"webpack": "^5.66.0",
"webpack-cli": "^4.9.1"
"ts-jest": "^27.1.4",
"typescript": "^4.6.4",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"@types/argparse": "^2.0.10",
"@types/benchmark": "^2.1.1",
"@types/compression": "1.7.2",
"@types/express": "^4.17.13",
"@types/node": "^16.11.19",
"@types/node-fetch": "^2.5.12",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/node": "^16.11.35",
"@types/node-fetch": "^2.6.1",
"@types/swagger-ui-dist": "3.30.1",
"argparse": "^2.0.1",
"body-parser": "^1.19.1",
"body-parser": "^1.20.0",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.17.2",
"express": "^4.18.1",
"h264-mp4-encoder": "^1.0.12",
"immer": "^9.0.12",
"immer": "^9.0.14",
"immutable": "^4.0.0",
"node-fetch": "^2.6.2",
"rxjs": "^7.5.2",
"swagger-ui-dist": "^4.1.3",
"tslib": "^2.3.1",
"node-fetch": "^2.6.7",
"rxjs": "^7.5.5",
"swagger-ui-dist": "^4.11.1",
"tslib": "^2.4.0",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"optionalDependencies": {
"gl": "^5.0.0"
"react": "^18.1.0 || ^17.0.2 || ^16.14.0",
"react-dom": "^18.1.0 || ^17.0.2 || ^16.14.0"
}
}

View File

@@ -8,7 +8,7 @@
import { Structure } from '../../mol-model/structure';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';

View File

@@ -44,10 +44,11 @@ function occlusionStyle(plugin: PluginContext) {
postprocessing: {
...plugin.canvas3d!.props.postprocessing,
occlusion: { name: 'on', params: {
samples: 64,
radius: 8,
bias: 1.0,
blurKernelSize: 13
bias: 0.8,
blurKernelSize: 15,
radius: 5,
samples: 32,
resolutionScale: 1
} },
outline: { name: 'on', params: {
scale: 1.0,

View File

@@ -13,22 +13,26 @@ import { GeometryExport } from '../../extensions/geo-export';
import { MAQualityAssessment } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessmentPLDDTPreset, QualityAssessmentQmeanPreset } from '../../extensions/model-archive/quality-assessment/behavior';
import { QualityAssessment } from '../../extensions/model-archive/quality-assessment/prop';
import { ModelExport } from '../../extensions/model-export';
import { Mp4Export } from '../../extensions/mp4-export';
import { PDBeStructureQualityReport } from '../../extensions/pdbe';
import { RCSBAssemblySymmetry, RCSBValidationReport } from '../../extensions/rcsb';
import { ZenodoImport } from '../../extensions/zenodo';
import { Volume } from '../../mol-model/volume';
import { DownloadStructure, PdbDownloadProvider } from '../../mol-plugin-state/actions/structure';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
import { BuiltInTopologyFormat } from '../../mol-plugin-state/formats/topology';
import { BuiltInCoordinatesFormat } from '../../mol-plugin-state/formats/coordinates';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -57,9 +61,11 @@ const Extensions = {
'rcsb-validation-report': PluginSpec.Behavior(RCSBValidationReport),
'anvil-membrane-orientation': PluginSpec.Behavior(ANVILMembraneOrientation),
'g3d': PluginSpec.Behavior(G3DFormat),
'model-export': PluginSpec.Behavior(ModelExport),
'mp4-export': PluginSpec.Behavior(Mp4Export),
'geo-export': PluginSpec.Behavior(GeometryExport),
'ma-quality-assessment': PluginSpec.Behavior(MAQualityAssessment),
'zenodo-import': PluginSpec.Behavior(ZenodoImport),
};
const DefaultViewerOptions = {
@@ -99,7 +105,14 @@ export class Viewer {
}
static async create(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
const o = { ...DefaultViewerOptions, ...options };
const definedOptions = {} as any;
// filter for defined properies only so the default values
// are property applied
for (const p of Object.keys(options) as (keyof ViewerOptions)[]) {
if (options[p] !== void 0) definedOptions[p] = options[p];
}
const o: ViewerOptions = { ...DefaultViewerOptions, ...definedOptions };
const defaultSpec = DefaultPluginUISpec();
const spec: PluginUISpec = {
@@ -357,11 +370,13 @@ export class Viewer {
const repr = plugin.build();
for (const iso of isovalues) {
const volume: StateObjectSelector<PluginStateObject.Volume.Data> = parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume;
const volumeData = volume.cell!.obj!.data;
repr
.to(parsed.volumes?.[iso.volumeIndex ?? 0] ?? parsed.volume)
.to(volume)
.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, firstVolume.data!, {
type: 'isosurface',
typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ? { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
typeParams: { alpha: iso.alpha ?? 1, isoValue: Volume.adjustedIsoValue(volumeData, iso.value, iso.type) },
color: 'uniform',
colorParams: { value: iso.color }
}));
@@ -441,11 +456,11 @@ export interface VolumeIsovalueInfo {
export interface LoadTrajectoryParams {
model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
| { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
| { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
| { kind: 'topology-url', url: string, format: BuiltInTopologyFormat, isBinary?: boolean }
| { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInTopologyFormat },
modelLabel?: string,
coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
coordinates: { kind: 'coordinates-url', url: string, format: BuiltInCoordinatesFormat, isBinary?: boolean }
| { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInCoordinatesFormat },
coordinatesLabel?: string,
preset?: keyof PresetTrajectoryHierarchy
}

View File

@@ -57,7 +57,7 @@
var pickScale = getParam('pick-scale', '[^&]+').trim();
var pickPadding = getParam('pick-padding', '[^&]+').trim();
var disableWboit = getParam('disable-wboit', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1';
var preferWebgl1 = getParam('prefer-webgl1', '[^&]+').trim() === '1' || void 0;
molstar.Viewer.create('app', {
layoutShowControls: !hideControls,
@@ -71,7 +71,7 @@
pixelScale: parseFloat(pixelScale) || 1,
pickScale: parseFloat(pickScale) || 0.25,
pickPadding: isNaN(parseFloat(pickPadding)) ? 1 : parseFloat(pickPadding),
enableWboit: !disableWboit,
enableWboit: disableWboit ? false : void 0, // use default value if disable-wboit is not set
preferWebgl1: preferWebgl1,
}).then(viewer => {
var snapshotId = getParam('snapshot-id', '[^&]+').trim();

View File

@@ -9,4 +9,4 @@ import './embedded.html';
import './favicon.ico';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
export * from './app';
export * from './app';

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env node
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Josh McMenemy <josh.mcmenemy@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse';
@@ -31,11 +32,11 @@ function extractIonNames(ccd: DatabaseCollection<CCD_Schema>) {
function writeIonNamesFile(filePath: string, ionNames: string[]) {
const output = `/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated ion names params file. Names extracted from CCD components.
*
* @author molstar/chem-comp-dict/create-table cli
* @author molstar/chem-comp-dict/create-ions cli
*/
export const IonNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});

View File

@@ -0,0 +1,77 @@
#!/usr/bin/env node
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import * as argparse from 'argparse';
import * as path from 'path';
import util from 'util';
import fs from 'fs';
require('util.promisify').shim();
const writeFile = util.promisify(fs.writeFile);
import { DatabaseCollection } from '../../mol-data/db';
import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd';
import { ensureDataAvailable, readCCD } from './util';
function extractSaccharideNames(ccd: DatabaseCollection<CCD_Schema>) {
const saccharideNames: string[] = [];
for (const k in ccd) {
const { chem_comp } = ccd[k];
const type = chem_comp.type.value(0).toUpperCase();
if (type.includes('SACCHARIDE')) {
saccharideNames.push(chem_comp.id.value(0));
}
}
// these are extra saccharides that don't have SACCHARIDE in their type
saccharideNames.push(
'UMQ', // UNDECYL-MALTOSIDE, via GlyFinder
'SQD', // SULFOQUINOVOSYLDIACYLGLYCEROL, via GlyFinder
);
return saccharideNames;
}
function writeSaccharideNamesFile(filePath: string, ionNames: string[]) {
const output = `/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated ion names params file. Names extracted from CCD components.
*
* @author molstar/cli/chem-comp-dict/create-saccharides
*/
export const SaccharideNames = new Set(${JSON.stringify(ionNames).replace(/"/g, "'").replace(/,/g, ', ')});
`;
writeFile(filePath, output);
}
async function run(out: string, forceDownload = false) {
await ensureDataAvailable(forceDownload);
const ccd = await readCCD();
const saccharideNames = extractSaccharideNames(ccd);
if (!fs.existsSync(path.dirname(out))) {
fs.mkdirSync(path.dirname(out));
}
writeSaccharideNamesFile(out, saccharideNames);
}
const parser = new argparse.ArgumentParser({
add_help: true,
description: 'Extract and save SaccharideNames from CCD.'
});
parser.add_argument('out', {
help: 'Generated file output path.'
});
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.parse_args();
run(args.out, args.forceDownload);

View File

@@ -71,7 +71,7 @@ function classify(name: string, field: CifField): CifWriter.Field {
}
export function convert(path: string, asText = false, hints?: EncodingStrategyHint[], filter?: string) {
return Task.create<Uint8Array>('BinaryCIF', async ctx => {
return Task.create<Uint8Array>('Convert CIF', async ctx => {
const encodingProvider: BinaryEncodingProvider = hints
? CifWriter.createEncodingProviderFromJsonConfig(hints)
: { get: (c, f) => void 0 };

View File

@@ -18,7 +18,7 @@ async function process(srcPath: string, outPath: string, configPath?: string, fi
const config = configPath ? JSON.parse(fs.readFileSync(configPath, 'utf8')) : void 0;
const filter = filterPath ? fs.readFileSync(filterPath, 'utf8') : void 0;
const res = await convert(srcPath, false, config, filter);
const res = await convert(srcPath, srcPath.toLowerCase().indexOf('.bcif') > 0, config, filter);
await write(outPath, res);
}
@@ -38,13 +38,13 @@ function run(args: Args) {
const parser = new argparse.ArgumentParser({
add_help: true,
description: 'Convert any CIF file to a BCIF file'
description: 'Convert any BCIF file to a CIF file or vice versa'
});
parser.add_argument('src', {
help: 'Source CIF path'
help: 'Source file path'
});
parser.add_argument('out', {
help: 'Output BCIF path'
help: 'Output file path'
});
parser.add_argument('-c', '--config', {
help: 'Optional encoding strategy/precision config path',

View File

@@ -161,7 +161,7 @@ const MMCIF_DIC_URL = 'http://mmcif.wwpdb.org/dictionaries/ascii/mmcif_pdbx_v50.
const IHM_DIC_PATH = `${DIC_DIR}/ihm-extension.dic`;
const IHM_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/IHM-dictionary/master/ihm-extension.dic';
const MA_DIC_PATH = `${DIC_DIR}/ma-extension.dic`;
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/MA-dictionary/master/mmcif_ma.dic';
const MA_DIC_URL = 'https://raw.githubusercontent.com/ihmwg/ModelCIF/master/dist/mmcif_ma.dic';
const CIF_CORE_DIC_PATH = `${DIC_DIR}/cif_core.dic`;
const CIF_CORE_DIC_URL = 'https://raw.githubusercontent.com/COMCIFS/cif_core/master/cif_core.dic';

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -13,15 +13,17 @@ export function getFieldType(type: string, description: string, values?: string[
switch (type) {
// mmCIF
case 'code':
case 'ucode':
case 'line':
case 'uline':
case 'text':
case 'char':
case 'uchar3':
case 'uchar1':
case 'boolean':
return values && values.length ? EnumCol(values, 'str', description) : StrCol(description);
case 'ucode':
case 'uline':
case 'uchar3':
case 'uchar1':
// only force lower-case for enums
return values && values.length ? EnumCol(values.map(x => x.toLowerCase()), 'lstr', description) : StrCol(description);
case 'aliasname':
case 'name':
case 'idname':
@@ -49,6 +51,7 @@ export function getFieldType(type: string, description: string, values?: string[
case 'operation_expression':
case 'point_symmetry':
case '4x3_matrix':
case '3x4_matrix':
case '3x4_matrices':
case 'point_group':
case 'point_group_helical':

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -10,7 +10,7 @@ import { FieldPath } from '../../../mol-io/reader/cif/schema';
function header(name: string, info: string, moldataImportPath: string) {
return `/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated '${name}' schema file. ${info}
*
@@ -35,13 +35,17 @@ function getTypeShorthands(schema: Database, fields?: Filter) {
const { columns } = schema.tables[table];
Object.keys(columns).forEach(columnName => {
if (fields && !fields[table][columnName]) return;
types.add(schema.tables[table].columns[columnName].type);
const col = schema.tables[table].columns[columnName];
if (col.type === 'enum') types.add(col.subType);
types.add(col.type);
});
});
const shorthands: string[] = [];
types.forEach(type => {
switch (type) {
case 'str': shorthands.push('const str = Schema.str;'); break;
case 'ustr': shorthands.push('const ustr = Schema.ustr;'); break;
case 'lstr': shorthands.push('const lstr = Schema.lstr;'); break;
case 'int': shorthands.push('const int = Schema.int;'); break;
case 'float': shorthands.push('const float = Schema.float;'); break;
case 'coord': shorthands.push('const coord = Schema.coord;'); break;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -29,8 +29,8 @@ export function FloatCol(description: string): FloatCol { return { type: 'float'
export type CoordCol = { type: 'coord' } & BaseCol
export function CoordCol(description: string): CoordCol { return { type: 'coord', description }; }
export type EnumCol = { type: 'enum', subType: 'int' | 'str', values: string[] } & BaseCol
export function EnumCol(values: string[], subType: 'int' | 'str', description: string): EnumCol {
export type EnumCol = { type: 'enum', subType: 'int' | 'str' | 'ustr' | 'lstr', values: string[] } & BaseCol
export function EnumCol(values: string[], subType: 'int' | 'str' | 'ustr' | 'lstr', description: string): EnumCol {
return { type: 'enum', description, values, subType };
}

View File

@@ -11,7 +11,7 @@ import { SphericalBasisOrder } from '../../extensions/alpha-orbitals/spherical-f
import { BasisAndOrbitals, CreateOrbitalDensityVolume, CreateOrbitalRepresentation3D, CreateOrbitalVolume, StaticBasisAndOrbitals } from '../../extensions/alpha-orbitals/transforms';
import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
import { PluginStateObject } from '../../mol-plugin-state/objects';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -80,20 +80,24 @@ export class AlphaOrbitalsExample {
this.plugin.managers.interactivity.setProps({ granularity: 'element' });
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
PluginCommands.Toast.Show(this.plugin, {
title: 'Error',
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
this.plugin.behaviors.canvas3d.initialized.subscribe(init => {
if (!init) return;
if (!canComputeGrid3dOnGPU(this.plugin.canvas3d?.webgl)) {
PluginCommands.Toast.Show(this.plugin, {
title: 'Error',
message: `Browser/device does not support required WebGL extension (OES_texture_float).`
});
return;
}
this.load({
moleculeSdf: DemoMoleculeSDF,
...DemoOrbitals
});
return;
}
this.load({
moleculeSdf: DemoMoleculeSDF,
...DemoOrbitals
mountControls(this, document.getElementById('controls')!);
});
mountControls(this, document.getElementById('controls')!);
}
readonly params = new BehaviorSubject<ParamDefinition.For<Params>>({} as any);

View File

@@ -9,7 +9,7 @@ import { EmptyLoci } from '../../mol-model/loci';
import { StructureSelection } from '../../mol-model/structure';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/model-index';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';

View File

@@ -6,7 +6,7 @@
import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
@@ -24,7 +24,7 @@ const Canvas3DPresets = {
illustrative: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
outline: { name: 'on', params: { scale: 1, threshold: 0.33, color: Color(0x000000) } }
},
renderer: {
@@ -36,7 +36,7 @@ const Canvas3DPresets = {
occlusion: {
canvas3d: <Preset>{
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15, resolutionScale: 1 } },
outline: { name: 'off', params: {} }
},
renderer: {

View File

@@ -10,7 +10,7 @@ import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/mod
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { createPluginUI } from '../../mol-plugin-ui';
import { createPluginUI } from '../../mol-plugin-ui/react18';
import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';

View File

@@ -32,6 +32,7 @@ import { Color } from '../../mol-util/color';
import { objectForEach } from '../../mol-util/object';
import { readFromFile } from '../../mol-util/data-source';
import { ColorNames } from '../../mol-util/color/names';
import { createBasic } from '../../mol-model-formats/structure/basic/schema';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`;
@@ -310,7 +311,8 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
const cif = getCifCurve(name, transforms, model);
const curveModelTask = Task.create('Curve Model', async ctx => {
const format = MmcifFormat.fromFrame(cif);
const models = await createModels(format.data.db, format, ctx);
const basic = createBasic(format.data.db, true);
const models = await createModels(basic, format, ctx);
return models.representative;
});

View File

@@ -11,149 +11,248 @@ import { Location } from '../../../mol-model/location';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { ColorTheme } from '../../../mol-theme/color';
import { ThemeDataContext } from '../../../mol-theme/theme';
import { Color } from '../../../mol-util/color';
import { Color, ColorMap } from '../../../mol-util/color';
import { getColorMapParams } from '../../../mol-util/color/params';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { TableLegend } from '../../../mol-util/legend';
import { iterableToArray } from '../../../mol-data/util';
import { ObjectKeys } from '../../../mol-util/type-helpers';
const DefaultColor = Color(0xCCCCCC);
const Description = 'Assigns colors to confal pyramids';
const DefaultClassColors = {
A: 0xFFC1C1,
B: 0xC8CFFF,
BII: 0x0059DA,
miB: 0x3BE8FB,
Z: 0x01F60E,
IC: 0xFA5CFB,
OPN: 0xE90000,
SYN: 0xFFFF01,
N: 0xF2F2F2,
};
const ErrorColor = Color(0xFFA10A);
type ConformerClasses = 'A' | 'B' | 'BII' | 'miB' | 'Z' | 'IC' | 'OPN' | 'SYN' | 'N';
const PyramidsColors = ColorMap({
NANT_Upr: DefaultClassColors.N,
NANT_Lwr: DefaultClassColors.N,
AA00_Upr: DefaultClassColors.A,
AA00_Lwr: DefaultClassColors.A,
AA02_Upr: DefaultClassColors.A,
AA02_Lwr: DefaultClassColors.A,
AA03_Upr: DefaultClassColors.A,
AA03_Lwr: DefaultClassColors.A,
AA04_Upr: DefaultClassColors.A,
AA04_Lwr: DefaultClassColors.A,
AA08_Upr: DefaultClassColors.A,
AA08_Lwr: DefaultClassColors.A,
AA09_Upr: DefaultClassColors.A,
AA09_Lwr: DefaultClassColors.A,
AA01_Upr: DefaultClassColors.A,
AA01_Lwr: DefaultClassColors.A,
AA05_Upr: DefaultClassColors.A,
AA05_Lwr: DefaultClassColors.A,
AA06_Upr: DefaultClassColors.A,
AA06_Lwr: DefaultClassColors.A,
AA10_Upr: DefaultClassColors.A,
AA10_Lwr: DefaultClassColors.A,
AA11_Upr: DefaultClassColors.A,
AA11_Lwr: DefaultClassColors.A,
AA07_Upr: DefaultClassColors.A,
AA07_Lwr: DefaultClassColors.A,
AA12_Upr: DefaultClassColors.A,
AA12_Lwr: DefaultClassColors.A,
AA13_Upr: DefaultClassColors.A,
AA13_Lwr: DefaultClassColors.A,
AB01_Upr: DefaultClassColors.A,
AB01_Lwr: DefaultClassColors.B,
AB02_Upr: DefaultClassColors.A,
AB02_Lwr: DefaultClassColors.B,
AB03_Upr: DefaultClassColors.A,
AB03_Lwr: DefaultClassColors.B,
AB04_Upr: DefaultClassColors.A,
AB04_Lwr: DefaultClassColors.B,
AB05_Upr: DefaultClassColors.A,
AB05_Lwr: DefaultClassColors.B,
BA01_Upr: DefaultClassColors.B,
BA01_Lwr: DefaultClassColors.A,
BA05_Upr: DefaultClassColors.B,
BA05_Lwr: DefaultClassColors.A,
BA09_Upr: DefaultClassColors.B,
BA09_Lwr: DefaultClassColors.A,
BA08_Upr: DefaultClassColors.BII,
BA08_Lwr: DefaultClassColors.A,
BA10_Upr: DefaultClassColors.B,
BA10_Lwr: DefaultClassColors.A,
BA13_Upr: DefaultClassColors.BII,
BA13_Lwr: DefaultClassColors.A,
BA16_Upr: DefaultClassColors.BII,
BA16_Lwr: DefaultClassColors.A,
BA17_Upr: DefaultClassColors.BII,
BA17_Lwr: DefaultClassColors.A,
BB00_Upr: DefaultClassColors.B,
BB00_Lwr: DefaultClassColors.B,
BB01_Upr: DefaultClassColors.B,
BB01_Lwr: DefaultClassColors.B,
BB17_Upr: DefaultClassColors.B,
BB17_Lwr: DefaultClassColors.B,
BB02_Upr: DefaultClassColors.B,
BB02_Lwr: DefaultClassColors.B,
BB03_Upr: DefaultClassColors.B,
BB03_Lwr: DefaultClassColors.B,
BB11_Upr: DefaultClassColors.B,
BB11_Lwr: DefaultClassColors.B,
BB16_Upr: DefaultClassColors.B,
BB16_Lwr: DefaultClassColors.B,
BB04_Upr: DefaultClassColors.B,
BB04_Lwr: DefaultClassColors.BII,
BB05_Upr: DefaultClassColors.B,
BB05_Lwr: DefaultClassColors.BII,
BB07_Upr: DefaultClassColors.BII,
BB07_Lwr: DefaultClassColors.BII,
BB08_Upr: DefaultClassColors.BII,
BB08_Lwr: DefaultClassColors.BII,
BB10_Upr: DefaultClassColors.miB,
BB10_Lwr: DefaultClassColors.miB,
BB12_Upr: DefaultClassColors.miB,
BB12_Lwr: DefaultClassColors.miB,
BB13_Upr: DefaultClassColors.miB,
BB13_Lwr: DefaultClassColors.miB,
BB14_Upr: DefaultClassColors.miB,
BB14_Lwr: DefaultClassColors.miB,
BB15_Upr: DefaultClassColors.miB,
BB15_Lwr: DefaultClassColors.miB,
BB20_Upr: DefaultClassColors.miB,
BB20_Lwr: DefaultClassColors.miB,
IC01_Upr: DefaultClassColors.IC,
IC01_Lwr: DefaultClassColors.IC,
IC02_Upr: DefaultClassColors.IC,
IC02_Lwr: DefaultClassColors.IC,
IC03_Upr: DefaultClassColors.IC,
IC03_Lwr: DefaultClassColors.IC,
IC04_Upr: DefaultClassColors.IC,
IC04_Lwr: DefaultClassColors.IC,
IC05_Upr: DefaultClassColors.IC,
IC05_Lwr: DefaultClassColors.IC,
IC06_Upr: DefaultClassColors.IC,
IC06_Lwr: DefaultClassColors.IC,
IC07_Upr: DefaultClassColors.IC,
IC07_Lwr: DefaultClassColors.IC,
OP01_Upr: DefaultClassColors.OPN,
OP01_Lwr: DefaultClassColors.OPN,
OP02_Upr: DefaultClassColors.OPN,
OP02_Lwr: DefaultClassColors.OPN,
OP03_Upr: DefaultClassColors.OPN,
OP03_Lwr: DefaultClassColors.OPN,
OP04_Upr: DefaultClassColors.OPN,
OP04_Lwr: DefaultClassColors.OPN,
OP05_Upr: DefaultClassColors.OPN,
OP05_Lwr: DefaultClassColors.OPN,
OP06_Upr: DefaultClassColors.OPN,
OP06_Lwr: DefaultClassColors.OPN,
OP07_Upr: DefaultClassColors.OPN,
OP07_Lwr: DefaultClassColors.OPN,
OP08_Upr: DefaultClassColors.OPN,
OP08_Lwr: DefaultClassColors.OPN,
OP09_Upr: DefaultClassColors.OPN,
OP09_Lwr: DefaultClassColors.OPN,
OP10_Upr: DefaultClassColors.OPN,
OP10_Lwr: DefaultClassColors.OPN,
OP11_Upr: DefaultClassColors.OPN,
OP11_Lwr: DefaultClassColors.OPN,
OP12_Upr: DefaultClassColors.OPN,
OP12_Lwr: DefaultClassColors.OPN,
OP13_Upr: DefaultClassColors.OPN,
OP13_Lwr: DefaultClassColors.OPN,
OP14_Upr: DefaultClassColors.OPN,
OP14_Lwr: DefaultClassColors.OPN,
OP15_Upr: DefaultClassColors.OPN,
OP15_Lwr: DefaultClassColors.OPN,
OP16_Upr: DefaultClassColors.OPN,
OP16_Lwr: DefaultClassColors.OPN,
OP17_Upr: DefaultClassColors.OPN,
OP17_Lwr: DefaultClassColors.OPN,
OP18_Upr: DefaultClassColors.OPN,
OP18_Lwr: DefaultClassColors.OPN,
OP19_Upr: DefaultClassColors.OPN,
OP19_Lwr: DefaultClassColors.OPN,
OP20_Upr: DefaultClassColors.OPN,
OP20_Lwr: DefaultClassColors.OPN,
OP21_Upr: DefaultClassColors.OPN,
OP21_Lwr: DefaultClassColors.OPN,
OP22_Upr: DefaultClassColors.OPN,
OP22_Lwr: DefaultClassColors.OPN,
OP23_Upr: DefaultClassColors.OPN,
OP23_Lwr: DefaultClassColors.OPN,
OP24_Upr: DefaultClassColors.OPN,
OP24_Lwr: DefaultClassColors.OPN,
OP25_Upr: DefaultClassColors.OPN,
OP25_Lwr: DefaultClassColors.OPN,
OP26_Upr: DefaultClassColors.OPN,
OP26_Lwr: DefaultClassColors.OPN,
OP27_Upr: DefaultClassColors.OPN,
OP27_Lwr: DefaultClassColors.OPN,
OP28_Upr: DefaultClassColors.OPN,
OP28_Lwr: DefaultClassColors.OPN,
OP29_Upr: DefaultClassColors.OPN,
OP29_Lwr: DefaultClassColors.OPN,
OP30_Upr: DefaultClassColors.OPN,
OP30_Lwr: DefaultClassColors.OPN,
OP31_Upr: DefaultClassColors.OPN,
OP31_Lwr: DefaultClassColors.OPN,
OPS1_Upr: DefaultClassColors.OPN,
OPS1_Lwr: DefaultClassColors.OPN,
OP1S_Upr: DefaultClassColors.OPN,
OP1S_Lwr: DefaultClassColors.SYN,
AAS1_Upr: DefaultClassColors.SYN,
AAS1_Lwr: DefaultClassColors.A,
AB1S_Upr: DefaultClassColors.A,
AB1S_Lwr: DefaultClassColors.SYN,
AB2S_Upr: DefaultClassColors.A,
AB2S_Lwr: DefaultClassColors.SYN,
BB1S_Upr: DefaultClassColors.B,
BB1S_Lwr: DefaultClassColors.SYN,
BB2S_Upr: DefaultClassColors.B,
BB2S_Lwr: DefaultClassColors.SYN,
BBS1_Upr: DefaultClassColors.SYN,
BBS1_Lwr: DefaultClassColors.B,
ZZ01_Upr: DefaultClassColors.Z,
ZZ01_Lwr: DefaultClassColors.Z,
ZZ02_Upr: DefaultClassColors.Z,
ZZ02_Lwr: DefaultClassColors.Z,
ZZ1S_Upr: DefaultClassColors.Z,
ZZ1S_Lwr: DefaultClassColors.SYN,
ZZ2S_Upr: DefaultClassColors.Z,
ZZ2S_Lwr: DefaultClassColors.SYN,
ZZS1_Upr: DefaultClassColors.SYN,
ZZS1_Lwr: DefaultClassColors.Z,
ZZS2_Upr: DefaultClassColors.SYN,
ZZS2_Lwr: DefaultClassColors.Z,
});
type PyramidsColors = typeof PyramidsColors;
const ColorMapping: ReadonlyMap<ConformerClasses, Color> = new Map([
['A', Color(0xFFC1C1)],
['B', Color(0xC8CFFF)],
['BII', Color(0x0059DA)],
['miB', Color(0x3BE8FB)],
['Z', Color(0x01F60E)],
['IC', Color(0xFA5CFB)],
['OPN', Color(0xE90000)],
['SYN', Color(0xFFFF01)],
['N', Color(0xF2F2F2)],
]);
export const ConfalPyramidsColorThemeParams = {
colors: PD.MappedStatic('default', {
'default': PD.EmptyGroup(),
'custom': PD.Group(getColorMapParams(PyramidsColors))
}),
};
export type ConfalPyramidsColorThemeParams = typeof ConfalPyramidsColorThemeParams;
const NtCToClasses: ReadonlyMap<string, [ConformerClasses, ConformerClasses]> = new Map([
['NANT', ['N', 'N']],
['AA00', ['A', 'A']],
['AA02', ['A', 'A']],
['AA03', ['A', 'A']],
['AA04', ['A', 'A']],
['AA08', ['A', 'A']],
['AA09', ['A', 'A']],
['AA01', ['A', 'A']],
['AA05', ['A', 'A']],
['AA06', ['A', 'A']],
['AA10', ['A', 'A']],
['AA11', ['A', 'A']],
['AA07', ['A', 'A']],
['AA12', ['A', 'A']],
['AA13', ['A', 'A']],
['AB01', ['A', 'B']],
['AB02', ['A', 'B']],
['AB03', ['A', 'B']],
['AB04', ['A', 'B']],
['AB05', ['A', 'B']],
['BA01', ['B', 'A']],
['BA05', ['B', 'A']],
['BA09', ['B', 'A']],
['BA08', ['BII', 'A']],
['BA10', ['B', 'A']],
['BA13', ['BII', 'A']],
['BA16', ['BII', 'A']],
['BA17', ['BII', 'A']],
['BB00', ['B', 'B']],
['BB01', ['B', 'B']],
['BB17', ['B', 'B']],
['BB02', ['B', 'B']],
['BB03', ['B', 'B']],
['BB11', ['B', 'B']],
['BB16', ['B', 'B']],
['BB04', ['B', 'BII']],
['BB05', ['B', 'BII']],
['BB07', ['BII', 'BII']],
['BB08', ['BII', 'BII']],
['BB10', ['miB', 'miB']],
['BB12', ['miB', 'miB']],
['BB13', ['miB', 'miB']],
['BB14', ['miB', 'miB']],
['BB15', ['miB', 'miB']],
['BB20', ['miB', 'miB']],
['IC01', ['IC', 'IC']],
['IC02', ['IC', 'IC']],
['IC03', ['IC', 'IC']],
['IC04', ['IC', 'IC']],
['IC05', ['IC', 'IC']],
['IC06', ['IC', 'IC']],
['IC07', ['IC', 'IC']],
['OP01', ['OPN', 'OPN']],
['OP02', ['OPN', 'OPN']],
['OP03', ['OPN', 'OPN']],
['OP04', ['OPN', 'OPN']],
['OP05', ['OPN', 'OPN']],
['OP06', ['OPN', 'OPN']],
['OP07', ['OPN', 'OPN']],
['OP08', ['OPN', 'OPN']],
['OP09', ['OPN', 'OPN']],
['OP10', ['OPN', 'OPN']],
['OP11', ['OPN', 'OPN']],
['OP12', ['OPN', 'OPN']],
['OP13', ['OPN', 'OPN']],
['OP14', ['OPN', 'OPN']],
['OP15', ['OPN', 'OPN']],
['OP16', ['OPN', 'OPN']],
['OP17', ['OPN', 'OPN']],
['OP18', ['OPN', 'OPN']],
['OP19', ['OPN', 'OPN']],
['OP20', ['OPN', 'OPN']],
['OP21', ['OPN', 'OPN']],
['OP22', ['OPN', 'OPN']],
['OP23', ['OPN', 'OPN']],
['OP24', ['OPN', 'OPN']],
['OP25', ['OPN', 'OPN']],
['OP26', ['OPN', 'OPN']],
['OP27', ['OPN', 'OPN']],
['OP28', ['OPN', 'OPN']],
['OP29', ['OPN', 'OPN']],
['OP30', ['OPN', 'OPN']],
['OP31', ['OPN', 'OPN']],
['OPS1', ['OPN', 'OPN']],
['OP1S', ['OPN', 'SYN']],
['AAS1', ['SYN', 'A']],
['AB1S', ['A', 'SYN']],
['AB2S', ['A', 'SYN']],
['BB1S', ['B', 'SYN']],
['BB2S', ['B', 'SYN']],
['BBS1', ['SYN', 'B']],
['ZZ01', ['Z', 'Z']],
['ZZ02', ['Z', 'Z']],
['ZZ1S', ['Z', 'SYN']],
['ZZ2S', ['Z', 'SYN']],
['ZZS1', ['SYN', 'Z']],
['ZZS2', ['SYN', 'Z']],
]);
function getConformerColor(ntc: string, useLower: boolean): Color {
const item = NtCToClasses.get(ntc);
if (!item) return ErrorColor;
return ColorMapping.get(useLower ? item[1] : item[0]) ?? ErrorColor;
}
export const ConfalPyramidsColorThemeParams = {};
export type ConfalPyramidsColorThemeParams = typeof ConfalPyramidsColorThemeParams
export function getConfalPyramidsColorThemeParams(ctx: ThemeDataContext) {
return ConfalPyramidsColorThemeParams; // TODO return copy
return PD.clone(ConfalPyramidsColorThemeParams);
}
export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values<ConfalPyramidsColorThemeParams>): ColorTheme<ConfalPyramidsColorThemeParams> {
const colorMap = props.colors.name === 'default' ? PyramidsColors : props.colors.params;
function color(location: Location, isSecondary: boolean): Color {
if (CPT.isLocation(location)) {
const { pyramid, isLower } = location.data;
return getConformerColor(pyramid.NtC, isLower);
const key = pyramid.NtC + `_${isLower ? 'Lwr' : 'Upr'}` as keyof PyramidsColors;
return colorMap[key] ?? ErrorColor;
}
return DefaultColor;
return ErrorColor;
}
return {
@@ -162,12 +261,7 @@ export function ConfalPyramidsColorTheme(ctx: ThemeDataContext, props: PD.Values
color,
props,
description: Description,
legend: TableLegend(iterableToArray(ColorMapping.entries()).map(([conformer, color]) => {
return [conformer, color] as [string, Color];
}).concat([
['Error', ErrorColor],
['Unknown', DefaultColor]
]))
legend: TableLegend(ObjectKeys(colorMap).map(k => [k.replace('_', ' '), colorMap[k]] as [string, Color]).concat([['Error', ErrorColor]])),
};
}

View File

@@ -123,10 +123,8 @@ function createPyramidsFromCif(model: Model,
for (let i = 0; i < _rowCount; i++) {
const model_num = PDB_model_number.value(i);
if (model_num !== model.modelNum) {
if (model_num !== model.modelNum)
hasMultipleModels = true;
continue; // We are only interested in data for the current model
}
const { _NtC, _confal_score } = getNtCAndConfalScore(id.value(i), i, stepsSummary);

View File

@@ -7,7 +7,7 @@
import { ConfalPyramidsProvider } from './property';
import { ConfalPyramidsTypes as CPT } from './types';
import { OrderedSet, Segmentation } from '../../../mol-data/int';
import { Segmentation } from '../../../mol-data/int';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure';
@@ -63,15 +63,12 @@ export namespace ConfalPyramidsUtil {
return prop.data.hasMultipleModels;
}
function getPossibleAltIdsIndices(eIFirst: ElementIndex, eILast: ElementIndex, structure: Structure, unit: Unit.Atomic): string[] {
const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
const uIFirst = OrderedSet.indexOf(unit.elements, eIFirst);
const uILast = OrderedSet.indexOf(unit.elements, eILast);
function getPossibleAltIds(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] {
const possibleAltIds: string[] = [];
for (let uI = uIFirst; uI <= uILast; uI++) {
loc.element = unit.elements[uI];
const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex);
for (let rI = residue.start; rI <= residue.end - 1; rI++) {
loc.element = unit.elements[rI];
const altId = StructureProperties.atom.label_alt_id(loc);
if (altId !== '' && !possibleAltIds.includes(altId)) possibleAltIds.push(altId);
}
@@ -79,10 +76,6 @@ export namespace ConfalPyramidsUtil {
return possibleAltIds;
}
function getPossibleAltIdsResidue(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] {
return getPossibleAltIdsIndices(unit.elements[residue.start], unit.elements[residue.end - 1], structure, unit);
}
class Utility {
protected getPyramidByName(name: string): { pyramid: CPT.Pyramid | undefined, index: number } {
const index = this.data.names.get(name);
@@ -122,19 +115,22 @@ export namespace ConfalPyramidsUtil {
export class UnitWalker extends Utility {
private getAtomIndices(names: string[], residue: Residue): ElementIndex[] {
let rI = residue.start;
const rILast = residue.end - 1;
const indices: ElementIndex[] = [];
for (; rI !== rILast; rI++) {
const eI = this.unit.elements[rI];
const loc = StructureElement.Location.create(this.structure, this.unit, eI);
const loc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex);
for (let rI = residue.start; rI <= residue.end - 1; rI++) {
loc.element = this.unit.elements[rI];
const thisName = StructureProperties.atom.label_atom_id(loc);
if (names.includes(thisName)) indices.push(eI);
if (names.includes(thisName)) indices.push(loc.element);
}
if (indices.length === 0)
throw new Error(`Element ${name} not found on residue ${residue.index}`);
if (indices.length === 0) {
let namesStr = '';
for (const n of names)
namesStr += `${n} `;
throw new Error(`Element [${namesStr}] not found on residue ${residue.index}`);
}
return indices;
}
@@ -257,12 +253,12 @@ export namespace ConfalPyramidsUtil {
}
private step(residue: Residue): { firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[] } {
const firstPossibleAltIds = getPossibleAltIdsResidue(residue, this.structure, this.unit);
const firstPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
const firstAtoms = this.processFirstResidue(residue, firstPossibleAltIds);
residue = this.residueIt.move();
const secondPossibleAltIds = getPossibleAltIdsResidue(residue, this.structure, this.unit);
const secondPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit);
const secondAtoms = this.processSecondResidue(residue, secondPossibleAltIds);
return { firstAtoms, secondAtoms };

View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { utf8ByteCount, utf8Write } from '../../mol-io/common/utf8';
import { to_mmCIF, Unit } from '../../mol-model/structure';
import { PluginContext } from '../../mol-plugin/context';
import { Task } from '../../mol-task';
import { getFormattedTime } from '../../mol-util/date';
import { download } from '../../mol-util/download';
import { zip } from '../../mol-util/zip/zip';
export async function exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
try {
await plugin.runTask(_exportHierarchy(plugin, options), { useOverlay: true });
} catch (e) {
console.error(e);
plugin.log.error(`Model export failed. See console for details.`);
}
}
function _exportHierarchy(plugin: PluginContext, options?: { format?: 'cif' | 'bcif' }) {
return Task.create('Export', async ctx => {
await ctx.update({ message: 'Exporting...', isIndeterminate: true, canAbort: false });
const format = options?.format ?? 'cif';
const { structures } = plugin.managers.structure.hierarchy.current;
const files: [name: string, data: string | Uint8Array][] = [];
const entryMap = new Map<string, number>();
for (const _s of structures) {
const s = _s.transform?.cell.obj?.data ?? _s.cell.obj?.data;
if (!s) continue;
if (s.models.length > 1) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Multimodel exports not supported.`);
continue;
}
if (s.units.some(u => !Unit.isAtomic(u))) {
plugin.log.warn(`[Export] Skipping ${_s.cell.obj?.label}: Non-atomic model exports not supported.`);
continue;
}
const name = entryMap.has(s.model.entryId)
? `${s.model.entryId}_${entryMap.get(s.model.entryId)! + 1}.${format}`
: `${s.model.entryId}.${format}`;
entryMap.set(s.model.entryId, (entryMap.get(s.model.entryId) ?? 0) + 1);
await ctx.update({ message: `Exporting ${s.model.entryId}...`, isIndeterminate: true, canAbort: false });
if (s.elementCount > 100000) {
// Give UI chance to update, only needed for larger structures.
await new Promise(res => setTimeout(res, 50));
}
try {
files.push([name, to_mmCIF(s.model.entryId, s, format === 'bcif', { copyAllCategories: true })]);
} catch (e) {
if (format === 'cif' && s.elementCount > 2000000) {
plugin.log.warn(`[Export] The structure might be too big to be exported as Text CIF, consider using the BinaryCIF format instead.`);
}
throw e;
}
}
if (files.length === 1) {
download(new Blob([files[0][1]]), files[0][0]);
} else if (files.length > 1) {
const zipData: Record<string, Uint8Array> = {};
for (const [fn, data] of files) {
if (data instanceof Uint8Array) {
zipData[fn] = data;
} else {
const bytes = new Uint8Array(utf8ByteCount(data));
utf8Write(bytes, 0, data);
zipData[fn] = bytes;
}
}
await ctx.update({ message: `Compressing Data...`, isIndeterminate: true, canAbort: false });
const buffer = await zip(ctx, zipData);
download(new Blob([new Uint8Array(buffer, 0, buffer.byteLength)]), `structures_${getFormattedTime()}.zip`);
}
plugin.log.info(`[Export] Done.`);
});
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { ModelExportUI } from './ui';
export const ModelExport = PluginBehavior.create<{}>({
name: 'extension-model-export',
category: 'misc',
display: {
name: 'Model Export'
},
ctor: class extends PluginBehavior.Handler<{}> {
register(): void {
this.ctx.customStructureControls.set('model-export', ModelExportUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customStructureControls.delete('model-export');
}
},
params: () => ({})
});

View File

@@ -0,0 +1,69 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { useState } from 'react';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { GetAppSvg } from '../../mol-plugin-ui/controls/icons';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { useBehavior } from '../../mol-plugin-ui/hooks/use-behavior';
import { PluginContext } from '../../mol-plugin/context';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { exportHierarchy } from './export';
export class ModelExportUI extends CollapsableControls<{}, {}> {
protected defaultState(): CollapsableState {
return {
header: 'Export Models',
isCollapsed: true,
brand: { accent: 'cyan', svg: GetAppSvg }
};
}
protected renderControls(): JSX.Element | null {
return <ExportControls plugin={this.plugin} />;
}
}
const Params = {
format: PD.Select<'cif' | 'bcif'>('cif', [['cif', 'mmCIF'], ['bcif', 'Binary mmCIF']])
};
const DefaultParams = PD.getDefaultValues(Params);
function ExportControls({ plugin }: { plugin: PluginContext }) {
const [params, setParams] = useState(DefaultParams);
const [exporting, setExporting] = useState(false);
useBehavior(plugin.managers.structure.hierarchy.behaviors.selection); // triggers UI update
const isBusy = useBehavior(plugin.behaviors.state.isBusy);
const hierarchy = plugin.managers.structure.hierarchy.current;
let label: string = 'Nothing to Export';
if (hierarchy.structures.length === 1) {
label = 'Export';
} if (hierarchy.structures.length > 1) {
label = 'Export (as ZIP)';
}
const onExport = async () => {
setExporting(true);
try {
await exportHierarchy(plugin, { format: params.format });
} finally {
setExporting(false);
}
};
return <>
<ParameterControls params={Params} values={params} onChangeValues={setParams} isDisabled={isBusy || exporting} />
<Button
onClick={onExport}
style={{ marginTop: 1 }}
disabled={isBusy || hierarchy.structures.length === 0 || exporting}
commit={hierarchy.structures.length ? 'on' : 'off'}
>
{label}
</Button>
</>;
}

View File

@@ -4,7 +4,7 @@ export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
// Generated on 2022-01-15T19:52:34-08:00
// Generated on 2022-04-30T15:35:08-07:00
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
@@ -477,6 +477,8 @@ export type ClustersMembers = {
export type CoreAssembly = {
/** Get PDB entry that includes this assembly. */
readonly entry?: Maybe<CoreEntry>;
/** Get all pairwise polymer interfaces for this PDB assembly. */
readonly interfaces?: Maybe<ReadonlyArray<Maybe<CoreInterface>>>;
readonly pdbx_struct_assembly?: Maybe<PdbxStructAssembly>;
readonly pdbx_struct_assembly_auth_evidence?: Maybe<ReadonlyArray<Maybe<PdbxStructAssemblyAuthEvidence>>>;
readonly pdbx_struct_assembly_gen?: Maybe<ReadonlyArray<Maybe<PdbxStructAssemblyGen>>>;
@@ -657,6 +659,8 @@ export type CoreEntry = {
readonly em_staining?: Maybe<ReadonlyArray<Maybe<EmStaining>>>;
readonly em_vitrification?: Maybe<ReadonlyArray<Maybe<EmVitrification>>>;
readonly entry?: Maybe<Entry>;
/** Get all groups for this PDB entry. */
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
readonly exptl?: Maybe<ReadonlyArray<Maybe<Exptl>>>;
readonly exptl_crystal?: Maybe<ReadonlyArray<Maybe<ExptlCrystal>>>;
readonly exptl_crystal_grow?: Maybe<ReadonlyArray<Maybe<ExptlCrystalGrow>>>;
@@ -701,6 +705,7 @@ export type CoreEntry = {
readonly rcsb_associated_holdings?: Maybe<CurrentEntry>;
readonly rcsb_binding_affinity?: Maybe<ReadonlyArray<Maybe<RcsbBindingAffinity>>>;
readonly rcsb_entry_container_identifiers: RcsbEntryContainerIdentifiers;
readonly rcsb_entry_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbEntryGroupMembership>>>;
readonly rcsb_entry_info: RcsbEntryInfo;
readonly rcsb_external_references?: Maybe<ReadonlyArray<Maybe<RcsbExternalReferences>>>;
/**
@@ -724,6 +729,16 @@ export type CoreEntry = {
readonly symmetry?: Maybe<Symmetry>;
};
export type CoreInterface = {
readonly rcsb_id: Scalars['String'];
readonly rcsb_interface_container_identifiers: RcsbInterfaceContainerIdentifiers;
readonly rcsb_interface_info?: Maybe<RcsbInterfaceInfo>;
/** List of operations for each interface partner. */
readonly rcsb_interface_operator: ReadonlyArray<Maybe<ReadonlyArray<Maybe<ReadonlyArray<Maybe<Scalars['String']>>>>>>;
readonly rcsb_interface_partner: ReadonlyArray<Maybe<RcsbInterfacePartner>>;
readonly rcsb_latest_revision?: Maybe<RcsbLatestRevision>;
};
export type CoreNonpolymerEntity = {
/** Get PDB entry that contains this non-polymer entity. */
readonly entry?: Maybe<CoreEntry>;
@@ -839,6 +854,8 @@ export type CorePolymerEntity = {
readonly pdbx_entity_src_syn?: Maybe<ReadonlyArray<Maybe<PdbxEntitySrcSyn>>>;
/** Get all unique Pfam annotations associated with this molecular entity. */
readonly pfams?: Maybe<ReadonlyArray<Maybe<CorePfam>>>;
/** Get all groups for this PDB entity. */
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
/** Get all unique polymer instances (chains) for this molecular entity. */
readonly polymer_entity_instances?: Maybe<ReadonlyArray<Maybe<CorePolymerEntityInstance>>>;
/** Get a BIRD chemical components described in this molecular entity. */
@@ -875,6 +892,7 @@ export type CorePolymerEntity = {
readonly rcsb_polymer_entity_container_identifiers: RcsbPolymerEntityContainerIdentifiers;
readonly rcsb_polymer_entity_feature?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeature>>>;
readonly rcsb_polymer_entity_feature_summary?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityFeatureSummary>>>;
readonly rcsb_polymer_entity_group_membership?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembership>>>;
readonly rcsb_polymer_entity_keywords?: Maybe<RcsbPolymerEntityKeywords>;
readonly rcsb_polymer_entity_name_com?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameCom>>>;
readonly rcsb_polymer_entity_name_sys?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityNameSys>>>;
@@ -3186,6 +3204,88 @@ export type GeneName = {
readonly value?: Maybe<Scalars['String']>;
};
export type GroupEntry = {
/** Get provenance associated with this group. */
readonly group_provenance?: Maybe<GroupProvenance>;
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
readonly rcsb_group_info: RcsbGroupInfo;
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
/** A unique textual identifier for a group */
readonly rcsb_id: Scalars['String'];
};
export type GroupMembersAlignmentAlignedRegions = {
/** Aligned region length */
readonly length: Scalars['Int'];
/** Entity seqeunce start position */
readonly query_begin: Scalars['Int'];
/** NCBI sequence start position */
readonly target_begin: Scalars['Int'];
};
export type GroupMembersAlignmentScores = {
readonly query_coverage: Scalars['Int'];
readonly query_length: Scalars['Int'];
readonly target_coverage: Scalars['Int'];
readonly target_length: Scalars['Int'];
};
export type GroupPolymerEntity = {
/** Get provenance associated with this group. */
readonly group_provenance?: Maybe<GroupProvenance>;
readonly rcsb_group_accession_info?: Maybe<RcsbGroupAccessionInfo>;
readonly rcsb_group_container_identifiers: RcsbGroupContainerIdentifiers;
readonly rcsb_group_info: RcsbGroupInfo;
readonly rcsb_group_related?: Maybe<ReadonlyArray<Maybe<RcsbGroupRelated>>>;
readonly rcsb_group_statistics?: Maybe<RcsbGroupStatistics>;
/** A unique textual identifier for a group */
readonly rcsb_id: Scalars['String'];
readonly rcsb_polymer_entity_group_members_rankings?: Maybe<ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankings>>>;
readonly rcsb_polymer_entity_group_sequence_alignment?: Maybe<RcsbPolymerEntityGroupSequenceAlignment>;
};
export type GroupProvenance = {
readonly rcsb_group_aggregation_method?: Maybe<RcsbGroupAggregationMethod>;
readonly rcsb_group_provenance_container_identifiers?: Maybe<RcsbGroupProvenanceContainerIdentifiers>;
/** A unique textual identifier for a group provenance */
readonly rcsb_id?: Maybe<Scalars['String']>;
};
export type InterfacePartnerFeatureAdditionalProperties = {
/**
* The additional property name.
*
* Allowable values:
* TO_BE_DEFINED
*
*/
readonly name?: Maybe<Scalars['String']>;
/** The value(s) of the additional property. */
readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['ObjectScalar']>>>;
};
export type InterfacePartnerFeatureFeaturePositions = {
/** An identifier for the monomer at which this segment of the feature begins. */
readonly beg_seq_id: Scalars['Int'];
/** An identifier for the monomer at which this segment of the feature ends. */
readonly end_seq_id?: Maybe<Scalars['Int']>;
/** The value(s) of the feature over the monomer segment. */
readonly values?: Maybe<ReadonlyArray<Maybe<Scalars['Float']>>>;
};
export type MethodDetails = {
/** A description of special aspects of the clustering process */
readonly description?: Maybe<Scalars['String']>;
/** Defines the name of the description associated with the clustering process */
readonly name?: Maybe<Scalars['String']>;
/** Defines the type of the description associated with the clustering process */
readonly type?: Maybe<Scalars['String']>;
/** Defines the value associated with the clustering process */
readonly value?: Maybe<Scalars['Float']>;
};
export type PdbxAuditRevisionCategory = {
/**
* The category updated in the pdbx_audit_revision_category record.
@@ -6745,6 +6845,16 @@ export type Query = {
readonly entries?: Maybe<ReadonlyArray<Maybe<CoreEntry>>>;
/** Get PDB entry given the PDB id. */
readonly entry?: Maybe<CoreEntry>;
/** Given a group ID get a group object formed by aggregating individual structures that share a degree of similarity */
readonly entry_group?: Maybe<GroupEntry>;
/** Given a list of group IDs get a list of group objects formed by aggregating structures that share a degree of similarity */
readonly entry_groups?: Maybe<ReadonlyArray<Maybe<GroupEntry>>>;
/** Given a group provenance ID get an object that describes aggregation method used to create groups */
readonly group_provenance?: Maybe<GroupProvenance>;
/** Get a pairwise polymeric interface given the PDB ID, ASSEMBLY ID and INTERFACE ID. */
readonly interface?: Maybe<CoreInterface>;
/** Get a list of pairwise polymeric interfaces given a list of INTERFACE IDs. Here INTERFACE ID is a compound identifier that includes entry_id, assembly_id and interface_id e.g. 1XXX-1.1. */
readonly interfaces?: Maybe<ReadonlyArray<Maybe<CoreInterface>>>;
/** Get a list of PDB non-polymer entities given a list of ENTITY IDs. Here ENTITY ID is a compound identifier that includes entry_id and entity_id separated by '_', e.g. 1XXX_1. */
readonly nonpolymer_entities?: Maybe<ReadonlyArray<Maybe<CoreNonpolymerEntity>>>;
/** Get a PDB non-polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
@@ -6757,6 +6867,10 @@ export type Query = {
readonly polymer_entities?: Maybe<ReadonlyArray<Maybe<CorePolymerEntity>>>;
/** Get a PDB polymer entity, given the PDB ID and ENTITY ID. Here ENTITY ID is a '1', '2', '3', etc. */
readonly polymer_entity?: Maybe<CorePolymerEntity>;
/** Given a group ID get a group object formed by aggregating polymer entities that share a degree of similarity */
readonly polymer_entity_group?: Maybe<GroupPolymerEntity>;
/** Given a list of group IDs get a list of group objects formed by aggregating polymer entities that share a degree of similarity */
readonly polymer_entity_groups?: Maybe<ReadonlyArray<Maybe<GroupPolymerEntity>>>;
/** Get a PDB polymer entity instance (chain), given the PDB ID and ENTITY INSTANCE ID. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
readonly polymer_entity_instance?: Maybe<CorePolymerEntityInstance>;
/** Get a list of PDB polymer entity instances (chains), given the list of ENTITY INSTANCE IDs. Here ENTITY INSTANCE ID identifies structural element in the asymmetric unit, e.g. 'A', 'B', etc. */
@@ -6831,6 +6945,38 @@ export type QueryEntryArgs = {
};
/** Query root */
export type QueryEntry_GroupArgs = {
group_id: Scalars['String'];
};
/** Query root */
export type QueryEntry_GroupsArgs = {
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
};
/** Query root */
export type QueryGroup_ProvenanceArgs = {
group_provenance_id: Scalars['String'];
};
/** Query root */
export type QueryInterfaceArgs = {
assembly_id: Scalars['String'];
entry_id: Scalars['String'];
interface_id: Scalars['String'];
};
/** Query root */
export type QueryInterfacesArgs = {
interface_ids: ReadonlyArray<Scalars['String']>;
};
/** Query root */
export type QueryNonpolymer_EntitiesArgs = {
entity_ids: ReadonlyArray<Scalars['String']>;
@@ -6870,6 +7016,18 @@ export type QueryPolymer_EntityArgs = {
};
/** Query root */
export type QueryPolymer_Entity_GroupArgs = {
group_id: Scalars['String'];
};
/** Query root */
export type QueryPolymer_Entity_GroupsArgs = {
group_ids: ReadonlyArray<InputMaybe<Scalars['String']>>;
};
/** Query root */
export type QueryPolymer_Entity_InstanceArgs = {
asym_id: Scalars['String'];
@@ -6952,6 +7110,8 @@ export type RcsbAssemblyContainerIdentifiers = {
readonly assembly_id: Scalars['String'];
/** Entry identifier for the container. */
readonly entry_id: Scalars['String'];
/** List of binary interface Ids within the assembly (it points to interface id collection). */
readonly interface_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
/**
* A unique identifier for each object in this assembly container formed by
* a dash separated concatenation of entry and assembly identifiers.
@@ -7010,6 +7170,24 @@ export type RcsbAssemblyInfo = {
* This is the total count of non-polymer entity instances generated in the assembly coordinate data.
*/
readonly nonpolymer_entity_instance_count?: Maybe<Scalars['Int']>;
/** Number of heterologous (both binding sites are different) interface entities */
readonly num_heterologous_interface_entities?: Maybe<Scalars['Int']>;
/** Number of heteromeric (both partners are different polymeric entities) interface entities */
readonly num_heteromeric_interface_entities?: Maybe<Scalars['Int']>;
/** Number of homomeric (both partners are the same polymeric entity) interface entities */
readonly num_homomeric_interface_entities?: Maybe<Scalars['Int']>;
/** Number of polymer-polymer interface entities, grouping equivalent interfaces at the entity level (i.e. same entity_ids on either side, with similar but not identical binding sites) */
readonly num_interface_entities?: Maybe<Scalars['Int']>;
/** Number of geometrically equivalent (i.e. same asym_ids on either side) polymer-polymer interfaces in the assembly */
readonly num_interfaces?: Maybe<Scalars['Int']>;
/** Number of isologous (both binding sites are same, i.e. interface is symmetric) interface entities */
readonly num_isologous_interface_entities?: Maybe<Scalars['Int']>;
/** Number of nucleic acid-nucleic acid interface entities */
readonly num_na_interface_entities?: Maybe<Scalars['Int']>;
/** Number of protein-nucleic acid interface entities */
readonly num_prot_na_interface_entities?: Maybe<Scalars['Int']>;
/** Number of protein-protein interface entities */
readonly num_protein_interface_entities?: Maybe<Scalars['Int']>;
/** The assembly non-hydrogen polymer entity atomic coordinate count. */
readonly polymer_atom_count?: Maybe<Scalars['Int']>;
/**
@@ -7085,6 +7263,10 @@ export type RcsbAssemblyInfo = {
* This is the total count of solvent entity instances generated in the assembly coordinate data.
*/
readonly solvent_entity_instance_count?: Maybe<Scalars['Int']>;
/** Total buried surface area calculated as the sum of buried surface areas over all interfaces */
readonly total_assembly_buried_surface_area?: Maybe<Scalars['Float']>;
/** Total number of interfacing residues in the assembly, calculated as the sum of interfacing residues over all interfaces */
readonly total_number_interface_residues?: Maybe<Scalars['Int']>;
/**
* The number of unmodeled polymer monomers in the assembly coordinate data. This is
* the total count of monomers with unreported coordinate data for all polymer
@@ -8590,6 +8772,25 @@ export type RcsbEntryContainerIdentifiers = {
readonly water_entity_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
};
export type RcsbEntryGroupMembership = {
/**
* Method used to establish group membership
*
* Allowable values:
* matching_deposit_group_id
*
*/
readonly aggregation_method: Scalars['String'];
/**
* A unique identifier for a group of entries
*
* Examples:
* G_1001001
*
*/
readonly group_id: Scalars['String'];
};
export type RcsbEntryInfo = {
/** The number of assemblies defined for this entry including the deposited assembly. */
readonly assembly_count?: Maybe<Scalars['Int']>;
@@ -8817,6 +9018,220 @@ export type RcsbGenomicLineage = {
readonly name?: Maybe<Scalars['String']>;
};
export type RcsbGroupAccessionInfo = {
/** Identifies the version of the groups solution */
readonly version: Scalars['Int'];
};
export type RcsbGroupAggregationMethod = {
/** The details on a method used to calculate cluster solutions */
readonly method: RcsbGroupAggregationMethodMethod;
readonly similarity_criteria?: Maybe<RcsbGroupAggregationMethodSimilarityCriteria>;
/**
* Specifies the type of similarity criteria used to aggregate members into higher levels in the hierarchy
*
* Allowable values:
* sequence_identity, matching_uniprot_accession, matching_deposit_group_id
*
*/
readonly type: Scalars['String'];
};
export type RcsbGroupAggregationMethodMethod = {
/** Additional details describing the clustering process */
readonly details?: Maybe<ReadonlyArray<Maybe<MethodDetails>>>;
/**
* The name of the software or the method used to calculate cluster solutions
*
* Allowable values:
* mmseqs2, matching_reference_identity
*
*/
readonly name: Scalars['String'];
/**
* The version of the software.
*
* Examples:
* v1.0, 3.1-2, unknown
*
*/
readonly version?: Maybe<Scalars['String']>;
};
export type RcsbGroupAggregationMethodSimilarityCriteria = {
/**
* A function or similarity measure that quantifies the similarity between two members
*
* Allowable values:
* rmsd, sequence_identity
*
*/
readonly similarity_function?: Maybe<Scalars['String']>;
};
export type RcsbGroupContainerIdentifiers = {
/** A unique textual identifier for a group */
readonly group_id: Scalars['String'];
/** Member identifiers representing a group */
readonly group_member_ids: ReadonlyArray<Maybe<Scalars['String']>>;
/**
* A unique group provenance identifier
*
* Allowable values:
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
*
*/
readonly group_provenance_id: Scalars['String'];
/** Member identifiers representing a higher level in the groping hierarchy that has parent-child relationship */
readonly parent_member_ids?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
};
export type RcsbGroupInfo = {
readonly group_description?: Maybe<Scalars['String']>;
readonly group_members_count: Scalars['Int'];
/**
* Granularity of group members identifiers
*
* Allowable values:
* assembly, entry, polymer_entity, polymer_entity_instance
*
*/
readonly group_members_granularity: Scalars['String'];
readonly group_name?: Maybe<Scalars['String']>;
};
export type RcsbGroupProvenanceContainerIdentifiers = {
/**
* A unique group provenance identifier
*
* Allowable values:
* provenance_sequence_identity, provenance_matching_uniprot_accession, provenance_matching_deposit_group_id
*
*/
readonly group_provenance_id: Scalars['String'];
};
export type RcsbGroupRelated = {
/**
* A unique code assigned to a reference related the group
*
* Examples:
* P69905
*
*/
readonly resource_accession_code?: Maybe<Scalars['String']>;
/**
* Defines the type of the resource describing related references
*
* Examples:
* UniProt
*
*/
readonly resource_name?: Maybe<Scalars['String']>;
};
export type RcsbGroupStatistics = {
/** The desired lower limit for the similarity between two members that belong to the same group */
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
/** Similarity score between two most similar group members */
readonly similarity_score_max?: Maybe<Scalars['Float']>;
/** Similarity score between two least similar group members */
readonly similarity_score_min?: Maybe<Scalars['Float']>;
};
export type RcsbInterfaceContainerIdentifiers = {
/** This item references an assembly in pdbx_struct_assembly */
readonly assembly_id: Scalars['String'];
/** Entry identifier for the container. */
readonly entry_id: Scalars['String'];
/**
* Identifier for NCS-equivalent interfaces within the assembly (same entity_ids on both sides)
*
* Examples:
* 1, 2
*
*/
readonly interface_entity_id?: Maybe<Scalars['String']>;
/**
* Identifier for the geometrically equivalent (same asym_ids on either side) interfaces within the assembly
*
* Examples:
* 1, 2
*
*/
readonly interface_id: Scalars['String'];
/**
* Unique identifier for the document
*
* Examples:
* 2UZI-1.A.B?1
*
*/
readonly rcsb_id: Scalars['String'];
};
export type RcsbInterfaceInfo = {
/** Total interface buried surface area */
readonly interface_area?: Maybe<Scalars['Float']>;
/** Allowable values: homo, hetero. */
readonly interface_character?: Maybe<Scalars['String']>;
/** Number of core interface residues, defined as those that bury >90% accessible surface area with respect to the unbound state */
readonly num_core_interface_residues?: Maybe<Scalars['Int']>;
/** Number of interface residues, defined as those with burial fraction > 0 */
readonly num_interface_residues?: Maybe<Scalars['Int']>;
/** Allowable values: Nucleic acid (only), Protein (only), Protein/NA. */
readonly polymer_composition?: Maybe<Scalars['String']>;
/** The Jaccard score (intersection over union) of interface contacts in homomeric interfaces, comparing contact sets left-right vs right-left. High values indicate isologous (symmetric) interfaces, with value=1 if perfectly symmetric (e.g. crystallographic symmetry) */
readonly self_jaccard_contact_score?: Maybe<Scalars['Float']>;
};
export type RcsbInterfacePartner = {
readonly interface_partner_feature?: Maybe<ReadonlyArray<Maybe<RcsbInterfacePartnerInterfacePartnerFeature>>>;
readonly interface_partner_identifier?: Maybe<RcsbInterfacePartnerInterfacePartnerIdentifier>;
};
export type RcsbInterfacePartnerInterfacePartnerFeature = {
readonly additional_properties?: Maybe<ReadonlyArray<Maybe<InterfacePartnerFeatureAdditionalProperties>>>;
/**
* Identifies the version of the feature assignment.
*
* Examples:
* V4_0_2
*
*/
readonly assignment_version?: Maybe<Scalars['String']>;
/** A description for the feature. */
readonly description?: Maybe<Scalars['String']>;
/** An identifier for the feature. */
readonly feature_id?: Maybe<Scalars['String']>;
readonly feature_positions?: Maybe<ReadonlyArray<Maybe<InterfacePartnerFeatureFeaturePositions>>>;
/** A name for the feature. */
readonly name?: Maybe<Scalars['String']>;
/**
* Code identifying the individual, organization or program that assigned the feature.
*
* Examples:
* NACCESS
*
*/
readonly provenance_source?: Maybe<Scalars['String']>;
/**
* A type or category of the feature.
*
* Allowable values:
* ASA_UNBOUND, ASA_BOUND
*
*/
readonly type?: Maybe<Scalars['String']>;
};
export type RcsbInterfacePartnerInterfacePartnerIdentifier = {
/** Instance identifier for this container. */
readonly asym_id: Scalars['String'];
/** Polymer entity identifier for the container. */
readonly entity_id: Scalars['String'];
};
export type RcsbLatestRevision = {
/** The major version number of the latest revision. */
readonly major_revision?: Maybe<Scalars['Int']>;
@@ -9733,6 +10148,7 @@ export type RcsbPolymerEntity = {
*
*/
readonly rcsb_multiple_source_flag?: Maybe<Scalars['String']>;
readonly rcsb_polymer_name_combined?: Maybe<RcsbPolymerEntityRcsbPolymerNameCombined>;
/**
* The number of biological sources for the polymer entity. Multiple source contributions
* may come from the same organism (taxonomy).
@@ -10059,6 +10475,69 @@ export type RcsbPolymerEntityFeatureSummary = {
readonly type?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityGroupMembersRankings = {
readonly group_members: ReadonlyArray<Maybe<RcsbPolymerEntityGroupMembersRankingsGroupMembers>>;
/**
* Defines ranking option applicable to group members
*
* Allowable values:
* coverage
*
*/
readonly ranking_criteria_type: Scalars['String'];
};
export type RcsbPolymerEntityGroupMembersRankingsGroupMembers = {
readonly member_id: Scalars['String'];
/** Quantifies the criteria used for ranking */
readonly original_score?: Maybe<Scalars['Float']>;
/** Reflects a relationship between group members such that, for any two members the first is ranked higher (smaller rank value) than the second */
readonly rank: Scalars['Int'];
};
export type RcsbPolymerEntityGroupMembership = {
/**
* Method used to establish group membership
*
* Allowable values:
* sequence_identity, matching_uniprot_accession
*
*/
readonly aggregation_method: Scalars['String'];
/**
* A unique identifier for a group of entities
*
* Examples:
* 1_100, P00003
*
*/
readonly group_id: Scalars['String'];
/** Degree of similarity expressed as a floating-point number */
readonly similarity_cutoff?: Maybe<Scalars['Float']>;
};
export type RcsbPolymerEntityGroupSequenceAlignment = {
/** Abstract reference where group members can be aligned to generate a MSA */
readonly abstract_reference: RcsbPolymerEntityGroupSequenceAlignmentAbstractReference;
/** List of alignments with core_entity canonical sequences */
readonly group_members_alignment: ReadonlyArray<Maybe<RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment>>;
};
export type RcsbPolymerEntityGroupSequenceAlignmentAbstractReference = {
/** Abstract reference length */
readonly length: Scalars['Int'];
/** Sequence that represents the abstract reference */
readonly sequence?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityGroupSequenceAlignmentGroupMembersAlignment = {
/** Aligned region */
readonly aligned_regions: ReadonlyArray<Maybe<GroupMembersAlignmentAlignedRegions>>;
readonly member_id?: Maybe<Scalars['String']>;
/** Alignment scores */
readonly scores: GroupMembersAlignmentScores;
};
export type RcsbPolymerEntityInstanceContainerIdentifiers = {
/** Instance identifier for this container. */
readonly asym_id: Scalars['String'];
@@ -10177,6 +10656,13 @@ export type RcsbPolymerEntityRcsbMacromolecularNamesCombined = {
readonly provenance_source?: Maybe<Scalars['String']>;
};
export type RcsbPolymerEntityRcsbPolymerNameCombined = {
/** Protein name annotated by the UniProtKB or macromolecular name assigned by the PDB */
readonly names?: Maybe<ReadonlyArray<Maybe<Scalars['String']>>>;
/** Allowable values: PDB Preferred Name, PDB Description, UniProt Name. */
readonly provenance_source?: Maybe<Scalars['String']>;
};
export type RcsbPolymerInstanceAnnotation = {
/** An identifier for the annotation. */
readonly annotation_id?: Maybe<Scalars['String']>;
@@ -10263,7 +10749,7 @@ export type RcsbPolymerInstanceFeature = {
* A type or category of the feature.
*
* Allowable values:
* ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ
* ANGLE_OUTLIER, BINDING_SITE, BOND_OUTLIER, C-MANNOSYLATION_SITE, CATH, CIS-PEPTIDE, ECOD, HELIX_P, MEMBRANE_SEGMENT, MOGUL_ANGLE_OUTLIER, MOGUL_BOND_OUTLIER, N-GLYCOSYLATION_SITE, O-GLYCOSYLATION_SITE, RAMACHANDRAN_OUTLIER, ROTAMER_OUTLIER, RSCC_OUTLIER, RSRZ_OUTLIER, S-GLYCOSYLATION_SITE, SABDAB_ANTIBODY_HEAVY_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_SUBCLASS, SABDAB_ANTIBODY_LIGHT_CHAIN_TYPE, SCOP, SCOP2B_SUPERFAMILY, SCOP2_FAMILY, SCOP2_SUPERFAMILY, SHEET, STEREO_OUTLIER, UNASSIGNED_SEC_STRUCT, UNOBSERVED_ATOM_XYZ, UNOBSERVED_RESIDUE_XYZ, ZERO_OCCUPANCY_ATOM_XYZ, ZERO_OCCUPANCY_RESIDUE_XYZ, ASA
*
*/
readonly type?: Maybe<Scalars['String']>;
@@ -13145,4 +13631,4 @@ export type AssemblySymmetryQueryVariables = Exact<{
}>;
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null | undefined>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null | undefined, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null | undefined> | null | undefined } | null | undefined> } | null | undefined>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null | undefined, readonly start: ReadonlyArray<number | null | undefined>, readonly end: ReadonlyArray<number | null | undefined> } | null | undefined> | null | undefined } | null | undefined> | null | undefined } | null | undefined };
export type AssemblySymmetryQuery = { readonly assembly?: { readonly rcsb_struct_symmetry?: ReadonlyArray<{ readonly kind: string, readonly oligomeric_state: string, readonly stoichiometry: ReadonlyArray<string | null>, readonly symbol: string, readonly type: string, readonly clusters: ReadonlyArray<{ readonly avg_rmsd?: number | null, readonly members: ReadonlyArray<{ readonly asym_id: string, readonly pdbx_struct_oper_list_ids?: ReadonlyArray<string | null> | null } | null> } | null>, readonly rotation_axes?: ReadonlyArray<{ readonly order?: number | null, readonly start: ReadonlyArray<number | null>, readonly end: ReadonlyArray<number | null> } | null> | null } | null> | null } | null };

View File

@@ -55,7 +55,16 @@ function createIntraUnitClashCylinderMesh(ctx: VisualContext, unit: Unit, struct
radius: (edgeIndex: number) => magnitude[edgeIndex] * sizeFactor,
};
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
const { mesh: m, boundingSphere } = createLinkCylinderMesh(ctx, builderProps, props, mesh);
if (boundingSphere) {
m.setBoundingSphere(boundingSphere);
} else if (m.triangleCount > 0) {
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
m.setBoundingSphere(sphere);
}
return m;
}
export const IntraUnitClashParams = {
@@ -169,7 +178,16 @@ function createInterUnitClashCylinderMesh(ctx: VisualContext, structure: Structu
radius: (edgeIndex: number) => edges[edgeIndex].props.magnitude * sizeFactor
};
return createLinkCylinderMesh(ctx, builderProps, props, mesh);
const { mesh: m, boundingSphere } = createLinkCylinderMesh(ctx, builderProps, props, mesh);
if (boundingSphere) {
m.setBoundingSphere(boundingSphere);
} else {
const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * sizeFactor);
m.setBoundingSphere(sphere);
}
return m;
}
export const InterUnitClashParams = {

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginBehavior } from '../../mol-plugin/behavior/behavior';
import { ZenodoImportUI } from './ui';
export const ZenodoImport = PluginBehavior.create<{ }>({
name: 'extension-zenodo-import',
category: 'misc',
display: {
name: 'Zenodo Export'
},
ctor: class extends PluginBehavior.Handler<{ }> {
register(): void {
this.ctx.customImportControls.set('zenodo-import', ZenodoImportUI as any);
}
update() {
return false;
}
unregister() {
this.ctx.customImportControls.delete('zenodo-import');
}
},
params: () => ({ })
});

View File

@@ -0,0 +1,302 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { DownloadFile } from '../../mol-plugin-state/actions/file';
import { DownloadStructure, LoadTrajectory } from '../../mol-plugin-state/actions/structure';
import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
import { CoordinatesFormatCategory } from '../../mol-plugin-state/formats/coordinates';
import { TopologyFormatCategory } from '../../mol-plugin-state/formats/topology';
import { TrajectoryFormatCategory } from '../../mol-plugin-state/formats/trajectory';
import { VolumeFormatCategory } from '../../mol-plugin-state/formats/volume';
import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
import { Button } from '../../mol-plugin-ui/controls/common';
import { OpenInBrowserSvg } from '../../mol-plugin-ui/controls/icons';
import { ParameterControls } from '../../mol-plugin-ui/controls/parameters';
import { PluginContext } from '../../mol-plugin/context';
import { formatBytes } from '../../mol-util';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
type ZenodoFile = {
bucket: string
checksum: string
key: string
links: {
[key: string]: string
self: string
}
size: number
type: string
}
type ZenodoRecord = {
id: number
conceptdoi: string
conceptrecid: string
created: string
doi: string
files: ZenodoFile[]
revision: number
updated: string
metadata: {
title: string
}
}
interface State {
busy?: boolean
recordValues: PD.Values<typeof ZenodoImportParams>
importValues?: PD.Values<ImportParams>
importParams?: ImportParams
record?: ZenodoRecord
files?: ZenodoFile[]
}
const ZenodoImportParams = {
record: PD.Text('', { description: 'Zenodo ID.' })
};
function createImportParams(files: ZenodoFile[], plugin: PluginContext) {
const modelOpts: [string, string][] = [];
const topologyOpts: [string, string][] = [];
const coordinatesOpts: [string, string][] = [];
const volumeOpts: [string, string][] = [];
const compressedOpts: [string, string][] = [];
const structureExts = new Map<string, { format: string, isBinary: boolean }>();
const coordinatesExts = new Map<string, { format: string, isBinary: boolean }>();
const topologyExts = new Map<string, { format: string, isBinary: boolean }>();
const volumeExts = new Map<string, { format: string, isBinary: boolean }>();
for (const { provider: { category, binaryExtensions, stringExtensions }, name } of plugin.dataFormats.list) {
if (category === TrajectoryFormatCategory) {
if (binaryExtensions) for (const e of binaryExtensions) structureExts.set(e, { format: name, isBinary: true });
if (stringExtensions) for (const e of stringExtensions) structureExts.set(e, { format: name, isBinary: false });
} else if (category === VolumeFormatCategory) {
if (binaryExtensions) for (const e of binaryExtensions) volumeExts.set(e, { format: name, isBinary: true });
if (stringExtensions) for (const e of stringExtensions) volumeExts.set(e, { format: name, isBinary: false });
} else if (category === CoordinatesFormatCategory) {
if (binaryExtensions) for (const e of binaryExtensions) coordinatesExts.set(e, { format: name, isBinary: true });
if (stringExtensions) for (const e of stringExtensions) coordinatesExts.set(e, { format: name, isBinary: false });
} else if (category === TopologyFormatCategory) {
if (binaryExtensions) for (const e of binaryExtensions) topologyExts.set(e, { format: name, isBinary: true });
if (stringExtensions) for (const e of stringExtensions) topologyExts.set(e, { format: name, isBinary: false });
}
}
for (const file of files) {
const label = `${file.key} (${formatBytes(file.size)})`;
if (structureExts.has(file.type)) {
const { format, isBinary } = structureExts.get(file.type)!;
modelOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
topologyOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
} else if (volumeExts.has(file.type)) {
const { format, isBinary } = volumeExts.get(file.type)!;
volumeOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
} else if (topologyExts.has(file.type)) {
const { format, isBinary } = topologyExts.get(file.type)!;
topologyOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
} else if (coordinatesExts.has(file.type)) {
const { format, isBinary } = coordinatesExts.get(file.type)!;
coordinatesOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
} else if (file.type === 'zip') {
compressedOpts.push([`${file.links.self}|${file.type}|true`, label]);
}
}
const params: PD.Params = {};
let defaultType = '';
if (modelOpts.length) {
defaultType = 'structure';
params.structure = PD.Select(modelOpts[0][0], modelOpts);
}
if (topologyOpts.length && coordinatesOpts.length) {
if (!defaultType) defaultType = 'trajectory';
params.trajectory = PD.Group({
topology: PD.Select(topologyOpts[0][0], topologyOpts),
coordinates: PD.Select(coordinatesOpts[0][0], coordinatesOpts),
}, { isFlat: true });
}
if (volumeOpts.length) {
if (!defaultType) defaultType = 'volume';
params.volume = PD.Select(volumeOpts[0][0], volumeOpts);
}
if (compressedOpts.length) {
if (!defaultType) defaultType = 'compressed';
params.compressed = PD.Select(compressedOpts[0][0], compressedOpts);
}
return {
type: PD.MappedStatic(defaultType, Object.keys(params).length ? params : { '': PD.EmptyGroup() })
};
}
type ImportParams = ReturnType<typeof createImportParams>
export class ZenodoImportUI extends CollapsableControls<{}, State> {
protected defaultState(): State & CollapsableState {
return {
header: 'Zenodo Import',
isCollapsed: true,
brand: { accent: 'cyan', svg: OpenInBrowserSvg },
recordValues: PD.getDefaultValues(ZenodoImportParams),
importValues: undefined,
importParams: undefined,
record: undefined,
files: undefined,
};
}
private recordParamsOnChange = (values: any) => {
this.setState({ recordValues: values });
};
private importParamsOnChange = (values: any) => {
this.setState({ importValues: values });
};
private loadRecord = async () => {
try {
this.setState({ busy: true });
const record: ZenodoRecord = await this.plugin.runTask(this.plugin.fetch({ url: `https://zenodo.org/api/records/${this.state.recordValues.record}`, type: 'json' }));
const importParams = createImportParams(record.files, this.plugin);
this.setState({
record,
files: record.files,
busy: false,
importValues: PD.getDefaultValues(importParams),
importParams
});
} catch (e) {
console.error(e);
this.plugin.log.error(`Failed to load Zenodo record '${this.state.recordValues.record}'`);
this.setState({ busy: false });
}
};
private loadFile = async (values: PD.Values<ImportParams>) => {
try {
this.setState({ busy: true });
const t = values.type;
if (t.name === 'structure') {
const defaultParams = DownloadStructure.createDefaultParams(this.plugin.state.data.root.obj!, this.plugin);
const [url, format, isBinary] = t.params.split('|');
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
url,
format: format as any,
isBinary: isBinary === 'true',
options: defaultParams.source.params.options,
}
}
}));
} else if (t.name === 'trajectory') {
const [topologyUrl, topologyFormat, topologyIsBinary] = t.params.topology.split('|');
const [coordinatesUrl, coordinatesFormat, coordinatesIsBinary] = t.params.coordinates.split('|');
await this.plugin.runTask(this.plugin.state.data.applyAction(LoadTrajectory, {
source: {
name: 'url',
params: {
model: {
url: topologyUrl,
format: topologyFormat as any,
isBinary: topologyIsBinary === 'true',
},
coordinates: {
url: coordinatesUrl,
format: coordinatesFormat as any,
isBinary: coordinatesIsBinary === 'true',
},
}
}
}));
} else if (t.name === 'volume') {
const [url, format, isBinary] = t.params.split('|');
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadDensity, {
source: {
name: 'url',
params: {
url,
format: format as any,
isBinary: isBinary === 'true',
}
}
}));
} else if (t.name === 'compressed') {
const [url, format, isBinary] = t.params.split('|');
await this.plugin.runTask(this.plugin.state.data.applyAction(DownloadFile, {
url,
format: format as any,
isBinary: isBinary === 'true',
visuals: true
}));
}
} catch (e) {
console.error(e);
this.plugin.log.error(`Failed to load Zenodo file`);
} finally {
this.setState({ busy: false });
}
};
private clearRecord = () => {
this.setState({
importValues: undefined,
importParams: undefined,
record: undefined,
files: undefined
});
};
private renderLoadRecord() {
return <div style={{ marginBottom: 10 }}>
<ParameterControls params={ZenodoImportParams} values={this.state.recordValues} onChangeValues={this.recordParamsOnChange} isDisabled={this.state.busy} />
<Button onClick={this.loadRecord} style={{ marginTop: 1 }} disabled={this.state.busy || !this.state.recordValues.record}>
Load Record
</Button>
</div>;
}
private renderRecordInfo(record: ZenodoRecord) {
return <div style={{ marginBottom: 10 }}>
<div className='msp-help-text'>
<div>Record {`${record.id}`}: <i>{`${record.metadata.title}`}</i></div>
</div>
<Button onClick={this.clearRecord} style={{ marginTop: 1 }} disabled={this.state.busy}>
Clear
</Button>
</div>;
}
private renderImportFile(params: ImportParams, values: PD.Values<ImportParams>) {
return values.type.name ? <div style={{ marginBottom: 10 }}>
<ParameterControls params={params} values={this.state.importValues} onChangeValues={this.importParamsOnChange} isDisabled={this.state.busy} />
<Button onClick={() => this.loadFile(values)} style={{ marginTop: 1 }} disabled={this.state.busy}>
Import File
</Button>
</div> : <div className='msp-help-text' style={{ marginBottom: 10 }}>
<div>No supported files</div>
</div>;
}
protected renderControls(): JSX.Element | null {
return <>
{!this.state.record ? this.renderLoadRecord() : null}
{this.state.record ? this.renderRecordInfo(this.state.record) : null}
{this.state.importParams && this.state.importValues ? this.renderImportFile(this.state.importParams, this.state.importValues) : null}
</>;
}
}

View File

@@ -10,6 +10,7 @@ import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { CameraTransitionManager } from './camera/transition';
import { BehaviorSubject } from 'rxjs';
import { Scene } from '../mol-gl/scene';
import { assertUnreachable } from '../mol-util/type-helpers';
export { ICamera, Camera };
@@ -84,7 +85,7 @@ class Camera implements ICamera {
switch (this.state.mode) {
case 'orthographic': updateOrtho(this); break;
case 'perspective': updatePers(this); break;
default: throw new Error('unknown camera mode');
default: assertUnreachable(this.state.mode);
}
const changed = !Mat4.areEqual(this.projection, this.prevProjection, EPSILON) || !Mat4.areEqual(this.view, this.prevView, EPSILON);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 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>
@@ -236,7 +236,7 @@ interface Canvas3D {
/** 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, noDraw?: boolean): void
mark(loci: Representation.Loci, action: MarkerAction): void
getLoci(pickingId: PickingId | undefined): Representation.Loci
notifyDidDraw: boolean,
@@ -345,34 +345,43 @@ namespace Canvas3D {
return { loci, repr };
}
function mark(reprLoci: Representation.Loci, action: MarkerAction, noDraw = false) {
let markBuffer: [reprLoci: Representation.Loci, action: MarkerAction][] = [];
function mark(reprLoci: Representation.Loci, action: MarkerAction) {
// NOTE: might try to optimize a case with opposite actions for the
// same loci. Tho this might end up being more expensive (and error prone)
// then just applying everything "naively".
markBuffer.push([reprLoci, action]);
}
function resolveMarking() {
let changed = false;
for (const [r, l] of markBuffer) {
changed = applyMark(r, l) || changed;
}
markBuffer = [];
if (changed) {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
}
return changed;
}
function applyMark(reprLoci: Representation.Loci, action: MarkerAction) {
const { repr, loci } = reprLoci;
let changed = false;
if (repr) {
changed = repr.mark(loci, action);
changed = repr.mark(loci, action) || changed;
} else {
changed = helper.handle.mark(loci, action);
changed = helper.camera.mark(loci, action) || changed;
reprRenderObjects.forEach((_, _repr) => { changed = _repr.mark(loci, action) || changed; });
}
if (changed) {
if (noDraw) {
// Even with `noDraw` make sure changes will be rendered.
// Note that with this calling mark (with or without `noDraw`) multiple times
// during a JS event loop iteration will only result in a single render call.
forceNextRender = true;
} else {
scene.update(void 0, true);
helper.handle.scene.update(void 0, true);
helper.camera.scene.update(void 0, true);
const prevPickDirty = pickHelper.dirty;
draw({ force: true, allowMulti: true });
pickHelper.dirty = prevPickDirty; // marking does not change picking buffers
}
}
changed = helper.handle.mark(loci, action) || changed;
changed = helper.camera.mark(loci, action) || changed;
return changed;
}
function render(force: boolean, allowMulti: boolean) {
function render(force: boolean) {
if (webgl.isContextLost) return false;
let resized = false;
@@ -386,6 +395,8 @@ namespace Canvas3D {
y > gl.drawingBufferHeight || y + height < 0
) return false;
const markingUpdated = resolveMarking();
let didRender = false;
controls.update(currentTime);
const cameraChanged = camera.update();
@@ -393,9 +404,9 @@ namespace Canvas3D {
const shouldRender = force || cameraChanged || resized || forceNextRender;
forceNextRender = false;
const multiSampleChanged = multiSampleHelper.update(shouldRender, p.multiSample);
const multiSampleChanged = multiSampleHelper.update(markingUpdated || shouldRender, p.multiSample);
if (shouldRender || multiSampleChanged) {
if (shouldRender || multiSampleChanged || markingUpdated) {
let cam: Camera | StereoCamera = camera;
if (p.camera.stereo.name === 'on') {
stereoCamera.update();
@@ -404,12 +415,13 @@ namespace Canvas3D {
const ctx = { renderer, camera: cam, scene, helper };
if (MultiSamplePass.isEnabled(p.multiSample)) {
const forceOn = !cameraChanged && allowMulti && !controls.isAnimating;
const forceOn = !cameraChanged && markingUpdated && !controls.isAnimating;
multiSampleHelper.render(ctx, p, true, forceOn);
} else {
passes.draw.render(ctx, p, true);
}
pickHelper.dirty = true;
// if only marking has updated, do not set the flag to dirty
pickHelper.dirty = pickHelper.dirty || shouldRender;
didRender = true;
}
@@ -421,9 +433,9 @@ namespace Canvas3D {
let currentTime = 0;
let drawPaused = false;
function draw(options?: { force?: boolean, allowMulti?: boolean }) {
function draw(options?: { force?: boolean }) {
if (drawPaused) return;
if (render(!!options?.force, !!options?.allowMulti) && notifyDidDraw) {
if (render(!!options?.force) && notifyDidDraw) {
didDraw.next(now() - startTime as now.Timestamp);
}
}
@@ -831,6 +843,8 @@ namespace Canvas3D {
dispose: () => {
contextRestoredSub.unsubscribe();
markBuffer = [];
scene.clear();
helper.debug.clear();
controls.dispose();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -17,7 +17,7 @@ import { WebGLContext } from '../../mol-gl/webgl/context';
import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
import { Sphere3D } from '../../mol-math/geometry';
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
import { DataLoci, EmptyLoci, isEveryLoci, Loci } from '../../mol-model/loci';
import { Shape } from '../../mol-model/shape';
import { Visual } from '../../mol-repr/visual';
import { ColorNames } from '../../mol-util/color/names';
@@ -113,8 +113,10 @@ export class CameraHelper {
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
if (!isCameraAxesLoci(loci)) return false;
if (loci.data !== this) return false;
if (!isEveryLoci(loci)) {
if (!isCameraAxesLoci(loci)) return false;
if (loci.data !== this) return false;
}
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -20,7 +20,7 @@ import produce from 'immer';
import { Shape } from '../../mol-model/shape';
import { PickingId } from '../../mol-geo/geometry/picking';
import { Camera } from '../camera';
import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
import { DataLoci, EmptyLoci, isEveryLoci, Loci } from '../../mol-model/loci';
import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
import { Visual } from '../../mol-repr/visual';
import { Interval } from '../../mol-data/int';
@@ -121,8 +121,10 @@ export class HandleHelper {
mark(loci: Loci, action: MarkerAction) {
if (!MarkerActions.is(MarkerActions.Highlighting, action)) return false;
if (!isHandleLoci(loci)) return false;
if (loci.data !== this) return false;
if (!isEveryLoci(loci)) {
if (!isHandleLoci(loci)) return false;
if (loci.data !== this) return false;
}
return Visual.mark(this.renderObject, loci, action, this.eachGroup);
}

View File

@@ -71,7 +71,7 @@ export class Canvas3dInteractionHelper {
const xyChanged = this.startX !== this.endX || this.startY !== this.endY;
if (e === InputEvent.Drag) {
if (xyChanged && !Representation.Loci.isEmpty(this.prevLoci)) {
if (xyChanged && !this.outsideViewport(this.startX, this.startY)) {
this.events.drag.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers, pageStart: Vec2.create(this.startX, this.startY), pageEnd: Vec2.create(this.endX, this.endY) });
this.startX = this.endX;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -11,48 +11,16 @@ import { Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
import { Texture } from '../../mol-gl/webgl/texture';
import { Camera, ICamera } from '../camera';
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../mol-gl/renderable/schema';
import { ComputeRenderable, createComputeRenderable } from '../../mol-gl/renderable';
import { ShaderCode } from '../../mol-gl/shader-code';
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
import { ValueCell } from '../../mol-util';
import { Vec2 } from '../../mol-math/linear-algebra';
import { Helper } from '../helper/helper';
import { quad_vert } from '../../mol-gl/shader/quad.vert';
import { depthMerge_frag } from '../../mol-gl/shader/depth-merge.frag';
import { StereoCamera } from '../camera/stereo';
import { WboitPass } from './wboit';
import { AntialiasingPass, PostprocessingPass, PostprocessingProps } from './postprocessing';
import { MarkingPass, MarkingProps } from './marking';
import { CopyRenderable, createCopyRenderable } from '../../mol-gl/compute/util';
const DepthMergeSchema = {
...QuadSchema,
tDepthPrimitives: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
tDepthVolumes: TextureSpec('texture', 'depth', 'ushort', 'nearest'),
uTexSize: UniformSpec('v2'),
dPackedDepth: DefineSpec('boolean'),
};
const DepthMergeShaderCode = ShaderCode('depth-merge', quad_vert, depthMerge_frag);
type DepthMergeRenderable = ComputeRenderable<Values<typeof DepthMergeSchema>>
function getDepthMergeRenderable(ctx: WebGLContext, depthTexturePrimitives: Texture, depthTextureVolumes: Texture, packedDepth: boolean): DepthMergeRenderable {
const values: Values<typeof DepthMergeSchema> = {
...QuadValues,
tDepthPrimitives: ValueCell.create(depthTexturePrimitives),
tDepthVolumes: ValueCell.create(depthTextureVolumes),
uTexSize: ValueCell.create(Vec2.create(depthTexturePrimitives.getWidth(), depthTexturePrimitives.getHeight())),
dPackedDepth: ValueCell.create(packedDepth),
};
const schema = { ...DepthMergeSchema };
const renderItem = createComputeRenderItem(ctx, 'triangles', DepthMergeShaderCode, schema, values);
return createComputeRenderable(renderItem, values);
}
type Props = {
postprocessing: PostprocessingProps
marking: MarkingProps
@@ -70,16 +38,13 @@ export class DrawPass {
private readonly drawTarget: RenderTarget;
readonly colorTarget: RenderTarget;
readonly depthTexture: Texture;
readonly depthTexturePrimitives: Texture;
readonly depthTextureTransparent: Texture;
readonly depthTextureOpaque: Texture;
readonly packedDepth: boolean;
private depthTarget: RenderTarget;
private depthTargetPrimitives: RenderTarget | null;
private depthTargetVolumes: RenderTarget | null;
private depthTextureVolumes: Texture;
private depthMerge: DepthMergeRenderable;
private depthTargetTransparent: RenderTarget;
private depthTargetOpaque: RenderTarget | null;
private copyFboTarget: CopyRenderable;
private copyFboPostprocessing: CopyRenderable;
@@ -101,19 +66,15 @@ export class DrawPass {
this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
this.packedDepth = !extensions.depthTexture;
this.depthTarget = webgl.createRenderTarget(width, height);
this.depthTexture = this.depthTarget.texture;
this.depthTargetTransparent = webgl.createRenderTarget(width, height);
this.depthTextureTransparent = this.depthTargetTransparent.texture;
this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTargetOpaque = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
this.depthTextureOpaque = this.depthTargetOpaque ? this.depthTargetOpaque.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
if (!this.packedDepth) {
this.depthTexturePrimitives.define(width, height);
this.depthTextureVolumes.define(width, height);
this.depthTextureOpaque.define(width, height);
}
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);
@@ -134,22 +95,14 @@ export class DrawPass {
if (width !== w || height !== h) {
this.colorTarget.setSize(width, height);
this.depthTarget.setSize(width, height);
this.depthTargetTransparent.setSize(width, height);
if (this.depthTargetPrimitives) {
this.depthTargetPrimitives.setSize(width, height);
if (this.depthTargetOpaque) {
this.depthTargetOpaque.setSize(width, height);
} else {
this.depthTexturePrimitives.define(width, height);
this.depthTextureOpaque.define(width, height);
}
if (this.depthTargetVolumes) {
this.depthTargetVolumes.setSize(width, height);
} else {
this.depthTextureVolumes.define(width, height);
}
ValueCell.update(this.depthMerge.values.uTexSize, Vec2.set(this.depthMerge.values.uTexSize.ref.value, width, height));
ValueCell.update(this.copyFboTarget.values.uTexSize, Vec2.set(this.copyFboTarget.values.uTexSize.ref.value, width, height));
ValueCell.update(this.copyFboPostprocessing.values.uTexSize, Vec2.set(this.copyFboPostprocessing.values.uTexSize.ref.value, width, height));
@@ -163,20 +116,6 @@ export class DrawPass {
}
}
private _depthMerge() {
const { state, gl } = this.webgl;
this.depthMerge.update();
this.depthTarget.bind();
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.CULL_FACE);
state.depthMask(false);
state.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
this.depthMerge.render();
}
private _renderWboit(renderer: Renderer, camera: ICamera, scene: Scene, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
if (!this.wboit?.supported) throw new Error('expected wboit to be supported');
@@ -184,38 +123,37 @@ export class DrawPass {
renderer.clear(true);
// render opaque primitives
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
renderer.clearDepth();
renderer.renderWboitOpaque(scene.primitives, camera, null);
// render opaque volumes
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
renderer.clearDepth();
renderer.renderWboitOpaque(scene.volumes, camera, this.depthTexturePrimitives);
// merge depth of opaque primitives and volumes
this._depthMerge();
if (PostprocessingPass.isEnabled(postprocessingProps)) {
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
this.depthTargetTransparent.bind();
renderer.clearDepth(true);
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
}
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
}
// render transparent primitives and volumes
this.wboit.bind();
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexture);
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexture);
if (scene.opacityAverage < 1 || scene.volumes.renderables.length > 0) {
this.wboit.bind();
renderer.renderWboitTransparent(scene.primitives, camera, this.depthTextureOpaque);
renderer.renderWboitTransparent(scene.volumes, camera, this.depthTextureOpaque);
// evaluate wboit
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
this.postprocessing.target.bind();
} else {
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.colorTarget.bind();
// evaluate wboit
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.postprocessing.target.bind();
} else {
this.colorTarget.bind();
}
this.wboit.render();
}
this.wboit.render();
}
private _renderBlended(renderer: Renderer, camera: ICamera, scene: Scene, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
@@ -224,7 +162,7 @@ export class DrawPass {
} else {
this.colorTarget.bind();
if (!this.packedDepth) {
this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
}
}
@@ -234,44 +172,38 @@ export class DrawPass {
if (!toDrawingBuffer) {
// do a depth pass if not rendering to drawing buffer and
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (this.depthTargetPrimitives) {
this.depthTargetPrimitives.bind();
renderer.clear(false);
// TODO: this should only render opaque
renderer.renderDepth(scene.primitives, camera, null);
if (this.depthTargetOpaque) {
this.depthTargetOpaque.bind();
renderer.clearDepth(true);
renderer.renderDepthOpaque(scene.primitives, camera, null);
this.colorTarget.bind();
}
// do direct-volume rendering
if (!this.packedDepth) {
this.depthTextureVolumes.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
renderer.clearDepth(); // from previous frame
}
renderer.renderBlendedVolumeOpaque(scene.volumes, camera, this.depthTexturePrimitives);
// do volume depth pass if extensions.depthTexture is unsupported (i.e. depthTarget is set)
if (this.depthTargetVolumes) {
this.depthTargetVolumes.bind();
renderer.clear(false);
renderer.renderDepth(scene.volumes, camera, this.depthTexturePrimitives);
this.colorTarget.bind();
}
// merge depths from primitive and volume rendering
this._depthMerge();
this.colorTarget.bind();
if (PostprocessingPass.isEnabled(postprocessingProps)) {
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
}
renderer.renderBlendedVolumeTransparent(scene.volumes, camera, this.depthTexturePrimitives);
if (!this.packedDepth) {
this.depthTextureOpaque.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer);
}
const target = PostprocessingPass.isEnabled(postprocessingProps)
? this.postprocessing.target : this.colorTarget;
if (!this.packedDepth) {
this.depthTexturePrimitives.attachFramebuffer(target.framebuffer, 'depth');
if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
this.depthTargetTransparent.bind();
renderer.clearDepth(true);
if (scene.opacityAverage < 1) {
renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
}
}
this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
if (!this.packedDepth) {
this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
} else {
this.colorTarget.depthRenderbuffer?.attachFramebuffer(this.postprocessing.target.framebuffer);
}
}
target.bind();
renderer.renderBlendedVolume(scene.volumes, camera, this.depthTextureOpaque);
}
renderer.renderBlendedTransparent(scene.primitives, camera, null);
@@ -307,19 +239,21 @@ export class DrawPass {
}
if (markingEnabled) {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest) {
this.marking.depthTarget.bind();
renderer.clear(false);
renderer.renderMarkingDepth(scene.primitives, camera, null);
if (scene.markerAverage > 0) {
const markingDepthTest = props.marking.ghostEdgeStrength < 1;
if (markingDepthTest && scene.markerAverage !== 1) {
this.marking.depthTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingDepth(scene.primitives, camera, null);
}
this.marking.maskTarget.bind();
renderer.clear(false, true);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(props.marking);
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
}
this.marking.maskTarget.bind();
renderer.clear(false);
renderer.renderMarkingMask(scene.primitives, camera, markingDepthTest ? this.marking.depthTarget.texture : null);
this.marking.update(props.marking);
this.marking.render(camera.viewport, postprocessingEnabled ? this.postprocessing.target : this.colorTarget);
}
if (helper.debug.isEnabled) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -157,6 +157,14 @@ export class MultiSamplePass {
ValueCell.update(compose.values.uWeight, sampleWeight);
// render scene
if (i === 0) {
drawPass.postprocessing.setOcclusionOffset(0, 0);
} else {
drawPass.postprocessing.setOcclusionOffset(
offset[0] / width,
offset[1] / height
);
}
drawPass.render(ctx, props, false);
// compose rendered scene with compose target
@@ -175,6 +183,8 @@ export class MultiSamplePass {
compose.render();
}
drawPass.postprocessing.setOcclusionOffset(0, 0);
ValueCell.update(compose.values.uWeight, 1.0);
ValueCell.update(compose.values.tColor, composeTarget.texture);
compose.update();
@@ -236,6 +246,14 @@ export class MultiSamplePass {
camera.update();
// render scene
if (sampleIndex === 0) {
drawPass.postprocessing.setOcclusionOffset(0, 0);
} else {
drawPass.postprocessing.setOcclusionOffset(
offset[0] / width,
offset[1] / height
);
}
drawPass.render(ctx, props, false);
// compose rendered scene with compose target
@@ -258,6 +276,8 @@ export class MultiSamplePass {
}
}
drawPass.postprocessing.setOcclusionOffset(0, 0);
this.bindOutputTarget(toDrawingBuffer);
gl.viewport(x, y, width, height);
gl.scissor(x, y, width, height);
@@ -291,23 +311,23 @@ const JitterVectors = [
[0, 0]
],
[
[4, 4], [-4, -4]
[0, 0], [-4, -4]
],
[
[-2, -6], [6, -2], [-6, 2], [2, 6]
[0, 0], [6, -2], [-6, 2], [2, 6]
],
[
[1, -3], [-1, 3], [5, 1], [-3, -5],
[0, 0], [-1, 3], [5, 1], [-3, -5],
[-5, 5], [-7, -1], [3, 7], [7, -7]
],
[
[1, 1], [-1, -3], [-3, 2], [4, -1],
[0, 0], [-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],
[0, 0], [-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],

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,7 +8,6 @@ import { PickingId } from '../../mol-geo/geometry/picking';
import { PickType, Renderer } from '../../mol-gl/renderer';
import { Scene } from '../../mol-gl/scene';
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';
@@ -64,13 +63,11 @@ export class PickPass {
}
}
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) {
const depth = this.drawPass.depthTexturePrimitives;
private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
renderer.clear(false);
renderer.update(camera);
renderer.renderPick(scene.primitives, camera, variant, null, pickType);
renderer.renderPick(scene.volumes, camera, variant, depth, pickType);
renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
if (helper.camera.isEnabled) {

View File

@@ -1,11 +1,11 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
*/
import { QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { CopyRenderable, createCopyRenderable, QuadSchema, QuadValues } from '../../mol-gl/compute/util';
import { TextureSpec, Values, UniformSpec, DefineSpec } from '../../mol-gl/renderable/schema';
import { ShaderCode } from '../../mol-gl/shader-code';
import { WebGLContext } from '../../mol-gl/webgl/context';
@@ -30,7 +30,8 @@ import { SmaaParams, SmaaPass } from './smaa';
const OutlinesSchema = {
...QuadSchema,
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
dOrthographic: DefineSpec('number'),
@@ -41,11 +42,15 @@ const OutlinesSchema = {
};
type OutlinesRenderable = ComputeRenderable<Values<typeof OutlinesSchema>>
function getOutlinesRenderable(ctx: WebGLContext, depthTexture: Texture): OutlinesRenderable {
function getOutlinesRenderable(ctx: WebGLContext, depthTextureOpaque: Texture, depthTextureTransparent: Texture): OutlinesRenderable {
const width = depthTextureOpaque.getWidth();
const height = depthTextureOpaque.getHeight();
const values: Values<typeof OutlinesSchema> = {
...QuadValues,
tDepth: ValueCell.create(depthTexture),
uTexSize: ValueCell.create(Vec2.create(depthTexture.getWidth(), depthTexture.getHeight())),
tDepthOpaque: ValueCell.create(depthTextureOpaque),
tDepthTransparent: ValueCell.create(depthTextureTransparent),
uTexSize: ValueCell.create(Vec2.create(width, height)),
dOrthographic: ValueCell.create(0),
uNear: ValueCell.create(1),
@@ -183,7 +188,8 @@ const PostprocessingSchema = {
...QuadSchema,
tSsaoDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tColor: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthOpaque: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tDepthTransparent: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
tOutlines: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
uTexSize: UniformSpec('v2'),
@@ -199,6 +205,7 @@ const PostprocessingSchema = {
uMaxPossibleViewZDiff: UniformSpec('f'),
dOcclusionEnable: DefineSpec('boolean'),
uOcclusionOffset: UniformSpec('v2'),
dOutlineEnable: DefineSpec('boolean'),
dOutlineScale: DefineSpec('number'),
@@ -206,12 +213,13 @@ const PostprocessingSchema = {
};
type PostprocessingRenderable = ComputeRenderable<Values<typeof PostprocessingSchema>>
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTexture: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, depthTextureOpaque: Texture, depthTextureTransparent: Texture, outlinesTexture: Texture, ssaoDepthTexture: Texture): PostprocessingRenderable {
const values: Values<typeof PostprocessingSchema> = {
...QuadValues,
tSsaoDepth: ValueCell.create(ssaoDepthTexture),
tColor: ValueCell.create(colorTexture),
tDepth: ValueCell.create(depthTexture),
tDepthOpaque: ValueCell.create(depthTextureOpaque),
tDepthTransparent: ValueCell.create(depthTextureTransparent),
tOutlines: ValueCell.create(outlinesTexture),
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
@@ -227,6 +235,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uMaxPossibleViewZDiff: ValueCell.create(0.5),
dOcclusionEnable: ValueCell.create(true),
uOcclusionOffset: ValueCell.create(Vec2.create(0, 0)),
dOutlineEnable: ValueCell.create(false),
dOutlineScale: ValueCell.create(1),
@@ -244,9 +253,10 @@ export const PostprocessingParams = {
occlusion: PD.MappedStatic('on', {
on: PD.Group({
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.' }),
radius: PD.Numeric(5, { min: 0, max: 10, step: 0.1 }, { description: 'Final occlusion 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 }),
resolutionScale: PD.Numeric(1, { min: 0.1, max: 1, step: 0.05 }, { description: 'Adjust resolution of occlusion calculation' }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
@@ -271,6 +281,10 @@ export class PostprocessingPass {
return props.occlusion.name === 'on' || props.outline.name === 'on';
}
static isOutlineEnabled(props: PostprocessingProps) {
return props.outline.name === 'on';
}
readonly target: RenderTarget;
private readonly outlinesTarget: RenderTarget;
@@ -281,6 +295,9 @@ export class PostprocessingPass {
private readonly ssaoBlurFirstPassFramebuffer: Framebuffer;
private readonly ssaoBlurSecondPassFramebuffer: Framebuffer;
private readonly downsampledDepthTarget: RenderTarget;
private readonly downsampleDepthRenderable: CopyRenderable;
private readonly ssaoDepthTexture: Texture;
private readonly ssaoDepthBlurProxyTexture: Texture;
@@ -290,30 +307,31 @@ export class PostprocessingPass {
private nSamples: number;
private blurKernelSize: number;
private downsampleFactor: number;
private readonly renderable: PostprocessingRenderable;
private ssaoScale: number;
private calcSsaoScale() {
// downscale ssao for high pixel-ratios
return Math.min(1, 1 / this.webgl.pixelRatio);
return Math.min(1, 1 / this.webgl.pixelRatio) * this.downsampleFactor;
}
constructor(private webgl: WebGLContext, drawPass: DrawPass) {
this.ssaoScale = this.calcSsaoScale();
const { colorTarget, depthTexture } = drawPass;
constructor(private webgl: WebGLContext, private drawPass: DrawPass) {
const { colorTarget, depthTextureTransparent, depthTextureOpaque } = drawPass;
const width = colorTarget.getWidth();
const height = colorTarget.getHeight();
this.nSamples = 1;
this.blurKernelSize = 1;
this.downsampleFactor = 1;
this.ssaoScale = this.calcSsaoScale();
// needs to be linear for anti-aliasing pass
this.target = webgl.createRenderTarget(width, height, false, 'uint8', 'linear');
this.outlinesTarget = webgl.createRenderTarget(width, height, false);
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTexture);
this.outlinesRenderable = getOutlinesRenderable(webgl, depthTextureOpaque, depthTextureTransparent);
this.randomHemisphereVector = [];
for (let i = 0; i < 256; i++) {
@@ -332,20 +350,23 @@ export class PostprocessingPass {
const sw = Math.floor(width * this.ssaoScale);
const sh = Math.floor(height * this.ssaoScale);
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.downsampledDepthTarget = webgl.createRenderTarget(sw, sh, false, 'uint8', 'linear');
this.downsampleDepthRenderable = createCopyRenderable(webgl, depthTextureOpaque);
this.ssaoDepthTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
this.ssaoDepthTexture.define(sw, sh);
this.ssaoDepthTexture.attachFramebuffer(this.ssaoFramebuffer, 'color0');
this.ssaoDepthBlurProxyTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
this.ssaoDepthBlurProxyTexture = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'linear');
this.ssaoDepthBlurProxyTexture.define(sw, sh);
this.ssaoDepthBlurProxyTexture.attachFramebuffer(this.ssaoBlurFirstPassFramebuffer, 'color0');
this.ssaoDepthTexture.attachFramebuffer(this.ssaoBlurSecondPassFramebuffer, 'color0');
this.ssaoRenderable = getSsaoRenderable(webgl, depthTexture);
this.ssaoRenderable = getSsaoRenderable(webgl, this.downsampleFactor === 1 ? depthTextureOpaque : this.downsampledDepthTarget.texture);
this.ssaoBlurFirstPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthTexture, 'horizontal');
this.ssaoBlurSecondPassRenderable = getSsaoBlurRenderable(webgl, this.ssaoDepthBlurProxyTexture, 'vertical');
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, this.outlinesTarget.texture, this.ssaoDepthTexture);
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTextureOpaque, depthTextureTransparent, this.outlinesTarget.texture, this.ssaoDepthTexture);
}
setSize(width: number, height: number) {
@@ -359,11 +380,13 @@ export class PostprocessingPass {
const sh = Math.floor(height * this.ssaoScale);
this.target.setSize(width, height);
this.outlinesTarget.setSize(width, height);
this.downsampledDepthTarget.setSize(sw, sh);
this.ssaoDepthTexture.define(sw, sh);
this.ssaoDepthBlurProxyTexture.define(sw, sh);
ValueCell.update(this.renderable.values.uTexSize, Vec2.set(this.renderable.values.uTexSize.ref.value, width, height));
ValueCell.update(this.outlinesRenderable.values.uTexSize, Vec2.set(this.outlinesRenderable.values.uTexSize.ref.value, width, height));
ValueCell.update(this.downsampleDepthRenderable.values.uTexSize, Vec2.set(this.downsampleDepthRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
@@ -434,6 +457,30 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.ssaoBlurSecondPassRenderable.values.dOcclusionKernelSize, this.blurKernelSize);
}
if (this.downsampleFactor !== props.occlusion.params.resolutionScale) {
needsUpdateSsao = true;
this.downsampleFactor = props.occlusion.params.resolutionScale;
this.ssaoScale = this.calcSsaoScale();
const sw = Math.floor(w * this.ssaoScale);
const sh = Math.floor(h * this.ssaoScale);
this.downsampledDepthTarget.setSize(sw, sh);
this.ssaoDepthTexture.define(sw, sh);
this.ssaoDepthBlurProxyTexture.define(sw, sh);
if (this.ssaoScale === 1) {
ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureTransparent);
} else {
ValueCell.update(this.ssaoRenderable.values.tDepth, this.downsampledDepthTarget.texture);
}
ValueCell.update(this.downsampleDepthRenderable.values.uTexSize, Vec2.set(this.downsampleDepthRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoRenderable.values.uTexSize, Vec2.set(this.ssaoRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurFirstPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurFirstPassRenderable.values.uTexSize.ref.value, sw, sh));
ValueCell.update(this.ssaoBlurSecondPassRenderable.values.uTexSize, Vec2.set(this.ssaoBlurSecondPassRenderable.values.uTexSize.ref.value, sw, sh));
}
}
if (props.outline.name === 'on') {
@@ -494,6 +541,13 @@ export class PostprocessingPass {
gl.scissor(x, y, width, height);
}
private occlusionOffset: [x: number, y: number] = [0, 0];
setOcclusionOffset(x: number, y: number) {
this.occlusionOffset[0] = x;
this.occlusionOffset[1] = y;
ValueCell.update(this.renderable.values.uOcclusionOffset, Vec2.set(this.renderable.values.uOcclusionOffset.ref.value, x, y));
}
render(camera: ICamera, toDrawingBuffer: boolean, transparentBackground: boolean, backgroundColor: Color, props: PostprocessingProps) {
this.updateState(camera, transparentBackground, backgroundColor, props);
@@ -502,14 +556,13 @@ export class PostprocessingPass {
this.outlinesRenderable.render();
}
if (props.occlusion.name === 'on') {
const { x, y, width, height } = camera.viewport;
const sx = Math.floor(x * this.ssaoScale);
const sy = Math.floor(y * this.ssaoScale);
const sw = Math.ceil(width * this.ssaoScale);
const sh = Math.ceil(height * this.ssaoScale);
this.webgl.gl.viewport(sx, sy, sw, sh);
this.webgl.gl.scissor(sx, sy, sw, sh);
// don't render occlusion if offset is given,
// which will reuse the existing occlusion
if (props.occlusion.name === 'on' && this.occlusionOffset[0] === 0 && this.occlusionOffset[1] === 0) {
if (this.ssaoScale < 1) {
this.downsampledDepthTarget.bind();
this.downsampleDepthRenderable.render();
}
this.ssaoFramebuffer.bind();
this.ssaoRenderable.render();
@@ -519,9 +572,6 @@ export class PostprocessingPass {
this.ssaoBlurSecondPassFramebuffer.bind();
this.ssaoBlurSecondPassRenderable.render();
this.webgl.gl.viewport(x, y, width, height);
this.webgl.gl.scissor(x, y, width, height);
}
if (toDrawingBuffer) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 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>
@@ -61,6 +61,29 @@ describe('column', () => {
});
});
describe('string column', () => {
const xs = ['A', 'b', null, undefined];
const xsArr = xs.map(x => x ?? '');
const xsLC = xs.map(x => (x ?? '').toLowerCase());
const arr = Column.ofArray({ array: xs as any, schema: Column.Schema.str });
const arrLC = Column.ofArray({ array: xs as any, schema: Column.Schema.Str({ transform: 'lowercase' }) });
const aliasedLC = Column.ofArray({ array: xs as any, schema: Column.Schema.Aliased<'a' | 'b'>(Column.Schema.lstr) });
it('value', () => {
for (let i = 0; i < xs.length; i++) {
expect(arr.value(i)).toBe(xs[i] ?? '');
expect(arrLC.value(i)).toBe(xsLC[i] ?? '');
expect(aliasedLC.value(i)).toBe(xsLC[i]);
}
});
it('array', () => {
expect(arr.toArray()).toEqual(xsArr);
expect(arrLC.toArray()).toEqual(xsLC);
expect(aliasedLC.toArray()).toEqual(xsLC);
});
});
describe('table', () => {
const schema = {
x: Column.Schema.int,

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 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>
@@ -25,38 +25,39 @@ interface Column<T> {
namespace Column {
export type ArrayCtor<T> = { new(size: number): ArrayLike<T> }
export type Schema<T = any> = Schema.Str | Schema.Int | Schema.Float | Schema.Coordinate | Schema.Aliased<T> | Schema.Tensor | Schema.List<number|string>
export type Schema<T = any> = Schema.Str | Schema.Int | Schema.Float | Schema.Coordinate | Schema.Aliased<T> | Schema.Tensor | Schema.List<number | string>
export namespace Schema {
// T also serves as a default value for undefined columns
type Base<T extends string> = { valueType: T }
export type Str = { '@type': 'str', T: string } & Base<'str'>
export type Str = { '@type': 'str', T: string, transform?: 'uppercase' | 'lowercase' } & Base<'str'>
export type Int = { '@type': 'int', T: number } & Base<'int'>
export type Float = { '@type': 'float', T: number } & Base<'float'>
export type Coordinate = { '@type': 'coord', T: number } & Base<'float'>
export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space, baseType: Int | Float } & Base<'tensor'>
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<T extends string ? 'str' : 'int'>
export type List<T extends number|string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
export type Aliased<T> = { '@type': 'aliased', T: T, transform?: T extends string ? 'uppercase' | 'lowercase' : never } & Base<T extends string ? 'str' : 'int'>
export type List<T extends number | string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
export const str: Str = { '@type': 'str', T: '', valueType: 'str' };
export const ustr: Str = { '@type': 'str', T: '', valueType: 'str', transform: 'uppercase' };
export const lstr: Str = { '@type': 'str', T: '', valueType: 'str', transform: 'lowercase' };
export const int: Int = { '@type': 'int', T: 0, valueType: 'int' };
export const coord: Coordinate = { '@type': 'coord', T: 0, valueType: 'float' };
export const float: Float = { '@type': 'float', T: 0, valueType: 'float' };
export function Str(defaultValue = ''): Str { return { '@type': 'str', T: defaultValue, valueType: 'str' }; };
export function Str(options?: { defaultValue?: string, transform?: 'uppercase' | 'lowercase' }): Str { return { '@type': 'str', T: options?.defaultValue ?? '', transform: options?.transform, valueType: 'str' }; };
export function Int(defaultValue = 0): Int { return { '@type': 'int', T: defaultValue, valueType: 'int' }; };
export function Float(defaultValue = 0): Float { return { '@type': 'float', T: defaultValue, valueType: 'float' }; };
export function Tensor(space: Tensors.Space, baseType: Int | Float = float): Tensor { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor', baseType }; }
export function Vector(dim: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.Vector(dim, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }
export function Matrix(rows: number, cols: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.ColumnMajorMatrix(rows, cols, baseType['@type'] === 'int' ? Int32Array : Float64Array), baseType); }
export function Aliased<T>(t: Str | Int, defaultValue?: T): Aliased<T> {
if (typeof defaultValue !== 'undefined') return { ...t, T: defaultValue } as any as Aliased<T>;
export function Aliased<T>(t: Str | Int): Aliased<T> {
return t as any as Aliased<T>;
}
export function List<T extends number|string>(separator: string, itemParse: (x: string) => T, defaultValue: T[] = []): List<T> {
export function List<T extends number | string>(separator: string, itemParse: (x: string) => T, defaultValue: T[] = []): List<T> {
return { '@type': 'list', T: defaultValue, separator, itemParse, valueType: 'list' };
}
}
@@ -287,8 +288,13 @@ function lambdaColumn<T extends Column.Schema>({ value, valueKind, areValuesEqua
function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Column.ArraySpec<T>): Column<T['T']> {
const rowCount = array.length;
const defaultValue = schema.T;
const value: Column<T['T']>['value'] = schema.valueType === 'str'
? row => { const v = array[row]; return typeof v === 'string' ? v : '' + v; }
? (schema as Column.Schema.Str).transform === 'lowercase'
? row => { const v = array[row]; return typeof v === 'string' ? v.toLowerCase() : `${v ?? defaultValue}`.toLowerCase(); }
: (schema as Column.Schema.Str).transform === 'uppercase'
? row => { const v = array[row]; return typeof v === 'string' ? v.toUpperCase() : `${v ?? defaultValue}`.toUpperCase(); }
: row => { const v = array[row]; return typeof v === 'string' ? v : `${v ?? defaultValue}`; }
: row => array[row];
const isTyped = ColumnHelpers.isTypedArray(array);
@@ -300,15 +306,35 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
value,
valueKind: valueKind ? valueKind : row => Column.ValueKind.Present,
toArray: schema.valueType === 'str'
? params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) {
const v = array[start + i];
ret[i] = typeof v === 'string' ? v : '' + v;
? (schema as Column.Schema.Str).transform === 'lowercase'
? params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) {
const v = array[start + i];
ret[i] = typeof v === 'string' ? v.toLowerCase() : `${v ?? defaultValue}`.toLowerCase();
}
return ret;
}
return ret;
}
: (schema as Column.Schema.Str).transform === 'uppercase'
? params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) {
const v = array[start + i];
ret[i] = typeof v === 'string' ? v.toUpperCase() : `${v ?? defaultValue}`.toUpperCase();
}
return ret;
}
: params => {
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
for (let i = 0, _i = end - start; i < _i; i++) {
const v = array[start + i];
ret[i] = typeof v === 'string' ? v : `${v ?? defaultValue}`;
}
return ret;
}
: isTyped
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
: params => {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 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>
@@ -11,11 +11,13 @@ import { Color } from '../../mol-util/color';
import { Vec2, Vec3, Vec4 } from '../../mol-math/linear-algebra';
import { LocationIterator } from '../util/location-iterator';
import { NullLocation } from '../../mol-model/location';
import { LocationColor, ColorTheme } from '../../mol-theme/color';
import { Geometry } from './geometry';
import { LocationColor, ColorTheme, ColorVolume } from '../../mol-theme/color';
import { createNullTexture, Texture } from '../../mol-gl/webgl/texture';
export type ColorType = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance' | 'volume' | 'volumeInstance' | 'direct'
export type ColorTypeLocation = 'uniform' | 'instance' | 'group' | 'groupInstance' | 'vertex' | 'vertexInstance';
export type ColorTypeGrid = 'volume' | 'volumeInstance';
export type ColorTypeDirect = 'direct';
export type ColorType = ColorTypeLocation | ColorTypeGrid | ColorTypeDirect;
export type ColorData = {
uColor: ValueCell<Vec3>,
@@ -29,7 +31,7 @@ export type ColorData = {
dUsePalette: ValueCell<boolean>,
}
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
export function createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
const data = _createColors(locationIt, positionIt, colorTheme, colorData);
if (colorTheme.palette) {
ValueCell.updateIfChanged(data.dUsePalette, true);
@@ -40,16 +42,19 @@ export function createColors(locationIt: LocationIterator, positionIt: LocationI
return data;
}
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any>, colorData?: ColorData): ColorData {
switch (Geometry.getGranularity(locationIt, colorTheme.granularity)) {
function _createColors(locationIt: LocationIterator, positionIt: LocationIterator, colorTheme: ColorTheme<any, any>, colorData?: ColorData): ColorData {
switch (colorTheme.granularity) {
case 'uniform': return createUniformColor(locationIt, colorTheme.color, colorData);
case 'instance': return createInstanceColor(locationIt, colorTheme.color, colorData);
case 'instance':
return locationIt.nonInstanceable
? createGroupColor(locationIt, colorTheme.color, colorData)
: createInstanceColor(locationIt, colorTheme.color, colorData);
case 'group': return createGroupColor(locationIt, colorTheme.color, colorData);
case 'groupInstance': return createGroupInstanceColor(locationIt, colorTheme.color, colorData);
case 'vertex': return createVertexColor(positionIt, colorTheme.color, colorData);
case 'vertexInstance': return createVertexInstanceColor(positionIt, colorTheme.color, colorData);
case 'volume': return createGridColor((colorTheme as any).grid, 'volume', colorData);
case 'volumeInstance': return createGridColor((colorTheme as any).grid, 'volumeInstance', colorData);
case 'volume': return createGridColor(colorTheme.grid, 'volume', colorData);
case 'volumeInstance': return createGridColor(colorTheme.grid, 'volumeInstance', colorData);
case 'direct': return createDirectColor(colorData);
}
}
@@ -207,12 +212,6 @@ function createVertexInstanceColor(locationIt: LocationIterator, color: Location
//
interface ColorVolume {
colors: Texture
dimension: Vec3
transform: Vec4
}
export function createGridColor(grid: ColorVolume, type: ColorType, colorData?: ColorData): ColorData {
const { colors, dimension, transform } = grid;
const width = colors.getWidth();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -157,6 +157,7 @@ export namespace Cylinders {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -241,6 +242,7 @@ export namespace Cylinders {
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
@@ -258,6 +260,7 @@ export namespace Cylinders {
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -165,7 +165,7 @@ export namespace Lines {
export const Params = {
...BaseGeometry.Params,
sizeFactor: PD.Numeric(3, { min: 0, max: 10, step: 0.1 }),
sizeFactor: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }),
lineSizeAttenuation: PD.Boolean(false),
};
export type Params = typeof Params

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -34,13 +34,15 @@ export function calcMeshColorSmoothing(input: ColorSmoothingInput, resolution: n
const isInstanceType = colorType.endsWith('Instance');
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
const pad = 1 + resolution;
const expandedBox = Box3D.expand(Box3D(), box, Vec3.create(pad, pad, pad));
const scaleFactor = 1 / resolution;
const scaledBox = Box3D.scale(Box3D(), box, scaleFactor);
const scaledBox = Box3D.scale(Box3D(), expandedBox, scaleFactor);
const gridDim = Box3D.size(Vec3(), scaledBox);
Vec3.ceil(gridDim, gridDim);
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
const { min } = box;
const { min } = expandedBox;
const [xn, yn] = gridDim;
const { width, height } = getVolumeTexture2dLayout(gridDim);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 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>
@@ -625,6 +625,7 @@ export namespace Mesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -700,6 +701,7 @@ export namespace Mesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
@@ -720,6 +722,7 @@ export namespace Mesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -129,6 +129,7 @@ export namespace Spheres {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -208,6 +209,7 @@ export namespace Spheres {
uDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
@@ -225,6 +227,7 @@ export namespace Spheres {
ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}

View File

@@ -8,6 +8,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { ChunkedArray } from '../../../mol-data/util';
import { Text } from './text';
import { getFontAtlas } from './font-atlas';
import { assertUnreachable } from '../../../mol-util/type-helpers';
const quadIndices = new Uint16Array([
0, 1, 2,
@@ -237,7 +238,7 @@ export namespace TextBuilder {
yBaseCenter = yTop;
break;
default:
throw new Error('unsupported attachment');
assertUnreachable(attachment);
}
caAdd2(mappings, xTip, yTip); // tip
caAdd2(mappings, xBaseA, yBaseA); // base A

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2021-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -259,13 +259,15 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
const isInstanceType = input.colorType.endsWith('Instance');
const box = Box3D.fromSphere3D(Box3D(), isInstanceType ? input.boundingSphere : input.invariantBoundingSphere);
const pad = 1 + resolution;
const expandedBox = Box3D.expand(Box3D(), box, Vec3.create(pad, pad, pad));
const scaleFactor = 1 / resolution;
const scaledBox = Box3D.scale(Box3D(), box, scaleFactor);
const scaledBox = Box3D.scale(Box3D(), expandedBox, scaleFactor);
const gridDim = Box3D.size(Vec3(), scaledBox);
Vec3.ceil(gridDim, gridDim);
Vec3.add(gridDim, gridDim, Vec3.create(2, 2, 2));
const { min } = box;
const { min } = expandedBox;
const [dx, dy, dz] = gridDim;
const { texDimX: width, texDimY: height, texCols } = getTexture2dSize(gridDim);
@@ -308,7 +310,7 @@ export function calcTextureMeshColorSmoothing(input: ColorSmoothingInput, resolu
accumulateTexture.attachFramebuffer(framebuffer, 0);
countTexture.attachFramebuffer(framebuffer, 1);
const accumulateRenderable = getAccumulateRenderable(webgl, input, box, resolution, stride);
const accumulateRenderable = getAccumulateRenderable(webgl, input, expandedBox, resolution, stride);
state.currentRenderItemId = -1;
framebuffer.bind();

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -24,6 +24,7 @@ import { Vec2, Vec4 } from '../../../mol-math/linear-algebra';
import { createEmptyClipping } from '../clipping-data';
import { NullLocation } from '../../../mol-model/location';
import { createEmptySubstance } from '../substance-data';
import { RenderableState } from '../../../mol-gl/renderable';
export interface TextureMesh {
readonly kind: 'texture-mesh',
@@ -113,6 +114,7 @@ export namespace TextureMesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
transparentBackfaces: PD.Select('off', PD.arrayToOptions(['off', 'on', 'opaque']), BaseGeometry.ShadingCategory),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
bumpAmplitude: PD.Numeric(1, { min: 0, max: 5, step: 0.1 }, BaseGeometry.ShadingCategory),
};
@@ -125,8 +127,8 @@ export namespace TextureMesh {
createValuesSimple,
updateValues,
updateBoundingSphere,
createRenderableState: BaseGeometry.createRenderableState,
updateRenderableState: BaseGeometry.updateRenderableState,
createRenderableState,
updateRenderableState,
createPositionIterator: () => LocationIterator(1, 1, 1, () => NullLocation)
};
@@ -172,6 +174,7 @@ export namespace TextureMesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
dTransparentBackfaces: ValueCell.create(props.transparentBackfaces),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
@@ -192,6 +195,7 @@ export namespace TextureMesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.dTransparentBackfaces, props.transparentBackfaces);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
@@ -208,4 +212,16 @@ export namespace TextureMesh {
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
function createRenderableState(props: PD.Values<Params>): RenderableState {
const state = BaseGeometry.createRenderableState(props);
updateRenderableState(state, props);
return state;
}
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = state.opaque && !props.xrayShaded;
state.writeDepth = state.opaque;
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -29,6 +29,7 @@ export function applyTransparencyValue(array: Uint8Array, start: number, end: nu
}
export function getTransparencyAverage(array: Uint8Array, count: number): number {
if (count === 0 || array.length < count) return 0;
let sum = 0;
for (let i = 0; i < count; ++i) {
sum += array[i];

View File

@@ -31,7 +31,7 @@ describe('renderer', () => {
expect(ctx.gl.drawingBufferHeight).toBe(32);
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(1);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(0);
expect(ctx.stats.resourceCounts.shader).toBe(0);
@@ -52,7 +52,7 @@ describe('renderer', () => {
scene.add(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
expect(ctx.stats.resourceCounts.texture).toBe(8);
expect(ctx.stats.resourceCounts.texture).toBe(9);
expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 5 : 0);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);
@@ -60,7 +60,7 @@ describe('renderer', () => {
scene.remove(points);
scene.commit();
expect(ctx.stats.resourceCounts.attribute).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(0);
expect(ctx.stats.resourceCounts.texture).toBe(1);
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
expect(ctx.stats.resourceCounts.program).toBe(5);
expect(ctx.stats.resourceCounts.shader).toBe(10);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -26,6 +26,7 @@ export const CylindersSchema = {
uDoubleSided: UniformSpec('b'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -22,6 +22,7 @@ export const MeshSchema = {
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -153,7 +153,7 @@ export const GlobalUniformSchema = {
uXrayEdgeFalloff: UniformSpec('f'),
uRenderWboit: UniformSpec('b'),
uRenderMask: UniformSpec('i'),
uMarkingDepthTest: UniformSpec('b'),
uMarkingType: UniformSpec('i'),
uPickType: UniformSpec('i'),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -23,6 +23,7 @@ export const SpheresSchema = {
uDoubleSided: UniformSpec('b'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -23,6 +23,7 @@ export const TextureMeshSchema = {
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dTransparentBackfaces: DefineSpec('string', ['off', 'on', 'opaque']),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -16,7 +16,7 @@ import { GlobalUniformValues } from './renderable/schema';
import { GraphicsRenderVariant } from './webgl/render-item';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { degToRad } from '../mol-math/misc';
import { createNullTexture, Texture, Textures } from './webgl/texture';
import { Texture, Textures } from './webgl/texture';
import { arrayMapUpsert } from '../mol-util/array';
import { clamp } from '../mol-math/interpolate';
@@ -53,19 +53,20 @@ interface Renderer {
readonly stats: RendererStats
readonly props: Readonly<RendererProps>
clear: (toBackgroundColor: boolean) => void
clearDepth: () => void
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => void
clearDepth: (packed?: boolean) => void
update: (camera: ICamera) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void
renderPick: (group: Scene.Group, camera: ICamera, variant: 'pick' | 'depth', depthTexture: Texture | null, pickType: PickType) => void
renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDepthOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderDepthTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlended: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolumeOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolumeTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderBlendedVolume: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderWboitOpaque: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
renderWboitTransparent: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
@@ -135,6 +136,18 @@ function getLight(props: RendererProps['light'], light?: Light): Light {
}
namespace Renderer {
const enum Flag {
None = 0,
BlendedFront = 1,
BlendedBack = 2
}
const enum Mask {
All = 0,
Opaque = 1,
Transparent = 2,
}
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats } = ctx;
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props);
@@ -146,9 +159,11 @@ namespace Renderer {
let transparentBackground = false;
const nullDepthTexture = createNullTexture(gl);
const emptyDepthTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
emptyDepthTexture.define(1, 1);
emptyDepthTexture.load({ array: new Uint8Array([255, 255, 255, 255]), width: 1, height: 1 });
const sharedTexturesList: Textures = [
['tDepth', nullDepthTexture]
['tDepth', emptyDepthTexture]
];
const view = Mat4();
@@ -191,7 +206,7 @@ namespace Renderer {
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(bgColor),
uRenderWboit: ValueCell.create(false),
uRenderMask: ValueCell.create(0),
uMarkingDepthTest: ValueCell.create(false),
uPickType: ValueCell.create(PickType.None),
uMarkingType: ValueCell.create(MarkingType.None),
@@ -220,7 +235,7 @@ namespace Renderer {
let globalUniformsNeedUpdate = true;
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant, flag: Flag) => {
if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant === 'pick')) {
return;
}
@@ -259,6 +274,24 @@ namespace Renderer {
state.disable(gl.DEPTH_TEST);
state.depthMask(false);
}
} else if (flag === Flag.BlendedFront) {
state.enable(gl.CULL_FACE);
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
} else if (flag === Flag.BlendedBack) {
state.enable(gl.CULL_FACE);
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.BACK);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.FRONT);
}
} else {
if (r.values.uDoubleSided) {
if (r.values.uDoubleSided.ref.value || r.values.hasReflection.ref.value) {
@@ -271,14 +304,9 @@ namespace Renderer {
state.disable(gl.CULL_FACE);
}
if (r.values.dFlipSided) {
if (r.values.dFlipSided.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
state.frontFace(gl.CCW);
state.cullFace(gl.BACK);
}
if (r.values.dFlipSided?.ref.value) {
state.frontFace(gl.CW);
state.cullFace(gl.FRONT);
} else {
// webgl default
state.frontFace(gl.CCW);
@@ -308,8 +336,8 @@ namespace Renderer {
ValueCell.updateIfChanged(globalUniforms.uTransparentBackground, transparentBackground);
};
const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderWboit: boolean, markingDepthTest: boolean) => {
arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || nullDepthTexture);
const updateInternal = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null, renderMask: Mask, markingDepthTest: boolean) => {
arrayMapUpsert(sharedTexturesList, 'tDepth', depthTexture || emptyDepthTexture);
ValueCell.update(globalUniforms.uModel, group.view);
ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, group.view, camera.view));
@@ -317,7 +345,7 @@ namespace Renderer {
ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection));
ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection));
ValueCell.updateIfChanged(globalUniforms.uRenderWboit, renderWboit);
ValueCell.updateIfChanged(globalUniforms.uRenderMask, renderMask);
ValueCell.updateIfChanged(globalUniforms.uMarkingDepthTest, markingDepthTest);
state.enable(gl.SCISSOR_TEST);
@@ -336,13 +364,13 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.All, false);
ValueCell.updateIfChanged(globalUniforms.uPickType, pickType);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.colorOnly) {
renderObject(renderables[i], variant);
renderObject(renderables[i], variant, Flag.None);
}
}
};
@@ -352,11 +380,43 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.All, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
renderObject(renderables[i], 'depth');
renderObject(renderables[i], 'depth', Flag.None);
}
};
const renderDepthOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.state.opaque && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'depth', Flag.None);
}
}
};
const renderDepthTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.disable(gl.BLEND);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'depth', Flag.None);
}
}
};
@@ -365,7 +425,7 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.All, false);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth);
const { renderables } = group;
@@ -373,7 +433,7 @@ namespace Renderer {
const r = renderables[i];
if (r.values.markerAverage.ref.value !== 1) {
renderObject(renderables[i], 'marking');
renderObject(renderables[i], 'marking', Flag.None);
}
}
};
@@ -383,7 +443,7 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, !!depthTexture);
updateInternal(group, camera, depthTexture, Mask.All, !!depthTexture);
ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask);
const { renderables } = group;
@@ -391,7 +451,7 @@ namespace Renderer {
const r = renderables[i];
if (r.values.markerAverage.ref.value > 0) {
renderObject(renderables[i], 'marking');
renderObject(renderables[i], 'marking', Flag.None);
}
}
};
@@ -406,13 +466,15 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (r.state.opaque) {
renderObject(r, 'colorBlended');
renderObject(r, 'colorBlended', Flag.None);
} else if (r.values.uDoubleSided?.ref.value && r.values.dTransparentBackfaces?.ref.value === 'opaque') {
renderObject(r, 'colorBlended', Flag.BlendedBack);
}
}
};
@@ -420,7 +482,7 @@ namespace Renderer {
const renderBlendedTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.enable(gl.DEPTH_TEST);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
@@ -435,53 +497,38 @@ namespace Renderer {
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque && r.state.writeDepth) {
renderObject(r, 'colorBlended');
renderObject(r, 'colorBlended', Flag.None);
}
}
state.depthMask(false);
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
if (!r.state.opaque && !r.state.writeDepth) {
renderObject(r, 'colorBlended');
if ((!r.state.opaque && !r.state.writeDepth) || r.values.transparencyAverage.ref.value > 0) {
if (r.values.uDoubleSided?.ref.value) {
// render frontfaces and backfaces separately to avoid artefacts
if (r.values.dTransparentBackfaces?.ref.value !== 'opaque') {
renderObject(r, 'colorBlended', Flag.BlendedBack);
}
renderObject(r, 'colorBlended', Flag.BlendedFront);
} else {
renderObject(r, 'colorBlended', Flag.None);
}
}
}
};
const renderBlendedVolumeOpaque = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
const renderBlendedVolume = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
state.enable(gl.BLEND);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorBlended');
}
}
};
const renderBlendedVolumeTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
state.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
state.enable(gl.BLEND);
updateInternal(group, camera, depthTexture, false, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
const r = renderables[i];
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorBlended');
if (r.values.dGeometryType.ref.value === 'directVolume') {
renderObject(r, 'colorBlended', Flag.None);
}
}
};
@@ -491,7 +538,7 @@ namespace Renderer {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
updateInternal(group, camera, depthTexture, false, false);
updateInternal(group, camera, depthTexture, Mask.Opaque, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -500,14 +547,14 @@ namespace Renderer {
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
if ((alpha === 1 && r.values.transparencyAverage.ref.value !== 1 && r.values.dGeometryType.ref.value !== 'directVolume' && r.values.dPointStyle?.ref.value !== 'fuzzy' && !r.values.dXrayShaded?.ref.value) || r.values.dTransparentBackfaces?.ref.value === 'opaque') {
renderObject(r, 'colorWboit', Flag.None);
}
}
};
const renderWboitTransparent = (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => {
updateInternal(group, camera, depthTexture, true, false);
updateInternal(group, camera, depthTexture, Mask.Transparent, false);
const { renderables } = group;
for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -517,19 +564,19 @@ namespace Renderer {
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
renderObject(r, 'colorWboit');
renderObject(r, 'colorWboit', Flag.None);
}
}
};
return {
clear: (toBackgroundColor: boolean) => {
clear: (toBackgroundColor: boolean, ignoreTransparentBackground?: boolean) => {
state.enable(gl.SCISSOR_TEST);
state.enable(gl.DEPTH_TEST);
state.colorMask(true, true, true, true);
state.depthMask(true);
if (transparentBackground) {
if (transparentBackground && !ignoreTransparentBackground) {
state.clearColor(0, 0, 0, 0);
} else if (toBackgroundColor) {
state.clearColor(bgColor[0], bgColor[1], bgColor[2], 1);
@@ -538,23 +585,31 @@ namespace Renderer {
}
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
},
clearDepth: () => {
clearDepth: (packed = false) => {
state.enable(gl.SCISSOR_TEST);
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
gl.clear(gl.DEPTH_BUFFER_BIT);
if (packed) {
state.colorMask(true, true, true, true);
state.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
} else {
state.enable(gl.DEPTH_TEST);
state.depthMask(true);
gl.clear(gl.DEPTH_BUFFER_BIT);
}
},
update,
renderPick,
renderDepth,
renderDepthOpaque,
renderDepthTransparent,
renderMarkingDepth,
renderMarkingMask,
renderBlended,
renderBlendedOpaque,
renderBlendedTransparent,
renderBlendedVolumeOpaque,
renderBlendedVolumeTransparent,
renderBlendedVolume,
renderWboitOpaque,
renderWboitTransparent,

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 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>
@@ -16,6 +16,7 @@ import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
import { hash1 } from '../mol-data/util';
import { GraphicsRenderable } from './renderable';
import { GraphicsRenderVariants } from './webgl/render-item';
import { clamp } from '../mol-math/interpolate';
const boundaryHelper = new BoundaryHelper('98');
@@ -79,6 +80,8 @@ interface Scene extends Object3D {
has: (o: GraphicsRenderObject) => boolean
clear: () => void
forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void
readonly markerAverage: number
readonly opacityAverage: number
}
namespace Scene {
@@ -98,6 +101,9 @@ namespace Scene {
let boundingSphereDirty = true;
let boundingSphereVisibleDirty = true;
let markerAverage = 0;
let opacityAverage = 0;
const object3d = Object3D.create();
const { view, position, direction, up } = object3d;
@@ -154,6 +160,7 @@ namespace Scene {
}
renderables.sort(renderableSort);
opacityAverage = calculateOpacityAverage();
return true;
}
@@ -175,12 +182,41 @@ namespace Scene {
const newVisibleHash = computeVisibleHash();
if (newVisibleHash !== visibleHash) {
boundingSphereVisibleDirty = true;
opacityAverage = calculateOpacityAverage();
return true;
} else {
return false;
}
}
function calculateMarkerAverage() {
if (primitives.length === 0) return 0;
let count = 0;
let markerAverage = 0;
for (let i = 0, il = primitives.length; i < il; ++i) {
if (!primitives[i].state.visible) continue;
markerAverage += primitives[i].values.markerAverage.ref.value;
count += 1;
}
return count > 0 ? markerAverage / count : 0;
}
function calculateOpacityAverage() {
if (primitives.length === 0) return 0;
let count = 0;
let opacityAverage = 0;
for (let i = 0, il = primitives.length; i < il; ++i) {
const p = primitives[i];
if (!p.state.visible) continue;
// TODO: simplify, handle in renderable.state???
// uAlpha is updated in "render" so we need to recompute it here
const alpha = clamp(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1);
opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha;
count += 1;
}
return count > 0 ? opacityAverage / count : 0;
}
return {
view, position, direction, up,
@@ -206,6 +242,8 @@ namespace Scene {
} else {
syncVisibility();
}
markerAverage = calculateMarkerAverage();
opacityAverage = calculateOpacityAverage();
},
add: (o: GraphicsRenderObject) => commitQueue.add(o),
remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
@@ -243,7 +281,13 @@ namespace Scene {
visibleHash = computeVisibleHash();
}
return boundingSphereVisible;
}
},
get markerAverage() {
return markerAverage;
},
get opacityAverage() {
return opacityAverage;
},
};
}
}

View File

@@ -202,6 +202,7 @@ export const DirectVolumeShaderCode = ShaderCode('direct-volume', directVolume_v
import { image_vert } from './shader/image.vert';
import { image_frag } from './shader/image.frag';
import { assertUnreachable } from '../mol-util/type-helpers';
export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
//
@@ -228,7 +229,7 @@ function getDefinesCode(defines: ShaderDefines, ignore?: IgnoreDefine) {
} else if (typeof v === 'boolean') {
if (v) lines.push(`#define ${name}`);
} else {
throw new Error('unknown define type');
assertUnreachable(v);
}
}
}

View File

@@ -5,5 +5,9 @@ if (interior) {
} else {
gl_FragColor.rgb *= 1.0 - uInteriorDarkening;
}
#ifdef dTransparentBackfaces_opaque
gl_FragColor.a = 1.0;
#endif
}
`;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*
@@ -8,61 +8,65 @@
*/
export const apply_light_color = `
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
normal = bumpNormal;
}
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
normal = bumpNormal;
}
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0));
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
#ifdef enabledFragDepth
physicalMaterial.roughness = min(max(roughness, 0.0525), 1.0);
#else
vec3 dxy = max(abs(dFdx(normal)), abs(dFdy(normal)));
float geometryRoughness = max(max(dxy.x, dxy.y), dxy.z);
physicalMaterial.roughness = min(max(roughness, 0.0525) + geometryRoughness, 1.0);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#endif
physicalMaterial.specularColor = mix(vec3(0.04), color.rgb, metalness);
physicalMaterial.specularF90 = 1.0;
GeometricContext geometry;
geometry.position = -vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize(vViewPosition);
IncidentLight directLight;
#pragma unroll_loop_start
for (int i = 0; i < dLightCount; ++i) {
directLight.direction = uLightDirection[i];
directLight.color = uLightColor[i] * PI; // * PI for punctual light
RE_Direct_Physical(directLight, geometry, physicalMaterial, reflectedLight);
}
#pragma unroll_loop_end
vec3 irradiance = uAmbientColor * PI; // * PI for punctual light
RE_IndirectDiffuse_Physical(irradiance, geometry, physicalMaterial, reflectedLight);
// indirect specular only metals
vec3 radiance = uAmbientColor * metalness;
vec3 iblIrradiance = uAmbientColor * metalness;
vec3 clearcoatRadiance = vec3(0.0);
RE_IndirectSpecular_Physical(radiance, iblIrradiance, clearcoatRadiance, geometry, physicalMaterial, reflectedLight);
vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;
gl_FragColor = vec4(outgoingLight, color.a);
#ifdef dXrayShaded
gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);

View File

@@ -31,11 +31,34 @@ export const assign_material_color = `
#elif defined(dRenderVariant_pick)
vec4 material = vColor;
#elif defined(dRenderVariant_depth)
#ifdef enabledFragDepth
vec4 material = packDepthToRGBA(gl_FragDepthEXT);
if (fragmentDepth > getDepth(gl_FragCoord.xy / uDrawingBufferSize)) {
discard;
}
#ifndef dXrayShaded
#if defined(dTransparency)
float dta = 1.0 - vTransparency;
if (vTransparency < 0.2) dta = 1.0; // hard cutoff looks better
if (uRenderMask == MaskTransparent && uAlpha * dta == 1.0) {
discard;
} else if (uRenderMask == MaskOpaque && uAlpha * dta < 1.0) {
discard;
}
#else
if (uRenderMask == MaskTransparent && uAlpha == 1.0) {
discard;
} else if (uRenderMask == MaskOpaque && uAlpha < 1.0) {
discard;
}
#endif
#else
vec4 material = packDepthToRGBA(gl_FragCoord.z);
if (uRenderMask == MaskOpaque) {
discard;
}
#endif
vec4 material = packDepthToRGBA(fragmentDepth);
#elif defined(dRenderVariant_marking)
vec4 material;
if(uMarkingType == 1) {
@@ -51,7 +74,7 @@ export const assign_material_color = `
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
depthTest = (fragmentDepth >= getDepthPacked(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
}
bool isHighlight = intMod(marker, 2.0) > 0.1;
float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
@@ -62,47 +85,41 @@ export const assign_material_color = `
}
#endif
// apply screendoor transparency
#if defined(dTransparency)
// apply per-group transparency
#if defined(dTransparency) && (defined(dRenderVariant_pick) || defined(dRenderVariant_color))
float ta = 1.0 - vTransparency;
#if defined(dRenderVariant_colorWboit)
if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better with wboit
#endif
if (vTransparency < 0.2) ta = 1.0; // hard cutoff looks better
#if defined(dRenderVariant_pick)
if (ta < uPickingAlphaThreshold)
discard; // ignore so the element below can be picked
#else
#elif defined(dRenderVariant_color)
material.a *= ta;
#if defined(dRenderVariant_colorBlended)
float at = 0.0;
// shift by view-offset during multi-sample rendering to allow for blending
vec2 coord = gl_FragCoord.xy + uViewOffset * 0.25;
const mat4 thresholdMatrix = mat4(
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
);
int ci = int(intMod(coord.x, 4.0));
int ri = int(intMod(coord.y, 4.0));
#if __VERSION__ == 100
vec4 i = vec4(float(ci * 4 + ri));
vec4 v = thresholdMatrix[0] * vec4(equal(i, vec4(0.0, 1.0, 2.0, 3.0))) +
thresholdMatrix[1] * vec4(equal(i, vec4(4.0, 5.0, 6.0, 7.0))) +
thresholdMatrix[2] * vec4(equal(i, vec4(8.0, 9.0, 10.0, 11.0))) +
thresholdMatrix[3] * vec4(equal(i, vec4(12.0, 13.0, 14.0, 15.0)));
at = v.x + v.y + v.z + v.w;
#else
at = thresholdMatrix[ci][ri];
#if defined(dTransparentBackfaces_off)
if ((uRenderMask == MaskOpaque && material.a < 1.0) ||
(uRenderMask == MaskTransparent && material.a == 1.0) ||
(interior && material.a < 1.0)
) {
discard;
}
#elif defined(dTransparentBackfaces_on)
if ((uRenderMask == MaskOpaque && material.a < 1.0) ||
(uRenderMask == MaskTransparent && material.a == 1.0)
) {
discard;
}
#elif defined(dTransparentBackfaces_opaque)
if (interior) {
material.a = 1.0;
} else if (
(uRenderMask == MaskOpaque && material.a < 1.0) ||
(uRenderMask == MaskTransparent && material.a == 1.0)
) {
discard;
}
#endif
if (ta < 0.99 && (ta < 0.01 || ta < at)) {
discard;
}
#elif defined(dRenderVariant_colorWboit)
material.a *= ta;
#endif
#endif
#endif

View File

@@ -2,6 +2,12 @@ export const check_picking_alpha = `
float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
float alpha = (1.0 - fogFactor) * uAlpha;
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1)
discard; // ignore so the element below can be picked
// if not opaque enough ignore so the element below can be picked
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1) {
#ifdef dTransparentBackfaces_opaque
if (!interior) discard;
#else
discard;
#endif
}
`;

View File

@@ -19,6 +19,8 @@ uniform float uBumpiness;
uniform vec3 uColorGridDim;
uniform vec4 uColorGridTransform;
uniform sampler2D tColorGrid;
#elif defined(dColorType_direct)
varying vec4 vColor;
#endif
#ifdef dUsePalette

View File

@@ -64,17 +64,24 @@ uniform float uXrayEdgeFalloff;
uniform mat4 uProjection;
uniform bool uRenderWboit;
uniform int uRenderMask;
uniform bool uMarkingDepthTest;
uniform sampler2D tDepth;
uniform vec2 uDrawingBufferSize;
float getDepth(const in vec2 coords) {
// always packed due to merged depth from primitives and volumes
float getDepthPacked(const in vec2 coords) {
return unpackRGBAToDepth(texture2D(tDepth, coords));
}
float getDepth(const in vec2 coords) {
#ifdef depthTextureSupport
return texture2D(tDepth, coords).r;
#else
return unpackRGBAToDepth(texture2D(tDepth, coords));
#endif
}
float calcDepth(const in vec3 pos) {
vec2 clipZW = pos.z * uProjection[2].zw + uProjection[3].zw;
return 0.5 + 0.5 * clipZW.x / clipZW.y;

View File

@@ -17,6 +17,10 @@ export const common = `
#define dColorType_varying
#endif
#define MaskAll 0
#define MaskOpaque 1
#define MaskTransparent 2
//
#define PI 3.14159265

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -7,13 +7,16 @@
export const wboit_write = `
#if defined(dRenderVariant_colorWboit)
if (!uRenderWboit) {
if (uRenderMask == MaskOpaque) {
if (preFogAlpha < 1.0) {
discard;
}
} else if (uRenderWboit) {
} else if (uRenderMask == MaskTransparent) {
// the 'fragmentDepth > 0.99' check is to handle precision issues with packed depth
if (preFogAlpha != 1.0 && !interior && (fragmentDepth < getDepth(gl_FragCoord.xy / uDrawingBufferSize) || fragmentDepth > 0.99)) {
if (preFogAlpha != 1.0 && (fragmentDepth < getDepth(gl_FragCoord.xy / uDrawingBufferSize) || fragmentDepth > 0.99)) {
#ifdef dTransparentBackfaces_off
if (interior) discard;
#endif
float alpha = gl_FragColor.a;
float wboitWeight = alpha * clamp(pow(1.0 - fragmentDepth, 2.0), 0.01, 1.0);
gl_FragColor = vec4(gl_FragColor.rgb * alpha * wboitWeight, alpha);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -127,13 +127,9 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Michael Krone <michael.krone@uni-tuebingen.de>
@@ -72,12 +72,7 @@ uniform float uPickingAlphaThreshold;
uniform bool uTransparentBackground;
uniform float uXrayEdgeFalloff;
uniform float uInteriorDarkening;
uniform bool uInteriorColorFlag;
uniform vec3 uInteriorColor;
uniform bool uRenderWboit;
uniform bool uDoubleSided;
uniform int uRenderMask;
uniform float uNear;
uniform float uFar;
@@ -146,12 +141,7 @@ float transferFunction(float value) {
float getDepth(const in vec2 coords) {
#ifdef depthTextureSupport
if (!uRenderWboit) {
// in case of opaque volumes (and depth texture support)
return texture2D(tDepth, coords).r;
} else {
return unpackRGBAToDepth(texture2D(tDepth, coords));
}
return texture2D(tDepth, coords).r;
#else
return unpackRGBAToDepth(texture2D(tDepth, coords));
#endif
@@ -166,9 +156,7 @@ vec3 v3m4(vec3 p, mat4 m) {
float preFogAlphaBlended = 0.0;
vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
#if !defined(dIgnoreLight)
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(uModelView * vTransform)));
mat4 cartnToUnit = uCartnToUnit * inverse4(vTransform);
#if defined(dClipVariant_pixel) && dClipObjectCount != 0
mat4 modelTransform = uModel * vTransform * uTransform;
@@ -363,7 +351,6 @@ void main() {
float fragmentDepth = calcDepth((uModelView * vec4(start, 1.0)).xyz);
float preFogAlpha = clamp(preFogAlphaBlended, 0.0, 1.0);
bool interior = false;
#include wboit_write
}
`;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -99,7 +99,6 @@ void main() {
if (imageData.a > 0.9) imageData.a = 1.0;
float fragmentDepth = gl_FragCoord.z;
bool interior = false;
#if defined(dRenderVariant_pick)
if (imageData.a < 0.3)
@@ -131,7 +130,7 @@ void main() {
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
depthTest = (fragmentDepth >= getDepthPacked(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
}
bool isHighlight = intMod(marker, 2.0) > 0.1;
gl_FragColor = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -16,7 +16,6 @@ precision highp int;
void main(){
#include clip_pixel
bool interior = false;
float fragmentDepth = gl_FragCoord.z;
#include assign_material_color

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -43,17 +43,13 @@ void main() {
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
#if defined(dFlatShaded)
vec3 normal = -faceNormal;
#else
vec3 normal = -normalize(vNormal);
if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
#endif
#include apply_light_color
vec3 normal = -normalize(vNormal);
if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
#endif
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,7 +1,8 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export const outlines_frag = `
@@ -9,7 +10,8 @@ precision highp float;
precision highp int;
precision highp sampler2D;
uniform sampler2D tDepth;
uniform sampler2D tDepthOpaque;
uniform sampler2D tDepthTransparent;
uniform vec2 uTexSize;
uniform float uNear;
@@ -27,8 +29,16 @@ float getViewZ(const in float depth) {
#endif
}
float getDepth(const in vec2 coords) {
return unpackRGBAToDepth(texture2D(tDepth, coords));
float getDepthOpaque(const in vec2 coords) {
#ifdef depthTextureSupport
return texture2D(tDepthOpaque, coords).r;
#else
return unpackRGBAToDepth(texture2D(tDepthOpaque, coords));
#endif
}
float getDepthTransparent(const in vec2 coords) {
return unpackRGBAToDepth(texture2D(tDepthTransparent, coords));
}
bool isBackground(const in float depth) {
@@ -41,8 +51,11 @@ void main(void) {
vec2 coords = gl_FragCoord.xy / uTexSize;
vec2 invTexSize = 1.0 / uTexSize;
float selfDepth = getDepth(coords);
float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(getDepth(coords));
float selfDepthOpaque = getDepthOpaque(coords);
float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque);
float selfDepthTransparent = getDepthTransparent(coords);
float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent);
float outline = 1.0;
float bestDepth = 1.0;
@@ -50,12 +63,22 @@ void main(void) {
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;
float sampleDepth = getDepth(sampleCoords);
float sampleViewZ = isBackground(sampleDepth) ? backgroundViewZ : getViewZ(sampleDepth);
if (abs(selfViewZ - sampleViewZ) > uMaxPossibleViewZDiff && selfDepth > sampleDepth && sampleDepth <= bestDepth) {
float sampleDepthOpaque = getDepthOpaque(sampleCoords);
float sampleDepthTransparent = getDepthTransparent(sampleCoords);
float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque);
if (abs(selfViewZOpaque - sampleViewZOpaque) > uMaxPossibleViewZDiff && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestDepth) {
outline = 0.0;
bestDepth = sampleDepth;
bestDepth = sampleDepthOpaque;
}
if (sampleDepthTransparent < sampleDepthOpaque) {
float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent);
if (abs(selfViewZTransparent - sampleViewZTransparent) > uMaxPossibleViewZDiff && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestDepth) {
outline = 0.0;
bestDepth = sampleDepthTransparent;
}
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -20,7 +20,6 @@ void main(){
#include clip_pixel
float fragmentDepth = gl_FragCoord.z;
bool interior = false;
#include assign_material_color
#if defined(dPointStyle_circle)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -12,7 +12,8 @@ precision highp sampler2D;
uniform sampler2D tSsaoDepth;
uniform sampler2D tColor;
uniform sampler2D tDepth;
uniform sampler2D tDepthOpaque;
uniform sampler2D tDepthTransparent;
uniform sampler2D tOutlines;
uniform vec2 uTexSize;
@@ -24,8 +25,7 @@ uniform vec3 uFogColor;
uniform vec3 uOutlineColor;
uniform bool uTransparentBackground;
uniform float uOcclusionBias;
uniform float uOcclusionRadius;
uniform vec2 uOcclusionOffset;
uniform float uMaxPossibleViewZDiff;
@@ -41,19 +41,27 @@ float getViewZ(const in float depth) {
#endif
}
float getDepth(const in vec2 coords) {
return unpackRGBAToDepth(texture2D(tDepth, coords));
float getDepthOpaque(const in vec2 coords) {
#ifdef depthTextureSupport
return texture2D(tDepthOpaque, coords).r;
#else
return unpackRGBAToDepth(texture2D(tDepthOpaque, coords));
#endif
}
float getDepthTransparent(const in vec2 coords) {
return unpackRGBAToDepth(texture2D(tDepthTransparent, coords));
}
bool isBackground(const in float depth) {
return depth == 1.0;
}
float getOutline(const in vec2 coords, out float closestTexel) {
float getOutline(const in vec2 coords, const in float opaqueDepth, out float closestTexel) {
float backgroundViewZ = uFar + 3.0 * uMaxPossibleViewZDiff;
vec2 invTexSize = 1.0 / uTexSize;
float selfDepth = getDepth(coords);
float selfDepth = min(opaqueDepth, getDepthTransparent(coords));
float selfViewZ = isBackground(selfDepth) ? backgroundViewZ : getViewZ(selfDepth);
float outline = 1.0;
@@ -69,14 +77,15 @@ float getOutline(const in vec2 coords, out float closestTexel) {
vec4 sampleOutlineCombined = texture2D(tOutlines, sampleCoords);
float sampleOutline = sampleOutlineCombined.r;
float sampleOutlineDepth = unpackRGToUnitInterval(sampleOutlineCombined.gb);
float sampleOutlineViewZ = isBackground(sampleOutlineDepth) ? backgroundViewZ : getViewZ(sampleOutlineDepth);
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineDepth) > uMaxPossibleViewZDiff) {
if (sampleOutline == 0.0 && sampleOutlineDepth < closestTexel && abs(selfViewZ - sampleOutlineViewZ) > uMaxPossibleViewZDiff) {
outline = 0.0;
closestTexel = sampleOutlineDepth;
}
}
}
return outline;
return closestTexel < opaqueDepth ? outline : 1.0;
}
float getSsao(vec2 coords) {
@@ -96,13 +105,13 @@ void main(void) {
float viewDist;
float fogFactor;
float opaqueDepth = getDepthOpaque(coords);
#ifdef dOcclusionEnable
float depth = getDepth(coords);
if (!isBackground(depth)) {
viewDist = abs(getViewZ(depth));
if (!isBackground(opaqueDepth)) {
viewDist = abs(getViewZ(opaqueDepth));
fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
float occlusionFactor = getSsao(coords);
float occlusionFactor = getSsao(coords + uOcclusionOffset);
if (!uTransparentBackground) {
color.rgb = mix(mix(occlusionColor, uFogColor, fogFactor), color.rgb, occlusionFactor);
} else {
@@ -114,16 +123,15 @@ void main(void) {
// outline needs to be handled after occlusion to keep them clean
#ifdef dOutlineEnable
float closestTexel;
float outline = getOutline(coords, closestTexel);
float outline = getOutline(coords, opaqueDepth, closestTexel);
if (outline == 0.0) {
color.rgb = mix(uOutlineColor, color.rgb, outline);
viewDist = abs(getViewZ(closestTexel));
fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!uTransparentBackground) {
color.rgb = mix(color.rgb, uFogColor, fogFactor);
color.rgb = mix(uOutlineColor, uFogColor, fogFactor);
} else {
color.a = 1.0 - fogFactor;
color.rgb = mix(uOutlineColor, color.rgb, fogFactor);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -92,12 +92,8 @@ void main(void){
#elif defined(dRenderVariant_marking)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
vec3 normal = -cameraNormal;
#include apply_light_color
#endif
vec3 normal = -cameraNormal;
#include apply_light_color
#include apply_interior_color
#include apply_marker_color

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Áron Samuel Kovács <aron.kovacs@mail.muni.cz>
@@ -51,7 +51,15 @@ bool outsideBounds(const in vec2 p) {
}
float getDepth(const in vec2 coords) {
return outsideBounds(coords) ? 1.0 : unpackRGBAToDepth(texture2D(tDepth, coords));
if (outsideBounds(coords)) {
return 1.0;
} else {
#ifdef depthTextureSupport
return texture2D(tDepth, coords).r;
#else
return unpackRGBAToDepth(texture2D(tDepth, coords));
#endif
}
}
vec3 normalFromDepth(const in float depth, const in float depth1, const in float depth2, vec2 offset1, vec2 offset2) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -33,7 +33,6 @@ void main(){
#include clip_pixel
float fragmentDepth = gl_FragCoord.z;
bool interior = false;
#include assign_material_color
if (vTexCoord.x > 1.0) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,8 +8,8 @@ import { WebGLContext } from './context';
import { ValueCell } from '../../mol-util';
import { RenderableSchema } from '../renderable/schema';
import { idFactory } from '../../mol-util/id-factory';
import { ValueOf } from '../../mol-util/type-helpers';
import { GLRenderingContext } from './compat';
import { assertUnreachable, ValueOf } from '../../mol-util/type-helpers';
import { GLRenderingContext, isWebGL2 } from './compat';
import { WebGLExtensions } from './extensions';
import { WebGLState } from './state';
@@ -48,6 +48,7 @@ export function getDataType(gl: GLRenderingContext, dataType: DataType) {
case 'uint32': return gl.UNSIGNED_INT;
case 'int32': return gl.INT;
case 'float32': return gl.FLOAT;
default: assertUnreachable(dataType);
}
}
@@ -66,16 +67,20 @@ function dataTypeFromArray(gl: GLRenderingContext, array: ArrayType) {
return gl.INT;
} else if (array instanceof Float32Array) {
return gl.FLOAT;
} else {
throw new Error('Should nevver happen');
}
assertUnreachable(array);
}
export function getBufferType(gl: GLRenderingContext, bufferType: BufferType) {
switch (bufferType) {
case 'attribute': return gl.ARRAY_BUFFER;
case 'elements': return gl.ELEMENT_ARRAY_BUFFER;
case 'uniform': return (gl as WebGL2RenderingContext).UNIFORM_BUFFER;
case 'uniform':
if (isWebGL2(gl)) {
return gl.UNIFORM_BUFFER;
} else {
throw new Error('WebGL2 is required for uniform buffers');
}
}
}
@@ -158,18 +163,10 @@ function createBuffer(gl: GLRenderingContext, array: ArrayType, usageHint: Usage
//
export type AttributeItemSize = 1 | 2 | 3 | 4 | 16
export type AttributeKind = 'float32' | 'int32'
export type AttributeKind = 'float32'
export function getAttribType(gl: GLRenderingContext, kind: AttributeKind, itemSize: AttributeItemSize) {
switch (kind) {
case 'int32':
switch (itemSize) {
case 1: return gl.INT;
case 2: return gl.INT_VEC2;
case 3: return gl.INT_VEC3;
case 4: return gl.INT_VEC4;
}
break;
case 'float32':
switch (itemSize) {
case 1: return gl.FLOAT;
@@ -178,9 +175,9 @@ export function getAttribType(gl: GLRenderingContext, kind: AttributeKind, itemS
case 4: return gl.FLOAT_VEC4;
case 16: return gl.FLOAT_MAT4;
}
break;
default:
assertUnreachable(kind);
}
throw new Error(`unknown attribute type for kind '${kind}' and itemSize '${itemSize}'`);
}
export type AttributeDefs = {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -281,7 +281,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
gl,
isWebGL2: isWebGL2(gl),
get pixelRatio() {
const dpr = (typeof window !== 'undefined') ? window.devicePixelRatio : 1;
const dpr = (typeof window !== 'undefined') ? (window.devicePixelRatio || 1) : 1;
return dpr * (props.pixelScale || 1);
},

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,13 +7,13 @@
import { ShaderCode, DefineValues, addShaderDefines } from '../shader-code';
import { WebGLState } from './state';
import { WebGLExtensions } from './extensions';
import { getUniformSetters, UniformsList, getUniformType, UniformSetters } from './uniform';
import { getUniformSetters, UniformsList, getUniformType, UniformSetters, isArrayUniform } from './uniform';
import { AttributeBuffers, getAttribType } from './buffer';
import { TextureId, Textures } from './texture';
import { idFactory } from '../../mol-util/id-factory';
import { RenderableSchema } from '../renderable/schema';
import { isDebugMode } from '../../mol-util/debug';
import { GLRenderingContext } from './compat';
import { GLRenderingContext, isWebGL2 } from './compat';
import { ShaderType, Shader } from './shader';
const getNextProgramId = idFactory();
@@ -41,7 +41,14 @@ function getLocations(gl: GLRenderingContext, program: WebGLProgram, schema: Ren
// unused attributes will result in a `-1` location which is usually fine
// if (loc === -1) console.info(`Could not get attribute location for '${k}'`);
locations[k] = loc;
} else if (spec.type === 'uniform' || spec.type === 'texture') {
} else if (spec.type === 'uniform') {
let loc = gl.getUniformLocation(program, k);
// headless-gl requires a '[0]' suffix for array uniforms (https://github.com/stackgl/headless-gl/issues/170)
if (loc === null && isArrayUniform(spec.kind)) loc = gl.getUniformLocation(program, k + '[0]');
// unused uniforms will result in a `null` location which is usually fine
// if (loc === null) console.info(`Could not get uniform location for '${k}'`);
locations[k] = loc as number;
} else if (spec.type === 'texture') {
const loc = gl.getUniformLocation(program, k);
// unused uniforms will result in a `null` location which is usually fine
// if (loc === null) console.info(`Could not get uniform location for '${k}'`);
@@ -72,7 +79,7 @@ function checkActiveAttributes(gl: GLRenderingContext, program: WebGLProgram, sc
}
const attribType = getAttribType(gl, spec.kind, spec.itemSize);
if (attribType !== type) {
throw new Error(`unexpected attribute type for ${name}`);
throw new Error(`unexpected attribute type '${attribType}' for ${name}, expected '${type}'`);
}
}
}
@@ -104,8 +111,12 @@ function checkActiveUniforms(gl: GLRenderingContext, program: WebGLProgram, sche
throw new Error(`unexpected sampler type for '${name}'`);
}
} else if (spec.kind === 'volume-float32' || spec.kind === 'volume-uint8') {
if (type !== (gl as WebGL2RenderingContext).SAMPLER_3D) {
throw new Error(`unexpected sampler type for '${name}'`);
if (isWebGL2(gl)) {
if (type !== gl.SAMPLER_3D) {
throw new Error(`unexpected sampler type for '${name}'`);
}
} else {
throw new Error(`WebGL2 is required to use SAMPLER_3D`);
}
} else {
// TODO

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -9,6 +9,7 @@ import { createNullTexture, Texture, TextureFilter } from './texture';
import { createNullFramebuffer, Framebuffer } from './framebuffer';
import { WebGLResources } from './resources';
import { GLRenderingContext, isWebGL2 } from './compat';
import { Renderbuffer } from './renderbuffer';
const getNextRenderTargetId = idFactory();
@@ -16,6 +17,7 @@ export interface RenderTarget {
readonly id: number
readonly texture: Texture
readonly framebuffer: Framebuffer
readonly depthRenderbuffer: Renderbuffer | null
getWidth: () => number
getHeight: () => number
@@ -54,6 +56,7 @@ export function createRenderTarget(gl: GLRenderingContext, resources: WebGLResou
id: getNextRenderTargetId(),
texture: targetTexture,
framebuffer,
depthRenderbuffer,
getWidth: () => _width,
getHeight: () => _height,
@@ -90,6 +93,7 @@ export function createNullRenderTarget(gl: GLRenderingContext): RenderTarget {
id: getNextRenderTargetId(),
texture: createNullTexture(gl),
framebuffer: createNullFramebuffer(),
depthRenderbuffer: null,
getWidth: () => 0,
getHeight: () => 0,

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -40,6 +40,7 @@ export interface Renderbuffer {
bind: () => void
attachFramebuffer: (framebuffer: Framebuffer) => void
detachFramebuffer: (framebuffer: Framebuffer) => void
setSize: (width: number, height: number) => void
reset: () => void
destroy: () => void
@@ -78,6 +79,12 @@ export function createRenderbuffer(gl: GLRenderingContext, format: RenderbufferF
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, _attachment, gl.RENDERBUFFER, _renderbuffer);
if (isDebugMode) checkFramebufferStatus(gl);
},
detachFramebuffer: (framebuffer: Framebuffer) => {
framebuffer.bind();
bind();
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, _attachment, gl.RENDERBUFFER, null);
if (isDebugMode) checkFramebufferStatus(gl);
},
setSize: (width: number, height: number) => {
_width = width;
_height = height;

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -44,6 +44,10 @@ export type WebGLState = {
cullFace: (mode: number) => void
/** sets whether writing into the depth buffer is enabled or disabled */
depthMask: (flag: boolean) => void
/** specifies the depth value used when clearing depth buffer, used when calling `gl.clear` */
clearDepth: (depth: number) => void
/** sets the depth comparison function */
depthFunc: (func: number) => void
/** sets which color components to enable or to disable */
colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => void
/** specifies the color values used when clearing color buffers, used when calling `gl.clear`, clamped to [0, 1] */
@@ -58,6 +62,8 @@ export type WebGLState = {
blendEquation: (mode: number) => void
/** set the RGB blend equation and alpha blend equation separately, determines how a new pixel is combined with an existing */
blendEquationSeparate: (modeRGB: number, modeAlpha: number) => void
/** specifies the source and destination blending factors, clamped to [0, 1] */
blendColor: (red: number, green: number, blue: number, alpha: number) => void
enableVertexAttrib: (index: number) => void
clearVertexAttribsState: () => void
@@ -72,6 +78,8 @@ export function createState(gl: GLRenderingContext): WebGLState {
let currentFrontFace = gl.getParameter(gl.FRONT_FACE);
let currentCullFace = gl.getParameter(gl.CULL_FACE_MODE);
let currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK);
let currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE);
let currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC);
let currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK);
let currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
@@ -79,6 +87,7 @@ export function createState(gl: GLRenderingContext): WebGLState {
let currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB);
let currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA);
let currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA);
let currentBlendColor = gl.getParameter(gl.BLEND_COLOR);
let currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
let currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);
@@ -129,6 +138,18 @@ export function createState(gl: GLRenderingContext): WebGLState {
currentDepthMask = flag;
}
},
clearDepth: (depth: number) => {
if (depth !== currentClearDepth) {
gl.clearDepth(depth);
currentClearDepth = depth;
}
},
depthFunc: (func: number) => {
if (func !== currentDepthFunc) {
gl.depthFunc(func);
currentDepthFunc = func;
}
},
colorMask: (red: boolean, green: boolean, blue: boolean, alpha: boolean) => {
if (red !== currentColorMask[0] || green !== currentColorMask[1] || blue !== currentColorMask[2] || alpha !== currentColorMask[3]) {
gl.colorMask(red, green, blue, alpha);
@@ -166,7 +187,6 @@ export function createState(gl: GLRenderingContext): WebGLState {
currentBlendDstAlpha = dstAlpha;
}
},
blendEquation: (mode: number) => {
if (mode !== currentBlendEqRGB || mode !== currentBlendEqAlpha) {
gl.blendEquation(mode);
@@ -181,6 +201,15 @@ export function createState(gl: GLRenderingContext): WebGLState {
currentBlendEqAlpha = modeAlpha;
}
},
blendColor: (red: number, green: number, blue: number, alpha: number) => {
if (red !== currentBlendColor[0] || green !== currentBlendColor[1] || blue !== currentBlendColor[2] || alpha !== currentBlendColor[3]) {
gl.blendColor(red, green, blue, alpha);
currentBlendColor[0] = red;
currentBlendColor[1] = green;
currentBlendColor[2] = blue;
currentBlendColor[3] = alpha;
}
},
enableVertexAttrib: (index: number) => {
gl.enableVertexAttribArray(index);
@@ -199,6 +228,8 @@ export function createState(gl: GLRenderingContext): WebGLState {
currentFrontFace = gl.getParameter(gl.FRONT_FACE);
currentCullFace = gl.getParameter(gl.CULL_FACE_MODE);
currentDepthMask = gl.getParameter(gl.DEPTH_WRITEMASK);
currentClearDepth = gl.getParameter(gl.DEPTH_CLEAR_VALUE);
currentDepthFunc = gl.getParameter(gl.DEPTH_FUNC);
currentColorMask = gl.getParameter(gl.COLOR_WRITEMASK);
currentClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
@@ -206,6 +237,7 @@ export function createState(gl: GLRenderingContext): WebGLState {
currentBlendDstRGB = gl.getParameter(gl.BLEND_DST_RGB);
currentBlendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA);
currentBlendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA);
currentBlendColor = gl.getParameter(gl.BLEND_COLOR);
currentBlendEqRGB = gl.getParameter(gl.BLEND_EQUATION_RGB);
currentBlendEqAlpha = gl.getParameter(gl.BLEND_EQUATION_ALPHA);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -176,7 +176,7 @@ function isTexture2d(x: TextureImage<any> | TextureVolume<any>, target: number,
return target === gl.TEXTURE_2D;
}
function isTexture3d(x: TextureImage<any> | TextureVolume<any>, target: number, gl: WebGL2RenderingContext): x is TextureImage<any> {
function isTexture3d(x: TextureImage<any> | TextureVolume<any>, target: number, gl: WebGL2RenderingContext): x is TextureVolume<any> {
return target === gl.TEXTURE_3D;
}
@@ -238,6 +238,10 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
throw new Error(`texture kind '${kind}' and type '${_type}' are incompatible`);
}
if (!extensions.depthTexture && _format === 'depth') {
throw new Error(`extension 'WEBGL_depth_texture' needed for 'depth' texture format`);
}
const target = getTarget(gl, kind);
const filter = getFilter(gl, _filter);
const format = getFormat(gl, _format, _type);
@@ -260,6 +264,10 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
let destroyed = false;
function define(_width: number, _height: number, _depth?: number) {
if (_width === 0 || _height === 0 || (isWebGL2(gl) && target === gl.TEXTURE_3D && _depth === 0)) {
throw new Error('empty textures are not allowed');
}
if (width === _width && height === _height && depth === (_depth || 0)) return;
width = _width, height = _height, depth = _depth || 0;
@@ -272,14 +280,20 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
throw new Error('unknown texture target');
}
}
define(1, 1, isWebGL2(gl) && target === gl.TEXTURE_3D ? 1 : 0);
function load(data: TextureImage<any> | TextureVolume<any> | HTMLImageElement, sub = false) {
if (data.width === 0 || data.height === 0 || (!isImage(data) && isWebGL2(gl) && isTexture3d(data, target, gl) && data.depth === 0)) {
throw new Error('empty textures are not allowed');
}
gl.bindTexture(target, texture);
// unpack alignment of 1 since we use textures only for data
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
if (isImage(data)) {
width = data.width, height = data.height;
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, type, data);

View File

@@ -42,6 +42,10 @@ export function getUniformType(gl: GLRenderingContext, kind: UniformKind) {
}
}
export function isArrayUniform(kind: UniformKind) {
return kind.endsWith('[]');
}
export type UniformSetter = (gl: GLRenderingContext, location: number, value: any) => void
export type UniformSetters = { [k: string]: UniformSetter }

View File

@@ -0,0 +1,28 @@
import { ArrayEncoding } from '../binary-cif/array-encoder';
import { decode } from '../binary-cif/decoder';
const E = ArrayEncoding;
test('fixedPoint2', async () => {
const fixedPoint2 = E.by(E.fixedPoint(100)).and(E.delta).and(E.integerPacking);
const x = [1.092, 1.960, 0.666, 0.480, 1.267];
const y = [7.428, 7.026, 6.851, 7.524, 8.333];
const z = [26.270, 26.561, 25.573, 27.055, 25.881];
const xEnc = fixedPoint2.encode(new Float32Array(x));
const yEnc = fixedPoint2.encode(new Float32Array(y));
const zEnc = fixedPoint2.encode(new Float32Array(z));
expect(xEnc.data.length).toEqual(6);
expect(yEnc.data.length).toEqual(5);
expect(zEnc.data.length).toEqual(6);
const xDec = decode(xEnc);
const yDec = decode(yEnc);
const zDec = decode(zEnc);
x.forEach((a, i) => expect(xDec[i]).toBeCloseTo(a, 2));
y.forEach((a, i) => expect(yDec[i]).toBeCloseTo(a, 2));
z.forEach((a, i) => expect(zDec[i]).toBeCloseTo(a, 2));
});

View File

@@ -7,6 +7,7 @@
import { ArrayEncoder, ArrayEncoding as E } from './array-encoder';
import { getArrayDigitCount } from '../../../mol-util/number';
import { assertUnreachable } from '../../../mol-util/type-helpers';
export function classifyIntArray(xs: ArrayLike<number>) {
return IntClassifier.classify(xs as number[]);
@@ -62,7 +63,7 @@ namespace IntClassifier {
for (let i = 0, n = data.length; i < n; i++) {
incSize(info, size, data[i]);
}
return { ...byteSize(size), kind: 'pack' };
return { ...byteSize(size), kind: 'pack' as const };
}
function deltaSize(data: number[], info: IntColumnInfo) {
@@ -72,7 +73,7 @@ namespace IntClassifier {
incSizeSigned(size, data[i] - prev);
prev = data[i];
}
return { ...byteSize(size), kind: 'delta' };
return { ...byteSize(size), kind: 'delta' as const };
}
function rleSize(data: number[], info: IntColumnInfo) {
@@ -90,7 +91,7 @@ namespace IntClassifier {
incSize(info, size, data[data.length - 1]);
incSize(info, size, run);
return { ...byteSize(size), kind: 'rle' };
return { ...byteSize(size), kind: 'rle' as const };
}
function deltaRleSize(data: number[], info: IntColumnInfo) {
@@ -111,7 +112,7 @@ namespace IntClassifier {
incSizeSigned(size, prevValue);
incSizeSigned(size, run);
return { ...byteSize(size), kind: 'delta-rle' };
return { ...byteSize(size), kind: 'delta-rle' as const };
}
export function getSize(data: number[]) {
@@ -132,9 +133,8 @@ namespace IntClassifier {
case 'rle': return E.by(E.runLength).and(E.integerPacking);
case 'delta': return E.by(E.delta).and(E.integerPacking);
case 'delta-rle': return E.by(E.delta).and(E.runLength).and(E.integerPacking);
default: assertUnreachable(size);
}
throw new Error('should not happen :)');
}
}
@@ -169,9 +169,8 @@ namespace FloatClassifier {
case 'rle': return fp.and(E.runLength).and(E.integerPacking);
case 'delta': return fp.and(E.delta).and(E.integerPacking);
case 'delta-rle': return fp.and(E.delta).and(E.runLength).and(E.integerPacking);
default: assertUnreachable(size);
}
throw new Error('should not happen :)');
}
function getMultiplier(mantissaDigits: number) {

View File

@@ -7,6 +7,7 @@
import { Encoding, EncodedData } from './encoding';
import { IsNativeEndianLittle, flipByteOrder } from '../binary';
import { assertUnreachable } from '../../../mol-util/type-helpers';
/**
* Fixed point, delta, RLE, integer packing adopted from https://github.com/rcsb/mmtf-javascript/
@@ -33,7 +34,7 @@ function decodeStep(data: any, encoding: Encoding): any {
case Encoding.IntDataType.Uint32: return uint32(data);
case Encoding.FloatDataType.Float32: return float32(data);
case Encoding.FloatDataType.Float64: return float64(data);
default: throw new Error('Unsupported ByteArray type.');
default: assertUnreachable(encoding.type);
}
}
case 'FixedPoint': return fixedPoint(data, encoding);
@@ -53,7 +54,7 @@ function getIntArray(type: Encoding.IntDataType, size: number) {
case Encoding.IntDataType.Uint8: return new Uint8Array(size);
case Encoding.IntDataType.Uint16: return new Uint16Array(size);
case Encoding.IntDataType.Uint32: return new Uint32Array(size);
default: throw new Error('Unsupported integer data type.');
default: assertUnreachable(type);
}
}
@@ -61,7 +62,7 @@ function getFloatArray(type: Encoding.FloatDataType, size: number) {
switch (type) {
case Encoding.FloatDataType.Float32: return new Float32Array(size);
case Encoding.FloatDataType.Float64: return new Float64Array(size);
default: throw new Error('Unsupported floating data type.');
default: assertUnreachable(type);
}
}

View File

@@ -0,0 +1,453 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted and converted to TypeScript from https://github.com/image-js/iobuffer
* MIT License, Copyright (c) 2015 Michaël Zasso
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { TypedArray } from '../../mol-util/type-helpers';
const defaultByteLength = 1024 * 8;
const charArray: string[] = [];
export interface IOBufferParameters {
offset?: number // Ignore the first n bytes of the ArrayBuffer
}
/**
* Class for writing and reading binary data
*/
export class IOBuffer {
private _lastWrittenByte: number;
private _mark = 0;
private _marks: number[] = [];
private _data: DataView;
offset = 0; // The current offset of the buffer's pointer
littleEndian = true;
buffer: ArrayBuffer; // Reference to the internal ArrayBuffer object
length: number; // Byte length of the internal ArrayBuffer
byteLength: number; // Byte length of the internal ArrayBuffer
byteOffset: number; // Byte offset of the internal ArrayBuffer
/**
* If it's a number, it will initialize the buffer with the number as
* the buffer's length. If it's undefined, it will initialize the buffer
* with a default length of 8 Kb. If its an ArrayBuffer, a TypedArray,
* it will create a view over the underlying ArrayBuffer.
*/
constructor(data: number | ArrayBuffer | TypedArray, params: IOBufferParameters = {}) {
let dataIsGiven = false;
if (data === undefined) {
data = defaultByteLength;
}
if (typeof data === 'number') {
data = new ArrayBuffer(data);
} else {
dataIsGiven = true;
}
const offset = params.offset ? params.offset >>> 0 : 0;
const byteLength = data.byteLength - offset;
let dvOffset = offset;
if (!(data instanceof ArrayBuffer)) {
if (data.byteLength !== data.buffer.byteLength) {
dvOffset = data.byteOffset + offset;
}
data = data.buffer;
}
if (dataIsGiven) {
this._lastWrittenByte = byteLength;
} else {
this._lastWrittenByte = 0;
}
this.buffer = data;
this.length = byteLength;
this.byteLength = byteLength;
this.byteOffset = dvOffset;
this._data = new DataView(this.buffer, dvOffset, byteLength);
}
/**
* Checks if the memory allocated to the buffer is sufficient to store more bytes after the offset
* @param byteLength The needed memory in bytes
*/
available(byteLength: number = 1) {
return (this.offset + byteLength) <= this.length;
}
/**
* Check if little-endian mode is used for reading and writing multi-byte values
* Returns true if little-endian mode is used, false otherwise
*/
isLittleEndian() {
return this.littleEndian;
}
/**
* Set little-endian mode for reading and writing multi-byte values
*/
setLittleEndian() {
this.littleEndian = true;
return this;
}
/**
* Check if big-endian mode is used for reading and writing multi-byte values
* Returns true if big-endian mode is used, false otherwise
*/
isBigEndian() {
return !this.littleEndian;
}
/**
* Switches to big-endian mode for reading and writing multi-byte values
*/
setBigEndian() {
this.littleEndian = false;
return this;
}
/**
* Move the pointer n bytes forward
*/
skip(n: number) {
if (n === undefined) n = 1;
this.offset += n;
return this;
}
/**
* Move the pointer to the given offset
*/
seek(offset: number) {
this.offset = offset;
return this;
}
/**
* Store the current pointer offset.
*/
mark() {
this._mark = this.offset;
return this;
}
/**
* Move the pointer back to the last pointer offset set by mark
*/
reset() {
this.offset = this._mark;
return this;
}
/**
* Push the current pointer offset to the mark stack
*/
pushMark() {
this._marks.push(this.offset);
return this;
}
/**
* Pop the last pointer offset from the mark stack, and set the current pointer offset to the popped value
*/
popMark() {
const offset = this._marks.pop();
if (offset === undefined) throw new Error('Mark stack empty');
this.seek(offset);
return this;
}
/**
* Move the pointer offset back to 0
*/
rewind() {
this.offset = 0;
return this;
}
/**
* Make sure the buffer has sufficient memory to write a given byteLength at the current pointer offset
* If the buffer's memory is insufficient, this method will create a new buffer (a copy) with a length
* that is twice (byteLength + current offset)
*/
ensureAvailable(byteLength: number) {
if (byteLength === undefined) byteLength = 1;
if (!this.available(byteLength)) {
const lengthNeeded = this.offset + byteLength;
const newLength = lengthNeeded * 2;
const newArray = new Uint8Array(newLength);
newArray.set(new Uint8Array(this.buffer));
this.buffer = newArray.buffer;
this.length = this.byteLength = newLength;
this._data = new DataView(this.buffer);
}
return this;
}
/**
* Read a byte and return false if the byte's value is 0, or true otherwise
* Moves pointer forward
*/
readBoolean() {
return this.readUint8() !== 0;
}
/**
* Read a signed 8-bit integer and move pointer forward
*/
readInt8() {
return this._data.getInt8(this.offset++);
}
/**
* Read an unsigned 8-bit integer and move pointer forward
*/
readUint8() {
return this._data.getUint8(this.offset++);
}
/**
* Alias for {@link IOBuffer#readUint8}
*/
readByte() {
return this.readUint8();
}
/**
* Read n bytes and move pointer forward.
*/
readBytes(n: number) {
if (n === undefined) n = 1;
const bytes = new Uint8Array(n);
for (let i = 0; i < n; i++) {
bytes[i] = this.readByte();
}
return bytes;
}
/**
* Read a 16-bit signed integer and move pointer forward
*/
readInt16() {
const value = this._data.getInt16(this.offset, this.littleEndian);
this.offset += 2;
return value;
}
/**
* Read a 16-bit unsigned integer and move pointer forward
*/
readUint16() {
const value = this._data.getUint16(this.offset, this.littleEndian);
this.offset += 2;
return value;
}
/**
* Read a 32-bit signed integer and move pointer forward
*/
readInt32() {
const value = this._data.getInt32(this.offset, this.littleEndian);
this.offset += 4;
return value;
}
/**
* Read a 32-bit unsigned integer and move pointer forward
*/
readUint32() {
const value = this._data.getUint32(this.offset, this.littleEndian);
this.offset += 4;
return value;
}
/**
* Read a 32-bit floating number and move pointer forward
*/
readFloat32() {
const value = this._data.getFloat32(this.offset, this.littleEndian);
this.offset += 4;
return value;
}
/**
* Read a 64-bit floating number and move pointer forward
*/
readFloat64() {
const value = this._data.getFloat64(this.offset, this.littleEndian);
this.offset += 8;
return value;
}
/**
* Read 1-byte ascii character and move pointer forward
*/
readChar() {
return String.fromCharCode(this.readInt8());
}
/**
* Read n 1-byte ascii characters and move pointer forward
*/
readChars(n = 1) {
charArray.length = n;
for (let i = 0; i < n; i++) {
charArray[i] = this.readChar();
}
return charArray.join('');
}
/**
* Write 0xff if the passed value is truthy, 0x00 otherwise
*/
writeBoolean(value = false) {
this.writeUint8(value ? 0xff : 0x00);
return this;
}
/**
* Write value as an 8-bit signed integer
*/
writeInt8(value: number) {
this.ensureAvailable(1);
this._data.setInt8(this.offset++, value);
this._updateLastWrittenByte();
return this;
}
/**
* Write value as a 8-bit unsigned integer
*/
writeUint8(value: number) {
this.ensureAvailable(1);
this._data.setUint8(this.offset++, value);
this._updateLastWrittenByte();
return this;
}
/**
* An alias for IOBuffer#writeUint8
*/
writeByte(value: number) {
return this.writeUint8(value);
}
/**
* Write bytes
*/
writeBytes(bytes: number[] | Uint8Array) {
this.ensureAvailable(bytes.length);
for (let i = 0; i < bytes.length; i++) {
this._data.setUint8(this.offset++, bytes[i]);
}
this._updateLastWrittenByte();
return this;
}
/**
* Write value as an 16-bit signed integer
*/
writeInt16(value: number) {
this.ensureAvailable(2);
this._data.setInt16(this.offset, value, this.littleEndian);
this.offset += 2;
this._updateLastWrittenByte();
return this;
}
/**
* Write value as a 16-bit unsigned integer
*/
writeUint16(value: number) {
this.ensureAvailable(2);
this._data.setUint16(this.offset, value, this.littleEndian);
this.offset += 2;
this._updateLastWrittenByte();
return this;
}
/**
* Write a 32-bit signed integer at the current pointer offset
*/
writeInt32(value: number) {
this.ensureAvailable(4);
this._data.setInt32(this.offset, value, this.littleEndian);
this.offset += 4;
this._updateLastWrittenByte();
return this;
}
/**
* Write a 32-bit unsigned integer at the current pointer offset
*/
writeUint32(value: number) {
this.ensureAvailable(4);
this._data.setUint32(this.offset, value, this.littleEndian);
this.offset += 4;
this._updateLastWrittenByte();
return this;
}
/**
* Write a 32-bit floating number at the current pointer offset
*/
writeFloat32(value: number) {
this.ensureAvailable(4);
this._data.setFloat32(this.offset, value, this.littleEndian);
this.offset += 4;
this._updateLastWrittenByte();
return this;
}
/**
* Write a 64-bit floating number at the current pointer offset
*/
writeFloat64(value: number) {
this.ensureAvailable(8);
this._data.setFloat64(this.offset, value, this.littleEndian);
this.offset += 8;
this._updateLastWrittenByte();
return this;
}
/**
* Write the charCode of the passed string's first character to the current pointer offset
*/
writeChar(str: string) {
return this.writeUint8(str.charCodeAt(0));
}
/**
* Write the charCodes of the passed string's characters to the current pointer offset
*/
writeChars(str: string) {
for (let i = 0; i < str.length; i++) {
this.writeUint8(str.charCodeAt(i));
}
return this;
}
/**
* Export a Uint8Array view of the internal buffer.
* The view starts at the byte offset and its length
* is calculated to stop at the last written byte or the original length.
*/
toArray() {
return new Uint8Array(this.buffer, this.byteOffset, this._lastWrittenByte);
}
/**
* Update the last written byte offset
*/
private _updateLastWrittenByte() {
if (this.offset > this._lastWrittenByte) {
this._lastWrittenByte = this.offset;
}
}
}

View File

@@ -0,0 +1,527 @@
/**
* Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Adapted from https://github.com/cheminfo-js/netcdfjs
* MIT License, Copyright (c) 2016 cheminfo
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { IOBuffer } from '../io-buffer';
export interface NetCDFRecordDimension {
length: number,
id?: number,
name?: string,
recordStep?: number
}
export interface NetCDFVariable {
name: string
dimensions: any[]
attributes: any[]
type: string
size: number
offset: number
record: boolean
}
export interface NetCDFHeader {
recordDimension: NetCDFRecordDimension,
version: number,
dimensions: { name: string, size: number }[],
globalAttributes: { name: string, type: string, value: string | number }[],
variables: NetCDFVariable[]
}
export interface NetCDFDimension {
name: string,
size: number
}
/**
* Throws a non-valid NetCDF exception if the statement it's true
*/
function notNetcdf(statement: boolean, reason: string) {
if (statement) {
throw new TypeError('Not a valid NetCDF v3.x file: ' + reason);
}
}
/**
* Moves 1, 2, or 3 bytes to next 4-byte boundary
*/
function padding(buffer: IOBuffer) {
if ((buffer.offset % 4) !== 0) {
buffer.skip(4 - (buffer.offset % 4));
}
}
/**
* Reads the name
*/
function readName(buffer: IOBuffer) {
// Read name
const nameLength = buffer.readUint32();
const name = buffer.readChars(nameLength);
// validate name
// TODO
// Apply padding
padding(buffer);
return name;
}
const types = {
BYTE: 1,
CHAR: 2,
SHORT: 3,
INT: 4,
FLOAT: 5,
DOUBLE: 6
};
/**
* Parse a number into their respective type
*/
function num2str(type: number) {
switch (Number(type)) {
case types.BYTE:
return 'byte';
case types.CHAR:
return 'char';
case types.SHORT:
return 'short';
case types.INT:
return 'int';
case types.FLOAT:
return 'float';
case types.DOUBLE:
return 'double';
default:
return 'undefined';
}
}
/**
* Parse a number type identifier to his size in bytes
*/
function num2bytes(type: number) {
switch (Number(type)) {
case types.BYTE:
return 1;
case types.CHAR:
return 1;
case types.SHORT:
return 2;
case types.INT:
return 4;
case types.FLOAT:
return 4;
case types.DOUBLE:
return 8;
default:
return -1;
}
}
/**
* Reverse search of num2str
*/
function str2num(type: string) {
switch (String(type)) {
case 'byte':
return types.BYTE;
case 'char':
return types.CHAR;
case 'short':
return types.SHORT;
case 'int':
return types.INT;
case 'float':
return types.FLOAT;
case 'double':
return types.DOUBLE;
default:
return -1;
}
}
/**
* Auxiliary function to read numeric data
*/
function readNumber(size: number, bufferReader: Function) {
if (size !== 1) {
const numbers = new Array(size);
for (let i = 0; i < size; i++) {
numbers[i] = bufferReader();
}
return numbers;
} else {
return bufferReader();
}
}
/**
* Given a type and a size reads the next element
*/
function readType(buffer: IOBuffer, type: number, size: number) {
switch (type) {
case types.BYTE:
return buffer.readBytes(size);
case types.CHAR:
return trimNull(buffer.readChars(size));
case types.SHORT:
return readNumber(size, buffer.readInt16.bind(buffer));
case types.INT:
return readNumber(size, buffer.readInt32.bind(buffer));
case types.FLOAT:
return readNumber(size, buffer.readFloat32.bind(buffer));
case types.DOUBLE:
return readNumber(size, buffer.readFloat64.bind(buffer));
default:
notNetcdf(true, 'non valid type ' + type);
return undefined;
}
}
/**
* Removes null terminate value
*/
function trimNull(value: string) {
if (value.charCodeAt(value.length - 1) === 0) {
return value.substring(0, value.length - 1);
}
return value;
}
// const STREAMING = 4294967295;
/**
* Read data for the given non-record variable
*/
function nonRecord(buffer: IOBuffer, variable: { type: string, size: number }) {
// variable type
const type = str2num(variable.type);
// size of the data
const size = variable.size / num2bytes(type);
// iterates over the data
const data = new Array(size);
for (let i = 0; i < size; i++) {
data[i] = readType(buffer, type, 1);
}
return data;
}
/**
* Read data for the given record variable
*/
function record(buffer: IOBuffer, variable: { type: string, size: number }, recordDimension: NetCDFRecordDimension) {
// variable type
const type = str2num(variable.type);
const width = variable.size ? variable.size / num2bytes(type) : 1;
// size of the data
// TODO streaming data
const size = recordDimension.length;
// iterates over the data
const data = new Array(size);
const step = recordDimension.recordStep;
for (let i = 0; i < size; i++) {
const currentOffset = buffer.offset;
data[i] = readType(buffer, type, width);
buffer.seek(currentOffset + step!);
}
return data;
}
// Grammar constants
const ZERO = 0;
const NC_DIMENSION = 10;
const NC_VARIABLE = 11;
const NC_ATTRIBUTE = 12;
/**
* Read the header of the file
* Returns object with the fields:
* - `recordDimension`: Number with the length of record dimension
* - `dimensions`: List of dimensions
* - `globalAttributes`: List of global attributes
* - `variables`: List of variables
*/
function header(buffer: IOBuffer, version: number) {
// Length of record dimension
// sum of the varSize's of all the record variables.
const header: Partial<NetCDFHeader> = { recordDimension: { length: buffer.readUint32() } };
// Version
header.version = version;
// List of dimensions
const dimList = dimensionsList(buffer) as { dimensions: NetCDFDimension[], recordId: number, recordName: string };
header.recordDimension!.id = dimList.recordId;
header.recordDimension!.name = dimList.recordName;
header.dimensions = dimList.dimensions;
// List of global attributes
header.globalAttributes = attributesList(buffer);
// List of variables
const variables = variablesList(buffer, dimList.recordId, version) as { variables: any[], recordStep: number };
header.variables = variables.variables;
header.recordDimension!.recordStep = variables.recordStep;
return header;
}
/**
* List of dimensions
*/
function dimensionsList(buffer: IOBuffer) {
let dimensions: NetCDFDimension[], recordId, recordName;
const dimList = buffer.readUint32();
if (dimList === ZERO) {
notNetcdf((buffer.readUint32() !== ZERO), 'wrong empty tag for list of dimensions');
return [];
} else {
notNetcdf((dimList !== NC_DIMENSION), 'wrong tag for list of dimensions');
// Length of dimensions
const dimensionSize = buffer.readUint32();
dimensions = new Array(dimensionSize);
for (let dim = 0; dim < dimensionSize; dim++) {
// Read name
const name = readName(buffer);
// Read dimension size
const size = buffer.readUint32();
if (size === 0) {
recordId = dim;
recordName = name;
}
dimensions[dim] = {
name: name,
size: size
};
}
return {
dimensions: dimensions,
recordId: recordId,
recordName: recordName
};
}
}
/**
* List of attributes
*/
function attributesList(buffer: IOBuffer) {
let attributes: { name: string, type: ReturnType<typeof num2str>, value: any }[];
const gAttList = buffer.readUint32();
if (gAttList === ZERO) {
notNetcdf((buffer.readUint32() !== ZERO), 'wrong empty tag for list of attributes');
return [];
} else {
notNetcdf((gAttList !== NC_ATTRIBUTE), 'wrong tag for list of attributes');
// Length of attributes
const attributeSize = buffer.readUint32();
attributes = new Array(attributeSize);
for (let gAtt = 0; gAtt < attributeSize; gAtt++) {
// Read name
const name = readName(buffer);
// Read type
const type = buffer.readUint32();
notNetcdf(((type < 1) || (type > 6)), 'non valid type ' + type);
// Read attribute
const size = buffer.readUint32();
const value = readType(buffer, type, size);
// Apply padding
padding(buffer);
attributes[gAtt] = {
name: name,
type: num2str(type),
value: value
};
}
}
return attributes;
}
/**
* List of variables
*/
function variablesList(buffer: IOBuffer, recordId: number, version: number) {
const varList = buffer.readUint32();
let recordStep = 0;
let variables;
if (varList === ZERO) {
notNetcdf(
(buffer.readUint32() !== ZERO),
'wrong empty tag for list of variables'
);
return [];
} else {
notNetcdf((varList !== NC_VARIABLE), 'wrong tag for list of variables');
// Length of variables
const variableSize = buffer.readUint32();
variables = new Array(variableSize);
for (let v = 0; v < variableSize; v++) {
// Read name
const name = readName(buffer);
// Read dimensionality of the variable
const dimensionality = buffer.readUint32();
// Index into the list of dimensions
const dimensionsIds = new Array(dimensionality);
for (let dim = 0; dim < dimensionality; dim++) {
dimensionsIds[dim] = buffer.readUint32();
}
// Read variables size
const attributes = attributesList(buffer);
// Read type
const type = buffer.readUint32();
notNetcdf(((type < 1) && (type > 6)), 'non valid type ' + type);
// Read variable size
// The 32-bit varSize field is not large enough to contain the
// size of variables that require more than 2^32 - 4 bytes,
// so 2^32 - 1 is used in the varSize field for such variables.
const varSize = buffer.readUint32();
// Read offset
let offset = buffer.readUint32();
if (version === 2) {
notNetcdf((offset > 0), 'offsets larger than 4GB not supported');
offset = buffer.readUint32();
}
// Count amount of record variables
if (dimensionsIds[0] === recordId) {
recordStep += varSize;
}
variables[v] = {
name: name,
dimensions: dimensionsIds,
attributes: attributes,
type: num2str(type),
size: varSize,
offset: offset,
record: (dimensionsIds[0] === recordId)
};
}
}
return {
variables: variables,
recordStep: recordStep
};
}
/**
* Reads a NetCDF v3.x file
* https://www.unidata.ucar.edu/software/netcdf/docs/file_format_specifications.html
*/
export class NetcdfReader {
header: Partial<NetCDFHeader>;
buffer: IOBuffer;
constructor(data: ArrayBuffer) {
const buffer = new IOBuffer(data);
buffer.setBigEndian();
// Validate that it's a NetCDF file
notNetcdf((buffer.readChars(3) !== 'CDF'), 'should start with CDF');
// Check the NetCDF format
const version = buffer.readByte();
notNetcdf((version > 2), 'unknown version');
// Read the header
this.header = header(buffer, version);
this.buffer = buffer;
}
/**
* Version for the NetCDF format
*/
get version() {
if (this.header.version === 1) {
return 'classic format';
} else {
return '64-bit offset format';
}
}
get recordDimension() {
return this.header.recordDimension;
}
get dimensions() {
return this.header.dimensions;
}
get globalAttributes() {
return this.header.globalAttributes;
}
get variables() {
return this.header.variables;
}
/**
* Checks if a variable is available
* @param {string|object} variableName - Name of the variable to check
* @return {Boolean} - Variable existence
*/
hasDataVariable(variableName: string) {
return this.header.variables && this.header.variables.findIndex(val => val.name === variableName) !== -1;
}
/**
* Retrieves the data for a given variable
* @param {string|object} variableName - Name of the variable to search or variable object
* @return {Array} - List with the variable values
*/
getDataVariable(variableName: string | NetCDFVariable) {
let variable: NetCDFVariable | undefined;
if (typeof variableName === 'string') {
// search the variable
variable = this.header.variables?.find((val) => val.name === variableName);
} else {
variable = variableName;
}
// throws if variable not found
if (variable === undefined) throw new Error('variable not found');
// go to the offset position
this.buffer.seek(variable.offset);
if (variable.record) {
// record variable case
return record(this.buffer, variable, this.header.recordDimension!);
} else {
// non-record variable case
return nonRecord(this.buffer, variable);
}
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2017-2022 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>
@@ -45,7 +45,7 @@ type ColumnCtor = (field: Data.CifField, category: Data.CifCategory, key: string
function getColumnCtor(t: Column.Schema): ColumnCtor {
switch (t.valueType) {
case 'str': return (f, c, k) => createColumn(t, f, f.str, f.toStringArray);
case 'str': return (f, c, k) => createStringColumn(t, f, f.str, f.toStringArray);
case 'int': return (f, c, k) => createColumn(t, f, f.int, f.toIntArray);
case 'float': return (f, c, k) => createColumn(t, f, f.float, f.toFloatArray);
case 'list': throw new Error('Use createListColumn instead.');
@@ -53,6 +53,27 @@ function getColumnCtor(t: Column.Schema): ColumnCtor {
}
}
function createStringColumn<T extends string>(schema: Column.Schema.Str | Column.Schema.Aliased<T>, field: Data.CifField, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
return {
schema,
__array: field.__array,
isDefined: field.isDefined,
rowCount: field.rowCount,
value: schema.transform === 'lowercase'
? row => value(row).toLowerCase() as T
: schema.transform === 'uppercase'
? row => value(row).toUpperCase() as T
: value,
valueKind: field.valueKind,
areValuesEqual: field.areValuesEqual,
toArray: schema.transform === 'lowercase'
? p => Array.from(toArray(p)).map(x => x.toLowerCase() as T)
: schema.transform === 'uppercase'
? p => Array.from(toArray(p)).map(x => x.toUpperCase() as T)
: toArray,
};
}
function createColumn<T>(schema: Column.Schema, field: Data.CifField, value: (row: number) => T, toArray: Column<T>['toArray']): Column<T> {
return {
schema,

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