Compare commits

...

56 Commits

Author SHA1 Message Date
dsehnal
805f772696 3.0.0-dev.5 2021-12-16 12:21:45 +01:00
dsehnal
308bbc1ea0 lint 2021-12-16 12:19:32 +01:00
dsehnal
4a248b5591 shouldResetCamera reset fix 2021-12-16 12:17:46 +01:00
David Sehnal
704a9a111d Merge pull request #316 from JonStargaryen/master
update State Snapshots canApply when snapshots are added
2021-12-16 11:48:15 +01:00
JonStargaryen
a6befc5509 update State Snapshots canApply when snapshots are added 2021-12-16 11:31:53 +01:00
dsehnal
9e7aa4226d CI task 2021-12-14 21:55:23 +01:00
David Sehnal
7a25699c23 Merge pull request #312 from molstar/dependabot/npm_and_yarn/swagger-ui-dist-4.1.3
Bump swagger-ui-dist from 4.1.1 to 4.1.3
2021-12-14 14:29:01 +01:00
dependabot[bot]
901ae7f6d6 Bump swagger-ui-dist from 4.1.1 to 4.1.3
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 4.1.1 to 4.1.3.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v4.1.1...v4.1.3)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-14 13:26:49 +00:00
dsehnal
c57a4cdf6e 3.0.0-dev.4 2021-12-14 14:25:51 +01:00
dsehnal
18573e17f0 package.lock 2021-12-14 14:23:37 +01:00
dsehnal
5bc7ffa8a7 react and react-dom as peerDependencies 2021-12-14 14:17:25 +01:00
David Sehnal
430cc83259 Merge pull request #311 from molstar/upload-overlay
Drag and Drop Overlay
2021-12-14 14:11:15 +01:00
dsehnal
3cd3afb775 drag and drop overlay 2021-12-14 14:10:05 +01:00
dsehnal
93215b6beb prefer webgl 1 in Safari 15.1-15.3 2021-12-14 13:19:27 +01:00
Alexander Rose
e4f630dbef improve drag and drop support
- any file type
- multiple files
- if session, only first is loaded
2021-12-11 16:37:20 -08:00
Alexander Rose
ccaf18af04 add analytics to viewer on deployment
- only for molstar.org (done in ./scripts/deploy.js)
2021-12-11 12:25:05 -08:00
Alexander Rose
2b72098f95 fix: false positives in Model.isFromPdbArchive
- wrong for, e.g., AlphaFold DB entries
2021-12-11 11:42:15 -08:00
Alexander Rose
b32546bea7 add outline color option to renderer 2021-12-11 11:30:00 -08:00
Alexander Rose
b9b0413e9f improve label repr defaults 2021-12-11 11:25:35 -08:00
David Sehnal
1d29b4627f Merge pull request #305 from MadCatX/cleanup-old-nodejs
Make cleanup script work with older vesions of Node.js
2021-12-10 12:43:08 +01:00
Michal Malý
bd44c76709 Make cleanup script work with older versions of Node.js 2021-12-10 09:36:58 +01:00
Alexander Rose
06b4761f2b add `carbonLightness` to illustrative color theme 2021-12-06 20:02:25 -08:00
Alexander Rose
daa3d1dbaa handle zero light count 2021-12-06 20:01:46 -08:00
Alexander Rose
5490d5ceb5 fixes for material bumpiness 2021-12-06 20:01:18 -08:00
dsehnal
cf3c1cfcce update Group Presets UI 2021-12-06 10:44:20 +01:00
Alexander Rose
be2607ae84 add bumpAmplitude render parameter 2021-12-05 21:29:07 -08:00
Alexander Rose
e2966241e8 Merge pull request #299 from molstar/bump
procedural bump mapping
2021-12-05 12:42:27 -08:00
Alexander Rose
447792b1ef changelog 2021-12-05 12:30:28 -08:00
Alexander Rose
1cbcb5c530 remove duplicated line 2021-12-05 12:10:26 -08:00
Alexander Rose
f8d32d1d8d fix temp texture usage for color smoothing 2021-12-05 12:07:32 -08:00
Alexander Rose
8c556c2849 Merge branch 'master' of https://github.com/molstar/molstar into bump 2021-12-05 12:02:21 -08:00
Alexander Rose
4d0f0ceebf 3.0.0-dev.3 2021-12-04 22:23:51 -08:00
Alexander Rose
14abcddfcf changelog 2021-12-04 22:17:24 -08:00
Alexander Rose
704cc96a9f Merge pull request #300 from sukolsak/fix-export
Fix USDZ and OBJ export
2021-12-04 22:13:41 -08:00
Sukolsak Sakshuwong
b51f610173 fix USDZ and OBJ export 2021-12-04 20:08:15 -08:00
Alexander Rose
98050875c7 add bumpiness to material 2021-12-04 16:54:23 -08:00
dsehnal
470280ea1a changelog and update eslint 2021-12-01 13:29:02 +01:00
dsehnal
dafb5a8299 3.0.0-dev.2 2021-12-01 13:26:08 +01:00
dsehnal
4a1af03744 package.json fix 2021-12-01 13:23:50 +01:00
dsehnal
bb176f1efb 3.0.0-dev.1 2021-12-01 13:17:15 +01:00
dsehnal
a53bcde973 npmignore tweaks 2021-12-01 13:14:28 +01:00
David Sehnal
1a8dc2c637 Merge pull request #295 from molstar/material-object
Refactor Material Representation
2021-11-29 18:04:52 +01:00
dsehnal
f96211ff91 comment 2021-11-29 18:04:23 +01:00
dsehnal
77f9c02785 udpate presets 2021-11-29 18:01:41 +01:00
dsehnal
7910b65fdc Merge branch 'master' of https://github.com/molstar/molstar 2021-11-29 14:49:53 +01:00
dsehnal
eb4fc4588d remove console.log 2021-11-29 14:49:51 +01:00
David Sehnal
5430674071 Merge pull request #294 from MadCatX/fixtypo
Fix typo in README
2021-11-29 14:10:20 +01:00
dsehnal
17e67e3b79 Refactor Material representation 2021-11-29 14:09:56 +01:00
dsehnal
e87a0d72e4 Fix docking viewer material usage 2021-11-29 12:48:26 +01:00
dsehnal
67d3c65907 ParamDefinition.Group.presets support 2021-11-29 12:35:57 +01:00
dsehnal
564a5486c9 StructureComponentManager.Options state saving support 2021-11-29 11:57:37 +01:00
Michal Malý
9ce11c4c32 Fix typo in README 2021-11-29 11:41:57 +01:00
dsehnal
5e97b551a5 changelog 2021-11-29 11:10:30 +01:00
David Sehnal
77536e75af Merge pull request #292 from MadCatX/provide-rebuild-cmd
Add packaging command to force a full rebuild
2021-11-29 11:05:44 +01:00
Michal Malý
3a6ab55266 Add a cleanup packaging script 2021-11-28 22:29:50 +01:00
Michal Malý
aaec452bc2 Add packaging command to force a full rebuild 2021-11-28 13:28:06 +01:00
67 changed files with 668 additions and 253 deletions

View File

@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14
node-version: 17
- run: npm ci
- run: sudo apt-get install xvfb
- name: Lint

View File

@@ -1 +1,5 @@
lib/tsconfig.commonjs.tsbuildinfo
tests
perf-tests
_spec
*.tsbuildinfo
*.js.map

View File

@@ -6,11 +6,38 @@ Note that since we don't clearly distinguish between a public and private interf
## [Unreleased]
## [v3.0.0-dev.5] - 2021-12-16
- Fix initial camera reset not triggering for some entries.
## [v3.0.0-dev.4] - 2021-12-14
- Add ``bumpiness`` (per-object and per-group), ``bumpFrequency`` & ``bumpAmplitude`` (per-object) render parameters (#299)
- Change ``label`` representation defaults: Use text border instead of rectangle background
- Add outline color option to renderer
- Fix false positives in Model.isFromPdbArchive
- Add drag and drop support for loading any file, including multiple at once
- If there are session files (.molx or .molj) among the dropped files, only the first session will be loaded
- Add drag and drop overlay
- Safari 15.1 - 15.3 WebGL 2 support workaround
- [Breaking] Move ``react`` and ``react-dom`` to ``peerDependencies``. This might break some builds.
## [v3.0.0-dev.3] - 2021-12-4
- Fix OBJ and USDZ export
## [v3.0.0-dev.2] - 2021-12-1
- Do not include tests and source maps in NPM package
## [v3.0.0-dev.0] - 2021-11-28
- Add multiple lights support (with color, intensity, and direction parameters)
- [Breaking] Add per-object material rendering properties
- ``SimpleSettingsParams.lighting.renderStyle`` and ``RendererParams.style`` were removed
- Add substance theme with per-group material rendering properties
- ``StructureComponentManager.Options`` state saving support
- ``ParamDefinition.Group.presets`` support
## [v2.4.1] - 2021-11-28

View File

@@ -68,6 +68,17 @@ If working on just the viewer, ``npm run watch-viewer`` will provide shorter com
Debug/production mode in browsers can be turned on/off during runtime by calling ``setMolStarDebugMode(true/false, true/false)`` from the dev console.
### Cleaning and forcing a full rebuild
npm run clean
Wipes the `build` and `lib` directories and `.tsbuildinfo` files.
npm run rebuild
Runs the cleanup script prior to building the project, forcing a full rebuild of the project.
Use these commands to resolve occassional build failures which may arise after some dependency updates. Once done, `npm run build` should work again. Note that full rebuilds take more time to complete.
### Build for production:
NODE_ENV=production npm run build

168
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "molstar",
"version": "3.0.0-dev.0",
"version": "3.0.0-dev.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "molstar",
"version": "3.0.0-dev.0",
"version": "3.0.0-dev.5",
"license": "MIT",
"dependencies": {
"@types/argparse": "^2.0.10",
@@ -27,8 +27,6 @@
"immer": "^9.0.7",
"immutable": "^3.8.2",
"node-fetch": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rxjs": "^7.4.0",
"swagger-ui-dist": "^4.1.1",
"tslib": "^2.3.1",
@@ -56,8 +54,8 @@
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.0.3",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"benchmark": "^2.1.4",
"concurrently": "^6.4.0",
"cpx2": "^4.0.0",
@@ -85,6 +83,10 @@
},
"optionalDependencies": {
"gl": "^4.9.2"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
}
},
"node_modules/@babel/code-frame": {
@@ -2885,13 +2887,13 @@
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz",
"integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
"integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
"dev": true,
"dependencies": {
"@typescript-eslint/experimental-utils": "5.4.0",
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/experimental-utils": "5.5.0",
"@typescript-eslint/scope-manager": "5.5.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
@@ -2917,15 +2919,15 @@
}
},
"node_modules/@typescript-eslint/experimental-utils": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz",
"integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
"integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/typescript-estree": "5.4.0",
"@typescript-eslint/scope-manager": "5.5.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/typescript-estree": "5.5.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
},
@@ -2941,14 +2943,14 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz",
"integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
"integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/typescript-estree": "5.4.0",
"@typescript-eslint/scope-manager": "5.5.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/typescript-estree": "5.5.0",
"debug": "^4.3.2"
},
"engines": {
@@ -2968,13 +2970,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz",
"integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
"integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/visitor-keys": "5.4.0"
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/visitor-keys": "5.5.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -2985,9 +2987,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz",
"integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
"integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -2998,13 +3000,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz",
"integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
"integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/visitor-keys": "5.4.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/visitor-keys": "5.5.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
@@ -3025,12 +3027,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz",
"integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
"integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/types": "5.5.0",
"eslint-visitor-keys": "^3.0.0"
},
"engines": {
@@ -11292,6 +11294,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -11304,6 +11307,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -11864,6 +11868,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -12522,9 +12527,9 @@
}
},
"node_modules/swagger-ui-dist": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.1.tgz",
"integrity": "sha512-xvPMEpOCbd5S9Z1pJT/L/K/C2yAX89ZWJDxEECkVW1dENsKFLbu7sRY7b6UBqLXuTUfdM5owlgPrpwr24CRonA=="
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.3.tgz",
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
},
"node_modules/swap-case": {
"version": "2.0.2",
@@ -16064,13 +16069,13 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.4.0.tgz",
"integrity": "sha512-9/yPSBlwzsetCsGEn9j24D8vGQgJkOTr4oMLas/w886ZtzKIs1iyoqFrwsX2fqYEeUwsdBpC21gcjRGo57u0eg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.5.0.tgz",
"integrity": "sha512-4bV6fulqbuaO9UMXU0Ia0o6z6if+kmMRW8rMRyfqXj/eGrZZRGedS4n0adeGNnjr8LKAM495hrQ7Tea52UWmQA==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "5.4.0",
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/experimental-utils": "5.5.0",
"@typescript-eslint/scope-manager": "5.5.0",
"debug": "^4.3.2",
"functional-red-black-tree": "^1.0.1",
"ignore": "^5.1.8",
@@ -16080,55 +16085,55 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.4.0.tgz",
"integrity": "sha512-Nz2JDIQUdmIGd6p33A+naQmwfkU5KVTLb/5lTk+tLVTDacZKoGQisj8UCxk7onJcrgjIvr8xWqkYI+DbI3TfXg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.5.0.tgz",
"integrity": "sha512-kjWeeVU+4lQ1SLYErRKV5yDXbWDPkpbzTUUlfAUifPYvpX0qZlrcCZ96/6oWxt3QxtK5WVhXz+KsnwW9cIW+3A==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.9",
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/typescript-estree": "5.4.0",
"@typescript-eslint/scope-manager": "5.5.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/typescript-estree": "5.5.0",
"eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0"
}
},
"@typescript-eslint/parser": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.4.0.tgz",
"integrity": "sha512-JoB41EmxiYpaEsRwpZEYAJ9XQURPFer8hpkIW9GiaspVLX8oqbqNM8P4EP8HOZg96yaALiLEVWllA2E8vwsIKw==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.5.0.tgz",
"integrity": "sha512-JsXBU+kgQOAgzUn2jPrLA+Rd0Y1dswOlX3hp8MuRO1hQDs6xgHtbCXEiAu7bz5hyVURxbXcA2draasMbNqrhmg==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.4.0",
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/typescript-estree": "5.4.0",
"@typescript-eslint/scope-manager": "5.5.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/typescript-estree": "5.5.0",
"debug": "^4.3.2"
}
},
"@typescript-eslint/scope-manager": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.4.0.tgz",
"integrity": "sha512-pRxFjYwoi8R+n+sibjgF9iUiAELU9ihPBtHzocyW8v8D8G8KeQvXTsW7+CBYIyTYsmhtNk50QPGLE3vrvhM5KA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.5.0.tgz",
"integrity": "sha512-0/r656RmRLo7CbN4Mdd+xZyPJ/fPCKhYdU6mnZx+8msAD8nJSP8EyCFkzbd6vNVZzZvWlMYrSNekqGrCBqFQhg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/visitor-keys": "5.4.0"
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/visitor-keys": "5.5.0"
}
},
"@typescript-eslint/types": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.4.0.tgz",
"integrity": "sha512-GjXNpmn+n1LvnttarX+sPD6+S7giO+9LxDIGlRl4wK3a7qMWALOHYuVSZpPTfEIklYjaWuMtfKdeByx0AcaThA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.5.0.tgz",
"integrity": "sha512-OaYTqkW3GnuHxqsxxJ6KypIKd5Uw7bFiQJZRyNi1jbMJnK3Hc/DR4KwB6KJj6PBRkJJoaNwzMNv9vtTk87JhOg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.4.0.tgz",
"integrity": "sha512-nhlNoBdhKuwiLMx6GrybPT3SFILm5Gij2YBdPEPFlYNFAXUJWX6QRgvi/lwVoadaQEFsizohs6aFRMqsXI2ewA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.5.0.tgz",
"integrity": "sha512-pVn8btYUiYrjonhMAO0yG8lm7RApzy2L4RC7Td/mC/qFkyf6vRbGyZozoA94+w6D2Y2GRqpMoCWcwx/EUOzyoQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/visitor-keys": "5.4.0",
"@typescript-eslint/types": "5.5.0",
"@typescript-eslint/visitor-keys": "5.5.0",
"debug": "^4.3.2",
"globby": "^11.0.4",
"is-glob": "^4.0.3",
@@ -16137,12 +16142,12 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.4.0.tgz",
"integrity": "sha512-PVbax7MeE7tdLfW5SA0fs8NGVVr+buMPrcj+CWYWPXsZCH8qZ1THufDzbXm1xrZ2b2PA1iENJ0sRq5fuUtvsJg==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.5.0.tgz",
"integrity": "sha512-4GzJ1kRtsWzHhdM40tv0ZKHNSbkDhF0Woi/TDwVJX6UICwJItvP7ZTXbjTkCdrors7ww0sYe0t+cIKDAJwZ7Kw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.4.0",
"@typescript-eslint/types": "5.5.0",
"eslint-visitor-keys": "^3.0.0"
}
},
@@ -22606,6 +22611,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -22615,6 +22621,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
@@ -23039,6 +23046,7 @@
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
"integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
"peer": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
@@ -23553,9 +23561,9 @@
}
},
"swagger-ui-dist": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.1.tgz",
"integrity": "sha512-xvPMEpOCbd5S9Z1pJT/L/K/C2yAX89ZWJDxEECkVW1dENsKFLbu7sRY7b6UBqLXuTUfdM5owlgPrpwr24CRonA=="
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.1.3.tgz",
"integrity": "sha512-WvfPSfAAMlE/sKS6YkW47nX/hA7StmhYnAHc6wWCXNL0oclwLj6UXv0hQCkLnDgvebi0MEV40SJJpVjKUgH1IQ=="
},
"swap-case": {
"version": "2.0.2",

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "3.0.0-dev.0",
"version": "3.0.0-dev.5",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -16,6 +16,8 @@
"test": "npm run lint && jest",
"jest": "jest",
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
"clean": "node ./scripts/clean.js",
"rebuild": "npm run clean && npm run build",
"build-viewer": "npm run build-tsc && npm run build-extra && npm run build-webpack-viewer",
"build-tsc": "concurrently \"tsc --incremental\" \"tsc --build tsconfig.commonjs.json --incremental\"",
"build-extra": "cpx \"src/**/*.{scss,html,ico}\" lib/",
@@ -36,7 +38,7 @@
"volume-server-test": "node lib/commonjs/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
"plugin-state": "node lib/commonjs/servers/plugin-state/index.js --working-folder ./build/state --port 1339",
"preversion": "npm run test",
"version": "npm run build",
"version": "npm run rebuild && cpx .npmignore lib/",
"postversion": "git push && git push --tags"
},
"files": [
@@ -98,8 +100,8 @@
"@types/cors": "^2.8.12",
"@types/gl": "^4.1.0",
"@types/jest": "^27.0.3",
"@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0",
"@typescript-eslint/eslint-plugin": "^5.5.0",
"@typescript-eslint/parser": "^5.5.0",
"benchmark": "^2.1.4",
"concurrently": "^6.4.0",
"cpx2": "^4.0.0",
@@ -144,14 +146,16 @@
"immer": "^9.0.7",
"immutable": "^3.8.2",
"node-fetch": "^2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rxjs": "^7.4.0",
"swagger-ui-dist": "^4.1.1",
"tslib": "^2.3.1",
"util.promisify": "^1.1.1",
"xhr2": "^0.2.1"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"optionalDependencies": {
"gl": "^4.9.2"
}

41
scripts/clean.js Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Michal Malý <malym@ibt.cas.cz>
*/
const fs = require('fs');
const path = require('path');
function removeDir(dirPath) {
for (const ent of fs.readdirSync(dirPath)) {
const entryPath = path.join(dirPath, ent);
remove(entryPath);
}
fs.rmdirSync(dirPath);
}
function remove(entryPath) {
const st = fs.statSync(entryPath);
if (st.isDirectory())
removeDir(entryPath);
else
fs.unlinkSync(entryPath);
}
const toClean = [
path.resolve(__dirname, '../build'),
path.resolve(__dirname, '../lib'),
path.resolve(__dirname, '../tsconfig.tsbuildinfo'),
];
toClean.forEach(ph => {
if (fs.existsSync(ph)) {
try {
remove(ph);
} catch (err) {
console.warn(`Cleanup failed: ${err}`);
}
}
});

View File

@@ -14,6 +14,9 @@ const buildDir = path.resolve(__dirname, '../build/');
const deployDir = path.resolve(buildDir, 'deploy/');
const localPath = path.resolve(deployDir, 'molstar.github.io/');
const analyticsTag = /<!-- __MOLSTAR_ANALYTICS__ -->/g;
const analyticsCode = `<!-- Cloudflare Web Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "c414cbae2d284ea995171a81e4a3e721"}'></script><!-- End Cloudflare Web Analytics -->`;
function log(command, stdout, stderr) {
if (command) {
console.log('\n###', command);
@@ -22,11 +25,18 @@ function log(command, stdout, stderr) {
}
}
function addAnalytics(path) {
const data = fs.readFileSync(path, 'utf8');
const result = data.replace(analyticsTag, analyticsCode);
fs.writeFileSync(path, result, 'utf8');
}
function copyViewer() {
console.log('\n###', 'copy viewer files');
const viewerBuildPath = path.resolve(buildDir, '../build/viewer/');
const viewerDeployPath = path.resolve(localPath, 'viewer/');
fse.copySync(viewerBuildPath, viewerDeployPath, { overwrite: true });
addAnalytics(path.resolve(viewerDeployPath, 'index.html'));
}
if (!fs.existsSync(localPath)) {

View File

@@ -21,6 +21,7 @@ import { PluginContext } from '../../mol-plugin/context';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateObjectRef } from '../../mol-state';
import { Color } from '../../mol-util/color';
import { Material } from '../../mol-util/material';
function shinyStyle(plugin: PluginContext) {
return PluginCommands.Canvas3D.SetSettings(plugin, { settings: {
@@ -50,7 +51,8 @@ function occlusionStyle(plugin: PluginContext) {
} },
outline: { name: 'on', params: {
scale: 1.0,
threshold: 0.33
threshold: 0.33,
color: Color(0x0000),
} }
}
} });
@@ -75,7 +77,7 @@ const PresetParams = {
...StructureRepresentationPresetProvider.CommonParams,
};
const CustomMaterial = Material({ roughness: 0.2, metalness: 0 });
export const StructurePreset = StructureRepresentationPresetProvider({
id: 'preset-structure',
@@ -92,8 +94,8 @@ export const StructurePreset = StructureRepresentationPresetProvider({
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, roughness: 0.2, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, roughness: 0.2 }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.35 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, material: CustomMaterial }, color: 'chain-id', colorParams: { palette: (plugin.customState as any).colorPalette } }, { tag: 'polymer' }),
};
await update.commit({ revertOnError: true });
@@ -147,8 +149,8 @@ const SurfacePreset = StructureRepresentationPresetProvider({
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, roughness: 0.2, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, roughness: 0.2, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
polymer: builder.buildRepresentation(update, components.polymer, { type: 'molecular-surface', typeParams: { ...typeParams, material: CustomMaterial, quality: 'custom', resolution: 0.5, doubleSided: true }, color: 'partial-charge' }, { tag: 'polymer' }),
};
await update.commit({ revertOnError: true });
@@ -175,8 +177,8 @@ const PocketPreset = StructureRepresentationPresetProvider({
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, roughness: 0.2, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, roughness: 0.2, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.26 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
surroundings: builder.buildRepresentation(update, components.surroundings, { type: 'molecular-surface', typeParams: { ...typeParams, material: CustomMaterial, includeParent: true, quality: 'custom', resolution: 0.2, doubleSided: true }, color: 'partial-charge' }, { tag: 'surroundings' }),
};
await update.commit({ revertOnError: true });
@@ -204,10 +206,10 @@ const InteractionsPreset = StructureRepresentationPresetProvider({
const { update, builder, typeParams } = StructureRepresentationPresetProvider.reprBuilder(plugin, params);
const representations = {
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, roughness: 0.2, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, roughness: 0.2, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, roughness: 0.2 }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, roughness: 0.2, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.3 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ligand' }),
ballAndStick: builder.buildRepresentation(update, components.surroundings, { type: 'ball-and-stick', typeParams: { ...typeParams, material: CustomMaterial, sizeFactor: 0.1, sizeAspectRatio: 1 }, color: 'element-symbol', colorParams: { carbonColor: { name: 'element-symbol', params: {} } } }, { tag: 'ball-and-stick' }),
interactions: builder.buildRepresentation(update, components.interactions, { type: InteractionsRepresentationProvider, typeParams: { ...typeParams, material: CustomMaterial }, color: InteractionTypeColorThemeProvider }, { tag: 'interactions' }),
label: builder.buildRepresentation(update, components.surroundings, { type: 'label', typeParams: { ...typeParams, material: CustomMaterial, background: false, borderWidth: 0.1 }, color: 'uniform', colorParams: { value: Color(0x000000) } }, { tag: 'label' }),
};
await update.commit({ revertOnError: true });

View File

@@ -91,5 +91,6 @@
var emdb = getParam('emdb', '[^&]+').trim();
if (emdb) viewer.loadEmdb(emdb);
</script>
<!-- __MOLSTAR_ANALYTICS__ -->
</body>
</html>

View File

@@ -11,6 +11,7 @@ import { PluginUIContext } from '../../mol-plugin-ui/context';
import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
import { PluginCommands } from '../../mol-plugin/commands';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import './index.html';
require('mol-plugin-ui/skin/light.scss');
@@ -26,7 +27,7 @@ const Canvas3DPresets = {
},
postprocessing: {
occlusion: { name: 'on', params: { samples: 32, radius: 6, bias: 1.4, blurKernelSize: 15 } },
outline: { name: 'on', params: { scale: 1, threshold: 0.1 } }
outline: { name: 'on', params: { scale: 1, threshold: 0.1, color: Color(0x000000) } }
},
renderer: {
style: { name: 'flat', params: {} }

View File

@@ -145,7 +145,7 @@ export class ObjExporter extends MeshExporter<ObjData> {
const color = ObjExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
Color.toArray(color, quantizedColors, i);
}
ObjExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
ObjExporter.quantizeColors(quantizedColors, vertexCount);
// face
for (let i = 0; i < drawCount; i += 3) {

View File

@@ -149,7 +149,7 @@ def Material "material${materialKey}"
const color = UsdzExporter.getColor(v, geoData, interpolatedColors, interpolatedOverpaint);
Color.toArray(color, quantizedColors, i);
}
UsdzExporter.quantizeColors(quantizedColors, mesh!.vertexCount);
UsdzExporter.quantizeColors(quantizedColors, vertexCount);
// material
const faceIndicesByMaterial = new Map<number, number[]>();

View File

@@ -125,17 +125,20 @@ export class Mp4Controls extends PluginComponent {
this.subscribe(this.plugin.canvas3d?.resized!, () => this.syncInfo());
this.subscribe(this.plugin.helpers.viewportScreenshot?.events.previewed!, () => this.syncInfo());
this.subscribe(this.plugin.behaviors.state.isBusy, b => {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
});
this.subscribe(this.plugin.behaviors.state.isBusy, b => this.updateCanApply(b));
this.subscribe(this.plugin.managers.snapshot.events.changed, b => this.updateCanApply(b));
this.sync();
this.syncInfo();
}
private updateCanApply(b?: any) {
const anim = this.current;
if (!b && anim) {
this.behaviors.canApply.next(anim.anim.canApply?.(this.plugin) ?? { canApply: true });
}
}
constructor(private plugin: PluginContext) {
super();

View File

@@ -514,7 +514,7 @@ namespace Canvas3D {
if (camera.transition.inTransition || nextCameraResetSnapshot) return false;
let cameraSphereOverlapsNone = true;
let cameraSphereOverlapsNone = true, isEmpty = true;
Sphere3D.set(cameraSphere, camera.state.target, camera.state.radius);
// check if any renderable has moved outside of the old bounding sphere
@@ -525,12 +525,13 @@ namespace Canvas3D {
const b = r.values.boundingSphere.ref.value;
if (!b.radius) continue;
isEmpty = false;
const cameraDist = Vec3.distance(cameraSphere.center, b.center);
if ((cameraDist > cameraSphere.radius || cameraDist > b.radius || b.radius > camera.state.radiusMax) && !Sphere3D.includes(oldBoundingSphereVisible, b)) return true;
if (Sphere3D.overlaps(cameraSphere, b)) cameraSphereOverlapsNone = false;
}
return cameraSphereOverlapsNone;
return cameraSphereOverlapsNone || (!isEmpty && cameraSphere.radius <= 0.1);
}
const sceneCommitTimeoutMs = 250;

View File

@@ -193,6 +193,7 @@ const PostprocessingSchema = {
uFogNear: UniformSpec('f'),
uFogFar: UniformSpec('f'),
uFogColor: UniformSpec('v3'),
uOutlineColor: UniformSpec('v3'),
uTransparentBackground: UniformSpec('b'),
uMaxPossibleViewZDiff: UniformSpec('f'),
@@ -220,6 +221,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uFogNear: ValueCell.create(10000),
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
uOutlineColor: ValueCell.create(Vec3.create(0, 0, 0)),
uTransparentBackground: ValueCell.create(false),
uMaxPossibleViewZDiff: ValueCell.create(0.5),
@@ -252,6 +254,7 @@ export const PostprocessingParams = {
on: PD.Group({
scale: PD.Numeric(1, { min: 1, max: 5, step: 1 }),
threshold: PD.Numeric(0.33, { min: 0.01, max: 1, step: 0.01 }),
color: PD.Color(Color(0x000000)),
}),
off: PD.Group({})
}, { cycle: true, description: 'Draw outline around 3D objects' }),
@@ -446,6 +449,8 @@ export class PostprocessingPass {
ValueCell.updateIfChanged(this.outlinesRenderable.values.uFar, camera.far);
ValueCell.updateIfChanged(this.outlinesRenderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
ValueCell.update(this.renderable.values.uOutlineColor, Color.toVec3Normalized(this.renderable.values.uOutlineColor.ref.value, props.outline.params.color));
ValueCell.updateIfChanged(this.renderable.values.uMaxPossibleViewZDiff, maxPossibleViewZDiff);
if (this.renderable.values.dOutlineScale.ref.value !== outlineScale) { needsUpdateMain = true; }
ValueCell.updateIfChanged(this.renderable.values.dOutlineScale, outlineScale);

View File

@@ -97,24 +97,24 @@ export namespace BaseGeometry {
}
export function createValues(props: PD.Values<Params>, counts: Counts) {
const { metalness, roughness } = Material.toObjectNormalized(props.material);
return {
alpha: ValueCell.create(props.alpha),
uAlpha: ValueCell.create(props.alpha),
uVertexCount: ValueCell.create(counts.vertexCount),
uGroupCount: ValueCell.create(counts.groupCount),
drawCount: ValueCell.create(counts.drawCount),
uMetalness: ValueCell.create(metalness),
uRoughness: ValueCell.create(roughness),
uMetalness: ValueCell.create(props.material.metalness),
uRoughness: ValueCell.create(props.material.roughness),
uBumpiness: ValueCell.create(props.material.bumpiness),
dLightCount: ValueCell.create(1),
};
}
export function updateValues(values: BaseValues, props: PD.Values<Params>) {
const { metalness, roughness } = Material.toObjectNormalized(props.material);
ValueCell.updateIfChanged(values.alpha, props.alpha); // `uAlpha` is set in renderable.render
ValueCell.updateIfChanged(values.uMetalness, metalness);
ValueCell.updateIfChanged(values.uRoughness, roughness);
ValueCell.updateIfChanged(values.uMetalness, props.material.metalness);
ValueCell.updateIfChanged(values.uRoughness, props.material.roughness);
ValueCell.updateIfChanged(values.uBumpiness, props.material.bumpiness);
}
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {

View File

@@ -157,6 +157,8 @@ export namespace Cylinders {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -237,6 +239,8 @@ export namespace Cylinders {
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
}
@@ -252,6 +256,8 @@ export namespace Cylinders {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {

View File

@@ -376,7 +376,7 @@ export function applyMeshSubstanceSmoothing(values: MeshValues, resolution: numb
colorType: values.dSubstanceType.ref.value,
boundingSphere: values.boundingSphere.ref.value,
invariantBoundingSphere: values.invariantBoundingSphere.ref.value,
itemSize: 3
itemSize: 4
}, resolution, stride, webgl, colorTexture);
if (smoothingData.kind === 'volume') {
ValueCell.updateIfChanged(values.dSubstanceType, smoothingData.type);

View File

@@ -625,6 +625,8 @@ export namespace Mesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -696,6 +698,8 @@ export namespace Mesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
meta: ValueCell.create(mesh.meta),
};
@@ -714,6 +718,8 @@ export namespace Mesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {

View File

@@ -129,6 +129,8 @@ export namespace Spheres {
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -204,6 +206,8 @@ export namespace Spheres {
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
};
}
@@ -219,6 +223,8 @@ export namespace Spheres {
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {

View File

@@ -23,14 +23,14 @@ export type SubstanceData = {
export function applySubstanceMaterial(array: Uint8Array, start: number, end: number, material: Material) {
for (let i = start; i < end; ++i) {
Material.toArray(material, array, i * 3);
array[i * 3 + 2] = 255;
Material.toArray(material, array, i * 4);
array[i * 4 + 3] = 255;
}
return true;
}
export function clearSubstance(array: Uint8Array, start: number, end: number) {
array.fill(0, start * 3, end * 3);
array.fill(0, start * 4, end * 4);
return true;
}

View File

@@ -510,10 +510,10 @@ export function applyTextureMeshSubstanceSmoothing(values: TextureMeshValues, re
stride *= 3; // triple because TextureMesh is never indexed (no elements buffer)
if (!webgl.namedTextures[ColorSmoothingRgbName]) {
webgl.namedTextures[ColorSmoothingRgbName] = webgl.resources.texture('image-uint8', 'rgb', 'ubyte', 'nearest');
if (!webgl.namedTextures[ColorSmoothingRgbaName]) {
webgl.namedTextures[ColorSmoothingRgbaName] = webgl.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
}
const colorData = webgl.namedTextures[ColorSmoothingRgbName];
const colorData = webgl.namedTextures[ColorSmoothingRgbaName];
colorData.load(values.tSubstance.ref.value);
const smoothingData = calcTextureMeshColorSmoothing({

View File

@@ -113,6 +113,8 @@ export namespace TextureMesh {
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, 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),
};
export type Params = typeof Params
@@ -168,6 +170,8 @@ export namespace TextureMesh {
dFlipSided: ValueCell.create(props.flipSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
uBumpFrequency: ValueCell.create(props.bumpFrequency),
uBumpAmplitude: ValueCell.create(props.bumpAmplitude),
dGeoTexture: ValueCell.create(true),
meta: ValueCell.create(textureMesh.meta),
@@ -187,6 +191,8 @@ export namespace TextureMesh {
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);
ValueCell.updateIfChanged(values.uBumpAmplitude, props.bumpAmplitude);
}
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { CylindersShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -26,6 +26,8 @@ export const CylindersSchema = {
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};
export type CylindersSchema = typeof CylindersSchema
export type CylindersValues = Values<CylindersSchema>

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec, UniformSpec } from './schema';
import { MeshShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -22,6 +22,8 @@ export const MeshSchema = {
dFlipSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')
} as const;
export type MeshSchema = typeof MeshSchema

View File

@@ -243,12 +243,12 @@ export type TransparencyValues = Values<TransparencySchema>
export const SubstanceSchema = {
uSubstanceTexDim: UniformSpec('v2'),
tSubstance: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
tSubstance: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
dSubstance: DefineSpec('boolean'),
uSubstanceGridDim: UniformSpec('v3'),
uSubstanceGridTransform: UniformSpec('v4'),
tSubstanceGrid: TextureSpec('texture', 'rgb', 'ubyte', 'linear'),
tSubstanceGrid: TextureSpec('texture', 'rgba', 'ubyte', 'linear'),
dSubstanceType: DefineSpec('string', ['groupInstance', 'volumeInstance']),
} as const;
export type SubstanceSchema = typeof SubstanceSchema
@@ -288,6 +288,7 @@ export const BaseSchema = {
uAlpha: UniformSpec('f', 'material'),
uMetalness: UniformSpec('f', 'material'),
uRoughness: UniformSpec('f', 'material'),
uBumpiness: UniformSpec('f', 'material'),
uVertexCount: UniformSpec('i'),
uInstanceCount: UniformSpec('i'),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -7,7 +7,7 @@
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
import { SpheresShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
@@ -23,6 +23,8 @@ export const SpheresSchema = {
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
};
export type SpheresSchema = typeof SpheresSchema
export type SpheresValues = Values<SpheresSchema>

View File

@@ -24,6 +24,8 @@ export const TextureMeshSchema = {
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
dGeoTexture: DefineSpec('boolean'),
uBumpFrequency: UniformSpec('f'),
uBumpAmplitude: UniformSpec('f'),
meta: ValueSpec('unknown')
};
export type TextureMeshSchema = typeof TextureMeshSchema

View File

@@ -8,17 +8,16 @@
*/
export const apply_light_color = `
// inputs
// - vec4 material
// - vec3 vViewPosition
// - vec3 normal
// - float uMetalness
// - float uRoughness
// - vec3 uLightColor
// - vec3 uAmbientColor
// outputs
// - sets gl_FragColor
#ifdef bumpEnabled
if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
#ifdef enabledFragDepth
if (!any(isNaN(bumpNormal))) normal = bumpNormal;
#else
normal = bumpNormal;
#endif
}
#endif
vec4 color = material;
@@ -26,7 +25,13 @@ ReflectedLight reflectedLight = ReflectedLight(vec3(0.0), vec3(0.0), vec3(0.0),
PhysicalMaterial physicalMaterial;
physicalMaterial.diffuseColor = color.rgb * (1.0 - metalness);
physicalMaterial.roughness = max(roughness, 0.0525);
#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;

View File

@@ -44,16 +44,16 @@ export const assign_color_varying = `
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance)
vSubstance = readFromTexture(tSubstance, aInstance * float(uGroupCount) + group, uSubstanceTexDim).rgb;
vSubstance = readFromTexture(tSubstance, aInstance * float(uGroupCount) + group, uSubstanceTexDim);
#elif defined(dSubstanceType_vertexInstance)
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim).rgb;
vSubstance = readFromTexture(tSubstance, int(aInstance) * uVertexCount + VertexID, uSubstanceTexDim);
#elif defined(dSubstanceType_volumeInstance)
vec3 sgridPos = (uSubstanceGridTransform.w * (vModelPosition - uSubstanceGridTransform.xyz)) / uSubstanceGridDim;
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim).rgb;
vSubstance = texture3dFrom2dLinear(tSubstanceGrid, sgridPos, uSubstanceGridDim, uSubstanceTexDim);
#endif
// pre-mix to avoid artifacts due to empty substance
vSubstance.rg = mix(vec2(uMetalness, uRoughness), vSubstance.rg, vSubstance.b);
vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
#if defined(dRenderVariant_pickObject)

View File

@@ -23,9 +23,11 @@ export const assign_material_color = `
float metalness = uMetalness;
float roughness = uRoughness;
float bumpiness = uBumpiness;
#ifdef dSubstance
metalness = mix(metalness, vSubstance.r, vSubstance.b);
roughness = mix(roughness, vSubstance.g, vSubstance.b);
metalness = mix(metalness, vSubstance.r, vSubstance.a);
roughness = mix(roughness, vSubstance.g, vSubstance.a);
bumpiness = mix(bumpiness, vSubstance.b, vSubstance.a);
#endif
#elif defined(dRenderVariant_pick)
vec4 material = vColor;

View File

@@ -1,6 +1,11 @@
export const color_frag_params = `
uniform float uMetalness;
uniform float uRoughness;
uniform float uBumpiness;
#ifdef bumpEnabled
uniform float uBumpFrequency;
uniform float uBumpAmplitude;
#endif
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
@@ -14,7 +19,7 @@ uniform float uRoughness;
#endif
#ifdef dSubstance
varying vec3 vSubstance;
varying vec4 vSubstance;
#endif
#elif defined(dRenderVariant_pick)
#if __VERSION__ == 100

View File

@@ -1,6 +1,7 @@
export const color_vert_params = `
uniform float uMetalness;
uniform float uRoughness;
uniform float uBumpiness;
#if defined(dRenderVariant_color)
#if defined(dColorType_uniform)
@@ -36,11 +37,11 @@ uniform float uRoughness;
#ifdef dSubstance
#if defined(dSubstanceType_groupInstance) || defined(dSubstanceType_vertexInstance)
varying vec3 vSubstance;
varying vec4 vSubstance;
uniform vec2 uSubstanceTexDim;
uniform sampler2D tSubstance;
#elif defined(dSubstanceType_volumeInstance)
varying vec3 vSubstance;
varying vec4 vSubstance;
uniform vec2 uSubstanceTexDim;
uniform vec3 uSubstanceGridDim;
uniform vec4 uSubstanceGridTransform;

View File

@@ -76,4 +76,49 @@ 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;
}
// "Bump Mapping Unparametrized Surfaces on the GPU" Morten S. Mikkelsen
// https://mmikk.github.io/papers3d/mm_sfgrad_bump.pdf
vec3 perturbNormal(in vec3 position, in vec3 normal, in float height, in float scale) {
vec3 sigmaS = dFdx(position);
vec3 sigmaT = dFdy(position);
vec3 r1 = cross(sigmaT, normal);
vec3 r2 = cross(normal, sigmaS);
float det = dot(sigmaS, r1);
float bs = dFdx(height);
float bt = dFdy(height);
vec3 surfGrad = sign(det) * (bs * r1 + bt * r2);
return normalize(abs(det) * normal - scale * surfGrad);
}
float hash(in float h) {
return fract(sin(h) * 43758.5453123);
}
float noise(in vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 157.0 + 113.0 * p.z;
return mix(
mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 157.0), hash(n + 158.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 270.0), hash(n + 271.0), f.x), f.y), f.z);
}
float fbm(in vec3 p) {
float f = 0.0;
f += 0.5 * noise(p);
p *= 2.01;
f += 0.25 * noise(p);
p *= 2.02;
f += 0.125 * noise(p);
return f;
}
`;

View File

@@ -214,6 +214,9 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
a31 * b01 - a30 * b03 - a32 * b00,
a20 * b03 - a21 * b01 + a22 * b00) / det;
}
#define isNaN(x) ( (x) != (x) )
#define isInf(x) ( (x) == (x)+1. )
#else
#define transpose2(m) transpose(m)
#define transpose3(m) transpose(m)
@@ -222,5 +225,8 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
#define inverse2(m) inverse(m)
#define inverse3(m) inverse(m)
#define inverse4(m) inverse(m)
#define isNaN isnan
#define isInf isinf
#endif
`;

View File

@@ -8,8 +8,10 @@
*/
export const light_frag_params = `
uniform vec3 uLightDirection[dLightCount];
uniform vec3 uLightColor[dLightCount];
#if dLightCount != 0
uniform vec3 uLightDirection[dLightCount];
uniform vec3 uLightColor[dLightCount];
#endif
uniform vec3 uAmbientColor;
struct PhysicalMaterial {

View File

@@ -8,6 +8,8 @@ export const cylinders_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
uniform mat4 uView;
varying mat4 vTransform;
@@ -18,6 +20,7 @@ varying float vCap;
uniform vec3 uCameraDir;
uniform vec3 uCameraPosition;
uniform mat4 uInvView;
#include common
#include common_frag_params
@@ -108,7 +111,8 @@ void main() {
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
gl_FragDepthEXT = calcDepth(vViewPosition);
// bugfix (mac only?)
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;

View File

@@ -8,6 +8,8 @@ export const mesh_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
#include common
#include common_frag_params
#include color_frag_params

View File

@@ -21,6 +21,7 @@ uniform float uFar;
uniform float uFogNear;
uniform float uFogFar;
uniform vec3 uFogColor;
uniform vec3 uOutlineColor;
uniform bool uTransparentBackground;
uniform float uOcclusionBias;
@@ -116,7 +117,7 @@ void main(void) {
float outline = getOutline(coords, closestTexel);
if (outline == 0.0) {
color.rgb *= outline;
color.rgb = mix(uOutlineColor, color.rgb, outline);
viewDist = abs(getViewZ(closestTexel));
fogFactor = smoothstep(uFogNear, uFogFar, viewDist);
if (!uTransparentBackground) {

View File

@@ -8,12 +8,16 @@ export const spheres_frag = `
precision highp float;
precision highp int;
#define bumpEnabled
#include common
#include common_frag_params
#include color_frag_params
#include light_frag_params
#include common_clip
uniform mat4 uInvView;
varying float vRadius;
varying float vRadiusSq;
varying vec3 vPoint;
@@ -73,7 +77,8 @@ void main(void){
gl_FragDepthEXT = 0.0 + (0.0000001 / vRadius);
}
// bugfix (mac only?)
vec3 vModelPosition = (uInvView * vec4(vViewPosition, 1.0)).xyz;
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;

View File

@@ -283,8 +283,10 @@ export namespace Model {
export function isFromPdbArchive(model: Model): boolean {
if (!MmcifFormat.is(model.sourceData)) return false;
const { db } = model.sourceData.data;
for (let i = 0, il = db.database_2.database_id.rowCount; i < il; ++i) {
if (db.database_2.database_id.value(i) === 'PDB') return true;
}
return (
db.database_2.database_id.isDefined ||
// 4 character PDB id
model.entryId.match(/^[1-9][a-z0-9]{3,3}$/i) !== null ||
// long PDB id

View File

@@ -18,7 +18,7 @@ import { Material } from '../../mol-util/material';
type SubstanceEachReprCallback = (update: StateBuilder.Root, repr: StateObjectCell<PluginStateObject.Molecule.Structure.Representation3D, StateTransform<typeof StateTransforms.Representation.StructureRepresentation3D>>, substance?: StateObjectCell<any, StateTransform<typeof StateTransforms.Representation.SubstanceStructureRepresentation3DFromBundle>>) => Promise<void>
const SubstanceManagerTag = 'substance-controls';
export async function setStructureSubstance(plugin: PluginContext, components: StructureComponentRef[], material: Material | -1, lociGetter: (structure: Structure) => Promise<StructureElement.Loci | EmptyLoci>, types?: string[]) {
export async function setStructureSubstance(plugin: PluginContext, components: StructureComponentRef[], material: Material | undefined, lociGetter: (structure: Structure) => Promise<StructureElement.Loci | EmptyLoci>, types?: string[]) {
await eachRepr(plugin, components, async (update, repr, substanceCell) => {
if (types && types.length > 0 && !types.includes(repr.params!.values.type.name)) return;
@@ -30,8 +30,8 @@ export async function setStructureSubstance(plugin: PluginContext, components: S
const layer = {
bundle: StructureElement.Bundle.fromLoci(loci),
material: material === -1 ? Material(0) : material,
clear: material === -1
material: material ?? Material(),
clear: !material
};
if (substanceCell) {

View File

@@ -388,7 +388,7 @@ class StructureComponentManager extends StatefulPluginComponent<StructureCompone
const p = params.action.params;
await setStructureSubstance(this.plugin, s.components, p.material, getLoci, params.representations);
} else if (params.action.name === 'resetMaterial') {
await setStructureSubstance(this.plugin, s.components, -1, getLoci, params.representations);
await setStructureSubstance(this.plugin, s.components, void 0, getLoci, params.representations);
} else if (params.action.name === 'clipping') {
const p = params.action.params;
await setStructureClipping(this.plugin, s.components, Clipping.Groups.fromNames(p.excludeGroups), getLoci, params.representations);

View File

@@ -547,7 +547,7 @@ const SubstanceStructureRepresentation3DFromScript = PluginStateTransform.BuiltI
}, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
defaultValue: [{
script: Script('(sel.atom.all)', 'mol-script'),
material: Material.fromNormalized(0, 1),
material: Material({ roughness: 1 }),
clear: false
}]
}),
@@ -604,7 +604,7 @@ const SubstanceStructureRepresentation3DFromBundle = PluginStateTransform.BuiltI
}, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
defaultValue: [{
bundle: StructureElement.Bundle.Empty,
material: Material.fromNormalized(0, 1),
material: Material({ roughness: 1 }),
clear: false
}],
isHidden: true

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -7,7 +7,9 @@
import * as React from 'react';
import { Mat4, Vec2, Vec3 } from '../../mol-math/linear-algebra';
import { Asset } from '../../mol-util/assets';
import { Color } from '../../mol-util/color';
import { ColorListEntry } from '../../mol-util/color/color';
import { ColorListName, ColorListOptions, ColorListOptionsScale, ColorListOptionsSet, getColorListFromName } from '../../mol-util/color/lists';
import { Legend as LegendData } from '../../mol-util/legend';
import { memoize1, memoizeLatest } from '../../mol-util/memoize';
@@ -16,16 +18,14 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { ParamMapping } from '../../mol-util/param-mapping';
import { camelCaseToWords } from '../../mol-util/string';
import { PluginUIComponent } from '../base';
import { PluginUIContext } from '../context';
import { ActionMenu } from './action-menu';
import { ColorOptions, ColorValueOption, CombinedColorControl } from './color';
import { Button, ControlGroup, ControlRow, ExpandGroup, IconButton, TextInput, ToggleButton } from './common';
import { Icon, HelpOutlineSvg, CheckSvg, ClearSvg, BookmarksOutlinedSvg, MoreHorizSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowDownwardSvg, ArrowUpwardSvg, DeleteOutlinedSvg } from './icons';
import { ArrowDownwardSvg, ArrowDropDownSvg, ArrowRightSvg, ArrowUpwardSvg, BookmarksOutlinedSvg, CheckSvg, ClearSvg, DeleteOutlinedSvg, HelpOutlineSvg, Icon, MoreHorizSvg } from './icons';
import { legendFor } from './legend';
import { LineGraphComponent } from './line-graph/line-graph-component';
import { Slider, Slider2 } from './slider';
import { Asset } from '../../mol-util/assets';
import { ColorListEntry } from '../../mol-util/color/color';
import { PluginUIContext } from '../context';
export type ParameterControlsCategoryFilter = string | null | (string | null)[]
@@ -1079,8 +1079,8 @@ export class MultiSelectControl extends React.PureComponent<ParamProps<PD.MultiS
}
}
export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean, showHelp: boolean }> {
state = { isExpanded: !!this.props.param.isExpanded, showHelp: false }
export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>> & { inMapped?: boolean }, { isExpanded: boolean, showPresets: boolean, showHelp: boolean }> {
state = { isExpanded: !!this.props.param.isExpanded, showPresets: false, showHelp: false }
change(value: any) {
this.props.onChange({ name: this.props.name, param: this.props.param, value });
@@ -1091,6 +1091,45 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
}
toggleExpanded = () => this.setState({ isExpanded: !this.state.isExpanded });
toggleShowPresets = () => this.setState({ showPresets: !this.state.showPresets });
presetItems = memoizeLatest((param: PD.Group<any>) => ActionMenu.createItemsFromSelectOptions(param.presets ?? []));
onSelectPreset: ActionMenu.OnSelect = item => {
this.setState({ showPresets: false });
this.change(item?.value);
}
pivotedPresets() {
if (!this.props.param.presets) return null;
const label = this.props.param.label || camelCaseToWords(this.props.name);
return <div className='msp-control-group-wrapper'>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleShowPresets}>
<Icon svg={BookmarksOutlinedSvg} />
{label} Presets
</button>
</div>
{this.state.showPresets && <ActionMenu items={this.presetItems(this.props.param)} onSelect={this.onSelectPreset} />}
</div>;
}
presets() {
if (!this.props.param.presets) return null;
return <>
<div className='msp-control-group-presets-wrapper'>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleShowPresets}>
<Icon svg={BookmarksOutlinedSvg} />
Presets
</button>
</div>
</div>
{this.state.showPresets && <ActionMenu items={this.presetItems(this.props.param)} onSelect={this.onSelectPreset} />}
</>;
}
pivoted() {
const key = this.props.param.pivot as string;
@@ -1116,6 +1155,7 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
{ctrl}
<IconButton svg={MoreHorizSvg} onClick={this.toggleExpanded} toggleState={this.state.isExpanded} title={`More Options`} />
<div className='msp-control-offset'>
{this.pivotedPresets()}
<ParameterControls params={filtered} onEnter={this.props.onEnter} values={this.props.value} onChange={this.onChangeParam} isDisabled={this.props.isDisabled} />
</div>
</div>;
@@ -1141,13 +1181,14 @@ export class GroupControl extends React.PureComponent<ParamProps<PD.Group<any>>
return controls;
}
return <div className='msp-control-group-wrapper'>
return <div className='msp-control-group-wrapper' style={{ position: 'relative' }}>
<div className='msp-control-group-header'>
<button className='msp-btn msp-form-control msp-btn-block' onClick={this.toggleExpanded}>
<Icon svg={this.state.isExpanded ? ArrowDropDownSvg : ArrowRightSvg} />
{label}
</button>
</div>
{this.presets()}
{this.state.isExpanded && <div className='msp-control-offset'>
{controls}
</div>}

View File

@@ -1,10 +1,10 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2020-21 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
interface Behavior<T> {
value: T;
@@ -16,26 +16,20 @@ export function useBehavior<T>(s: Behavior<T>): T;
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined;
// eslint-disable-next-line
export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
const [value, setValue] = useState(s?.value);
const [, next] = useState({});
const current = useRef<T>();
current.current = s?.value;
useEffect(() => {
if (!s) {
if (value !== void 0) setValue(void 0);
return;
}
let fst = true;
const sub = s.subscribe((v) => {
if (fst) {
fst = false;
if (v !== value) setValue(v);
} else setValue(v);
if (current.current !== v) next({});
});
return () => {
sub.unsubscribe();
};
// eslint-disable-next-line
return () => sub.unsubscribe();
}, [s]);
return value;
return s?.value;
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -18,6 +18,10 @@ import { Toasts } from './toast';
import { Viewport, ViewportControls } from './viewport';
import { PluginCommands } from '../mol-plugin/commands';
import { PluginUIContext } from './context';
import { OpenFiles } from '../mol-plugin-state/actions/file';
import { Asset } from '../mol-util/assets';
import { BehaviorSubject } from 'rxjs';
import { useBehavior } from './hooks/use-behavior';
export class Plugin extends React.Component<{ plugin: PluginUIContext }, {}> {
region(kind: 'left' | 'right' | 'bottom' | 'main', element: JSX.Element) {
@@ -102,22 +106,34 @@ class Layout extends PluginUIComponent {
onDrop = (ev: React.DragEvent<HTMLDivElement>) => {
ev.preventDefault();
let file: File | undefined | null;
const files: File[] = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file') continue;
file = ev.dataTransfer.items[i].getAsFile();
break;
const file = ev.dataTransfer.items[i].getAsFile();
if (file) files.push(file);
}
} else {
file = ev.dataTransfer.files[0];
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[0];
if (file) files.push(file);
}
}
if (!file) return;
const fn = file?.name.toLowerCase() || '';
if (fn.endsWith('molx') || fn.endsWith('molj')) {
PluginCommands.State.Snapshots.OpenFile(this.plugin, { file });
const sessions = files.filter(f => {
const fn = f.name.toLowerCase();
return fn.endsWith('.molx') || fn.endsWith('.molj');
});
if (sessions.length > 0) {
PluginCommands.State.Snapshots.OpenFile(this.plugin, { file: sessions[0] });
} else {
this.plugin.runTask(this.plugin.state.data.applyAction(OpenFiles, {
files: files.map(f => Asset.File(f)),
format: { name: 'auto', params: {} },
visuals: true
}));
}
}
@@ -125,13 +141,16 @@ class Layout extends PluginUIComponent {
ev.preventDefault();
}
private showDragOverlay = new BehaviorSubject(false);
onDragEnter = () => this.showDragOverlay.next(true);
render() {
const layout = this.plugin.layout.state;
const controls = this.plugin.spec.components?.controls || {};
const viewport = this.plugin.spec.components?.viewport?.view || DefaultViewport;
return <div className='msp-plugin' onDrop={this.onDrop} onDragOver={this.onDragOver}>
<div className={this.layoutClassName}>
return <div className='msp-plugin'>
<div className={this.layoutClassName} onDragEnter={this.onDragEnter}>
<div className={this.layoutVisibilityClassName}>
{this.region('main', viewport)}
{layout.showControls && controls.top !== 'none' && this.region('top', controls.top || SequenceView)}
@@ -140,11 +159,69 @@ class Layout extends PluginUIComponent {
{layout.showControls && controls.bottom !== 'none' && this.region('bottom', controls.bottom || Log)}
</div>
{!this.plugin.spec.components?.hideTaskOverlay && <OverlayTaskProgress />}
<DragOverlay plugin={this.plugin} showDragOverlay={this.showDragOverlay} />
</div>
</div>;
}
}
function dropFiles(ev: React.DragEvent<HTMLDivElement>, plugin: PluginUIContext, showDragOverlay: BehaviorSubject<boolean>) {
ev.preventDefault();
ev.stopPropagation();
showDragOverlay.next(false);
const files: File[] = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file') continue;
const file = ev.dataTransfer.items[i].getAsFile();
if (file) files.push(file);
}
} else {
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[0];
if (file) files.push(file);
}
}
const sessions = files.filter(f => {
const fn = f.name.toLowerCase();
return fn.endsWith('.molx') || fn.endsWith('.molj');
});
if (sessions.length > 0) {
PluginCommands.State.Snapshots.OpenFile(plugin, { file: sessions[0] });
} else {
plugin.runTask(plugin.state.data.applyAction(OpenFiles, {
files: files.map(f => Asset.File(f)),
format: { name: 'auto', params: {} },
visuals: true
}));
}
}
function DragOverlay({ plugin, showDragOverlay }: { plugin: PluginUIContext, showDragOverlay: BehaviorSubject<boolean> }) {
const show = useBehavior(showDragOverlay);
const preventDrag = (e: React.DragEvent) => {
e.dataTransfer.dropEffect = 'copy';
e.preventDefault();
e.stopPropagation();
};
return <div
className='msp-drag-drop-overlay'
style={{ display: show ? 'flex' : 'none' }}
onDragEnter={preventDrag}
onDragOver={preventDrag}
onDragLeave={() => showDragOverlay.next(false)}
onDrop={e => dropFiles(e, plugin, showDragOverlay)}
>
Upload File(s)
</div>;
}
export class ControlsWrapper extends PluginUIComponent {
render() {
const StructureTools = this.plugin.spec.components?.structureTools || DefaultStructureTools;

View File

@@ -529,6 +529,18 @@
}
}
.msp-control-group-presets-wrapper {
position: absolute;
right: 0;
top: 0;
.msp-control-group-header {
background: transparent;
}
button {
background: transparent !important;
}
}
.msp-parameter-matrix {
input {
flex: 1 1 auto;
@@ -614,4 +626,19 @@
.msp-list-unstyled {
padding-left: 0;
list-style: none;
}
.msp-drag-drop-overlay {
border: 12px dashed $font-color;
background: rgba(0, 0, 0, 0.36);
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
font-size: 48px;
font-weight: bold;
}

View File

@@ -20,12 +20,17 @@ export class PluginConfigItem<T = any> {
function item<T>(key: string, defaultValue?: T) { return new PluginConfigItem(key, defaultValue); }
function preferWebGl1() {
export function preferWebGl1() {
if (typeof navigator === 'undefined' || typeof window === 'undefined') return false;
// WebGL2 isn't working in MacOS 12.0.1 Safari 15.1 (but is working in Safari tech preview)
// WebGL2 isn't working in MacOS 12.0.1 Safari 15.1, 15.2. It is working in Safari 15.4 tech preview, so disabling all versions before that.
// prefer webgl 1 based on the userAgent substring
if (navigator.userAgent.indexOf('Version/15.1 Safari') > 0) {
const unpportedSafariVersions = [
'Version/15.1 Safari',
'Version/15.2 Safari',
'Version/15.3 Safari'
];
if (unpportedSafariVersions.some(v => navigator.userAgent.indexOf(v) > 0)) {
return true;
}

View File

@@ -20,6 +20,7 @@ import { merge } from 'rxjs';
import { PluginContext } from './context';
import { PluginComponent } from '../mol-plugin-state/component';
import { PluginConfig } from './config';
import { StructureComponentManager } from '../mol-plugin-state/manager/structure/component';
export { PluginState };
@@ -64,6 +65,9 @@ class PluginState extends PluginComponent {
canvas3d: p.canvas3d ? { props: this.plugin.canvas3d?.props } : void 0,
interactivity: p.interactivity ? { props: this.plugin.managers.interactivity.props } : void 0,
structureFocus: this.plugin.managers.structure.focus.getSnapshot(),
structureComponentManager: p.componentManager ? {
options: this.plugin.managers.structure.component.state.options
} : void 0,
durationInMs: p?.durationInMs
};
}
@@ -71,6 +75,8 @@ class PluginState extends PluginComponent {
async setSnapshot(snapshot: PluginState.Snapshot) {
await this.animation.stop();
// this needs to go 1st since these changes are already baked into the behavior and data state
if (snapshot.structureComponentManager?.options) await this.plugin.managers.structure.component.setOptions(snapshot.structureComponentManager?.options);
if (snapshot.behaviour) await this.plugin.runTask(this.behaviors.setSnapshot(snapshot.behaviour));
if (snapshot.data) await this.plugin.runTask(this.data.setSnapshot(snapshot.data));
if (snapshot.canvas3d?.props) {
@@ -139,6 +145,7 @@ namespace PluginState {
durationInMs: PD.Numeric(1500, { min: 100, max: 15000, step: 100 }, { label: 'Duration in ms' }),
data: PD.Boolean(true),
behavior: PD.Boolean(false),
componentManager: PD.Boolean(true),
animation: PD.Boolean(true),
startAnimation: PD.Boolean(false),
canvas3d: PD.Boolean(true),
@@ -172,6 +179,9 @@ namespace PluginState {
props?: InteractivityManager.Props
},
structureFocus?: StructureFocusSnapshot,
structureComponentManager?: {
options?: StructureComponentManager.Options
},
durationInMs?: number
}

View File

@@ -13,6 +13,7 @@ import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { PolymerBackboneSphereParams, PolymerBackboneSphereVisual } from '../visual/polymer-backbone-sphere';
import { PolymerGapParams, PolymerGapVisual } from '../visual/polymer-gap-cylinder';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const BackboneVisuals = {
'polymer-backbone-cylinder': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerBackboneCylinderParams>) => UnitsRepresentation('Polymer backbone cylinder', ctx, getParams, PolymerBackboneCylinderVisual),
@@ -25,7 +26,8 @@ export const BackboneParams = {
...PolymerBackboneCylinderParams,
...PolymerGapParams,
sizeAspectRatio: PD.Numeric(1, { min: 0.1, max: 3, step: 0.1 }),
visuals: PD.MultiSelect(['polymer-backbone-cylinder', 'polymer-backbone-sphere', 'polymer-gap'], PD.objectToOptions(BackboneVisuals))
visuals: PD.MultiSelect(['polymer-backbone-cylinder', 'polymer-backbone-sphere', 'polymer-gap'], PD.objectToOptions(BackboneVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type BackboneParams = typeof BackboneParams
export function getBackboneParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -15,6 +15,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { getUnitKindsParam } from '../params';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const BallAndStickVisuals = {
'element-sphere': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Element sphere', ctx, getParams, ElementSphereVisual),
@@ -31,7 +32,8 @@ export const BallAndStickParams = {
unitKinds: getUnitKindsParam(['atomic']),
sizeFactor: PD.Numeric(0.15, { min: 0.01, max: 10, step: 0.01 }),
sizeAspectRatio: PD.Numeric(2 / 3, { min: 0.01, max: 3, step: 0.01 }),
visuals: PD.MultiSelect(['element-sphere', 'intra-bond', 'inter-bond'], PD.objectToOptions(BallAndStickVisuals))
visuals: PD.MultiSelect(['element-sphere', 'intra-bond', 'inter-bond'], PD.objectToOptions(BallAndStickVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type BallAndStickParams = typeof BallAndStickParams
export function getBallAndStickParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -4,6 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { BaseGeometry } from '../../../mol-geo/geometry/base';
import { Structure, Model } from '../../../mol-model/structure';
import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
@@ -25,6 +26,7 @@ export const CarbohydrateParams = {
...CarbohydrateLinkParams,
...CarbohydrateTerminalLinkParams,
visuals: PD.MultiSelect(['carbohydrate-symbol', 'carbohydrate-link', 'carbohydrate-terminal-link'], PD.objectToOptions(CarbohydrateVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type CarbohydrateParams = typeof CarbohydrateParams
export function getCarbohydrateParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -18,6 +18,7 @@ import { PolymerTraceParams, PolymerTraceVisual } from '../visual/polymer-trace-
import { SecondaryStructureProvider } from '../../../mol-model-props/computed/secondary-structure';
import { CustomProperty } from '../../../mol-model-props/common/custom-property';
import { HelixOrientationProvider } from '../../../mol-model-props/computed/helix-orientation';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const CartoonVisuals = {
'polymer-trace': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTraceParams>) => UnitsRepresentation('Polymer trace mesh', ctx, getParams, PolymerTraceVisual),
@@ -35,6 +36,7 @@ export const CartoonParams = {
...PolymerDirectionParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
visuals: PD.MultiSelect(['polymer-trace', 'polymer-gap', 'nucleotide-block'], PD.objectToOptions(CartoonVisuals)),
bumpFrequency: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type CartoonParams = typeof CartoonParams

View File

@@ -14,6 +14,7 @@ import { AtomSiteAnisotrop } from '../../../mol-model-formats/structure/property
import { IntraUnitBondCylinderParams, IntraUnitBondCylinderVisual } from '../visual/bond-intra-unit-cylinder';
import { InterUnitBondCylinderVisual, InterUnitBondCylinderParams } from '../visual/bond-inter-unit-cylinder';
import { getUnitKindsParam } from '../params';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const EllipsoidVisuals = {
'ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, EllipsoidMeshParams>) => UnitsRepresentation('Ellipsoid Mesh', ctx, getParams, EllipsoidMeshVisual),
@@ -32,6 +33,7 @@ export const EllipsoidParams = {
sizeAspectRatio: PD.Numeric(0.1, { min: 0.01, max: 3, step: 0.01 }),
linkCap: PD.Boolean(true),
visuals: PD.MultiSelect(['ellipsoid-mesh', 'intra-bond', 'inter-bond'], PD.objectToOptions(EllipsoidVisuals)),
bumpFrequency: PD.Numeric(0, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type EllipsoidParams = typeof EllipsoidParams
export function getEllipsoidParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const GaussianSurfaceVisuals = {
'gaussian-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, GaussianSurfaceMeshParams>) => UnitsRepresentation('Gaussian surface mesh', ctx, getParams, GaussianSurfaceVisual),
@@ -23,6 +24,7 @@ export const GaussianSurfaceParams = {
...GaussianSurfaceMeshParams,
...GaussianWireframeParams,
visuals: PD.MultiSelect(['gaussian-surface-mesh'], PD.objectToOptions(GaussianSurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type GaussianSurfaceParams = typeof GaussianSurfaceParams
export function getGaussianSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { MolecularSurfaceWireframeParams, MolecularSurfaceWireframeVisual } from '../visual/molecular-surface-wireframe';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const MolecularSurfaceVisuals = {
'molecular-surface-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MolecularSurfaceMeshParams>) => UnitsRepresentation('Molecular surface mesh', ctx, getParams, MolecularSurfaceMeshVisual),
@@ -22,6 +23,7 @@ export const MolecularSurfaceParams = {
...MolecularSurfaceMeshParams,
...MolecularSurfaceWireframeParams,
visuals: PD.MultiSelect(['molecular-surface-mesh'], PD.objectToOptions(MolecularSurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type MolecularSurfaceParams = typeof MolecularSurfaceParams
export function getMolecularSurfaceParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -11,6 +11,7 @@ import { Representation, RepresentationParamsGetter, RepresentationContext } fro
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { OrientationEllipsoidMeshParams, OrientationEllipsoidMeshVisual } from '../visual/orientation-ellipsoid-mesh';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const OrientationVisuals = {
'orientation-ellipsoid-mesh': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, OrientationEllipsoidMeshParams>) => UnitsRepresentation('Orientation ellipsoid mesh', ctx, getParams, OrientationEllipsoidMeshVisual),
@@ -19,6 +20,7 @@ const OrientationVisuals = {
export const OrientationParams = {
...OrientationEllipsoidMeshParams,
visuals: PD.MultiSelect(['orientation-ellipsoid-mesh'], PD.objectToOptions(OrientationVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type OrientationParams = typeof OrientationParams
export function getOrientationParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -12,6 +12,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
import { Structure, Unit } from '../../../mol-model/structure';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const PuttyVisuals = {
'polymer-tube': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, PolymerTubeParams>) => UnitsRepresentation('Polymer tube mesh', ctx, getParams, PolymerTubeVisual),
@@ -23,6 +24,7 @@ export const PuttyParams = {
...PolymerGapParams,
sizeFactor: PD.Numeric(0.2, { min: 0, max: 10, step: 0.01 }),
visuals: PD.MultiSelect(['polymer-tube', 'polymer-gap'], PD.objectToOptions(PuttyVisuals)),
bumpFrequency: PD.Numeric(2, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type PuttyParams = typeof PuttyParams
export function getPuttyParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -11,6 +11,7 @@ import { StructureRepresentation, StructureRepresentationProvider, StructureRepr
import { RepresentationParamsGetter, RepresentationContext, Representation } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { BaseGeometry } from '../../../mol-geo/geometry/base';
const SpacefillVisuals = {
'element-sphere': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ElementSphereParams>) => UnitsRepresentation('Sphere mesh', ctx, getParams, ElementSphereVisual),
@@ -18,6 +19,7 @@ const SpacefillVisuals = {
export const SpacefillParams = {
...ElementSphereParams,
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type SpacefillParams = typeof SpacefillParams
export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
@@ -21,11 +21,12 @@ import { BoundaryHelper } from '../../../mol-math/geometry/boundary-helper';
export const LabelTextParams = {
...ComplexTextParams,
background: PD.Boolean(true),
background: PD.Boolean(false),
backgroundMargin: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
backgroundColor: PD.Color(ColorNames.black),
backgroundOpacity: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
level: PD.Select('residue', [['chain', 'Chain'], ['residue', 'Residue'], ['element', 'Element']] as const),
borderWidth: PD.Numeric(0.25, { min: 0, max: 0.5, step: 0.01 }),
level: PD.Select('residue', [['chain', 'Chain'], ['residue', 'Residue'], ['element', 'Element']] as const, { isEssential: true }),
chainScale: PD.Numeric(10, { min: 0, max: 20, step: 0.1 }),
residueScale: PD.Numeric(1, { min: 0, max: 20, step: 0.1 }),
elementScale: PD.Numeric(0.5, { min: 0, max: 20, step: 0.1 }),

View File

@@ -28,6 +28,7 @@ import { extractIsosurface } from '../../mol-gl/compute/marching-cubes/isosurfac
import { WebGLContext } from '../../mol-gl/webgl/context';
import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
import { Texture } from '../../mol-gl/webgl/texture';
import { BaseGeometry } from '../../mol-geo/geometry/base';
export const VolumeIsosurfaceParams = {
isoValue: Volume.IsoValueParam
@@ -260,6 +261,7 @@ export const IsosurfaceParams = {
...IsosurfaceMeshParams,
...IsosurfaceWireframeParams,
visuals: PD.MultiSelect(['solid'], PD.objectToOptions(IsosurfaceVisuals)),
bumpFrequency: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }, BaseGeometry.ShadingCategory),
};
export type IsosurfaceParams = typeof IsosurfaceParams
export function getIsosurfaceParams(ctx: ThemeRegistryContext, volume: Volume) {

View File

@@ -17,7 +17,8 @@ const DefaultIllustrativeColor = Color(0xEEEEEE);
const Description = `Assigns an illustrative color that gives every chain a unique color with lighter carbons (inspired by David Goodsell's Molecule of the Month style).`;
export const IllustrativeColorThemeParams = {
...ChainIdColorThemeParams
...ChainIdColorThemeParams,
carbonLightness: PD.Numeric(0.8, { min: -6, max: 6, step: 0.1 })
};
export type IllustrativeColorThemeParams = typeof IllustrativeColorThemeParams
export function getIllustrativeColorThemeParams(ctx: ThemeDataContext) {
@@ -29,7 +30,7 @@ export function IllustrativeColorTheme(ctx: ThemeDataContext, props: PD.Values<I
function illustrativeColor(location: Location, typeSymbol: ElementSymbol) {
const baseColor = chainIdColor(location, false);
return typeSymbol === 'C' ? Color.lighten(baseColor, 0.8) : baseColor;
return typeSymbol === 'C' ? Color.lighten(baseColor, props.carbonLightness) : baseColor;
}
function color(location: Location): Color {

View File

@@ -8,6 +8,7 @@ import { Loci } from '../mol-model/loci';
import { Structure, StructureElement } from '../mol-model/structure';
import { Script } from '../mol-script/script';
import { Material } from '../mol-util/material';
import { shallowEqual } from '../mol-util/object';
export { Substance };
@@ -26,7 +27,7 @@ namespace Substance {
if (sA.layers.length !== sB.layers.length) return false;
for (let i = 0, il = sA.layers.length; i < il; ++i) {
if (sA.layers[i].clear !== sB.layers[i].clear) return false;
if (sA.layers[i].material !== sB.layers[i].material) return false;
if (!shallowEqual(sA.layers[i].material, sB.layers[i].material)) return false;
if (!Loci.areEqual(sA.layers[i].loci, sB.layers[i].loci)) return false;
}
return true;
@@ -51,25 +52,32 @@ namespace Substance {
export function merge(substance: Substance): Substance {
if (isEmpty(substance)) return substance;
const { structure } = substance.layers[0].loci;
const map = new Map<Material | -1, StructureElement.Loci>();
let clearLoci: StructureElement.Loci | undefined = void 0;
const map = new Map<Material, StructureElement.Loci>();
let shadowed = StructureElement.Loci.none(structure);
for (let i = 0, il = substance.layers.length; i < il; ++i) {
let { loci, material, clear } = substance.layers[il - i - 1]; // process from end
loci = StructureElement.Loci.subtract(loci, shadowed);
shadowed = StructureElement.Loci.union(loci, shadowed);
if (!StructureElement.Loci.isEmpty(loci)) {
const materialOrClear = clear ? -1 : material;
if (map.has(materialOrClear)) {
loci = StructureElement.Loci.union(loci, map.get(materialOrClear)!);
if (clear) {
clearLoci = clearLoci
? StructureElement.Loci.union(loci, clearLoci)
: loci;
} else {
if (map.has(material)) {
loci = StructureElement.Loci.union(loci, map.get(material)!);
}
map.set(material, loci);
}
map.set(materialOrClear, loci);
}
}
const layers: Substance.Layer[] = [];
map.forEach((loci, materialOrClear) => {
const clear = materialOrClear === -1;
const material = clear ? Material(0) : materialOrClear;
layers.push({ loci, material, clear });
if (clearLoci) {
layers.push({ loci: clearLoci, material: Material(), clear: true });
}
map.forEach((loci, material) => {
layers.push({ loci, material, clear: false });
});
return { layers };
}

View File

@@ -6,51 +6,47 @@
import { NumberArray } from './type-helpers';
import { ParamDefinition as PD } from './param-definition';
import { toFixed } from './number';
/** Material properties expressed as a single number */
export type Material = { readonly '@type': 'material' } & number
export interface Material {
/** Normalized to [0, 1] range */
metalness: number,
/** Normalized to [0, 1] range */
roughness: number
/** Normalized to [0, 1] range */
bumpiness: number
}
export function Material(hex: number) { return hex as Material; }
export function Material(values?: Partial<Material>) {
return { ...Material.Zero, ...values };
}
export namespace Material {
export function fromNormalized(metalness: number, roughness: number): Material {
return (((metalness * 255) << 16) | ((roughness * 255) << 8)) as Material;
}
export function fromObjectNormalized(v: { metalness: number, roughness: number }): Material {
return fromNormalized(v.metalness, v.roughness);
}
export function toObjectNormalized(material: Material, fractionDigits?: number) {
const metalness = (material >> 16 & 255) / 255;
const roughness = (material >> 8 & 255) / 255;
return {
metalness: fractionDigits ? toFixed(metalness, fractionDigits) : metalness,
roughness: fractionDigits ? toFixed(roughness, fractionDigits) : roughness
};
}
export const Zero: Material = { metalness: 0, roughness: 0, bumpiness: 0 };
export function toArray(material: Material, array: NumberArray, offset: number) {
array[offset] = (material >> 16 & 255);
array[offset + 1] = (material >> 8 & 255);
array[offset] = material.metalness * 255;
array[offset + 1] = material.roughness * 255;
array[offset + 2] = material.bumpiness * 255;
return array;
}
export function toString(material: Material) {
const metalness = (material >> 16 & 255) / 255;
const roughness = (material >> 8 & 255) / 255;
return `M ${metalness} | R ${roughness}`;
export function toString({ metalness, roughness, bumpiness }: Material) {
return `M ${metalness.toFixed(2)} | R ${roughness.toFixed(2)} | B ${bumpiness.toFixed(2)}`;
}
export function getParam(info?: { isExpanded?: boolean, isFlat?: boolean }) {
return PD.Converted(
(v: Material) => toObjectNormalized(v, 2),
(v: { metalness: number, roughness: number }) => fromObjectNormalized(v),
PD.Group({
metalness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
roughness: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
}, info)
);
return PD.Group({
metalness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
roughness: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }),
bumpiness: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
}, {
...info,
presets: [
[{ metalness: 0, roughness: 1, bumpiness: 0 }, 'Matte'],
[{ metalness: 0, roughness: 0.2, bumpiness: 0 }, 'Plastic'],
[{ metalness: 0, roughness: 0.6, bumpiness: 0 }, 'Glossy'],
[{ metalness: 1.0, roughness: 0.6, bumpiness: 0 }, 'Metallic'],
]
});
}
}

View File

@@ -225,12 +225,14 @@ export namespace ParamDefinition {
export interface Group<T> extends Base<T> {
type: 'group',
params: Params,
presets?: Select<T>['options'],
isExpanded?: boolean,
isFlat?: boolean,
pivot?: keyof T
}
export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any, pivot?: keyof T }): Group<Normalize<T>> {
export function Group<T>(params: For<T>, info?: Info & { isExpanded?: boolean, isFlat?: boolean, customDefault?: any, pivot?: keyof T, presets?: Select<T>['options'] }): Group<Normalize<T>> {
const ret = setInfo<Group<Normalize<T>>>({ type: 'group', defaultValue: info?.customDefault || getDefaultValues(params as any as Params) as any, params: params as any as Params }, info);
if (info?.presets) ret.presets = info.presets;
if (info?.isExpanded) ret.isExpanded = info.isExpanded;
if (info?.isFlat) ret.isFlat = info.isFlat;
if (info?.pivot) ret.pivot = info.pivot as any;