mirror of
https://github.com/molstar/molstar.git
synced 2026-06-05 22:31:26 +08:00
Compare commits
370 Commits
v0.4.2
...
v0.5.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d31f1ba07 | ||
|
|
d722d17a02 | ||
|
|
522d929f5a | ||
|
|
42037074cb | ||
|
|
1e3f17efdf | ||
|
|
7389e0075d | ||
|
|
a080114690 | ||
|
|
1293848d9b | ||
|
|
16b2d9e873 | ||
|
|
7a64334261 | ||
|
|
009b20c95a | ||
|
|
296bea1b88 | ||
|
|
e0775607cc | ||
|
|
a1fb4b8bf3 | ||
|
|
29ae78c193 | ||
|
|
46cfb42cce | ||
|
|
94ef9f4dbe | ||
|
|
b864e992ef | ||
|
|
57ed9a4226 | ||
|
|
b2c93cbeda | ||
|
|
60540dadee | ||
|
|
c0236650f0 | ||
|
|
fb3017cfff | ||
|
|
929c91a48c | ||
|
|
bfd9595c1c | ||
|
|
c091f9a8ae | ||
|
|
d5b71b302b | ||
|
|
67103232be | ||
|
|
f471fc3d2b | ||
|
|
2a0783d005 | ||
|
|
7234ad261b | ||
|
|
001d1fb8d3 | ||
|
|
35a56bf37c | ||
|
|
9715ff061e | ||
|
|
9866db9ced | ||
|
|
fc2288a583 | ||
|
|
11d1bbe222 | ||
|
|
bc07256d8e | ||
|
|
3826bdb0c3 | ||
|
|
9609dddd47 | ||
|
|
ad7b318da0 | ||
|
|
66c76ea6b5 | ||
|
|
29e47c1e90 | ||
|
|
e08a074a1c | ||
|
|
d7ebb30e05 | ||
|
|
8330068434 | ||
|
|
42dea4a2eb | ||
|
|
4de0ae6628 | ||
|
|
5a714af309 | ||
|
|
20f73fdae7 | ||
|
|
aca91cf18f | ||
|
|
974a7d7520 | ||
|
|
f9d7545f7d | ||
|
|
0e135c4645 | ||
|
|
961f847765 | ||
|
|
b83fed4701 | ||
|
|
651fd2aca7 | ||
|
|
2dd863e711 | ||
|
|
bac5ea7ea6 | ||
|
|
312db7eec3 | ||
|
|
31850b3a71 | ||
|
|
ee35e39611 | ||
|
|
872130d65c | ||
|
|
b903554f52 | ||
|
|
c3fba20780 | ||
|
|
870d2da8ed | ||
|
|
22d17f6c8e | ||
|
|
6bbf12a660 | ||
|
|
ab4ae742db | ||
|
|
7725071ae2 | ||
|
|
c7ab6ebec7 | ||
|
|
d56abadf4c | ||
|
|
7eb2952ec5 | ||
|
|
21cf2d5437 | ||
|
|
5af0c448c6 | ||
|
|
2ca9392c5a | ||
|
|
a5a6f2dcc4 | ||
|
|
91f5207d68 | ||
|
|
c89848dec0 | ||
|
|
116d3ec319 | ||
|
|
20266a229b | ||
|
|
0a26e84f8f | ||
|
|
93bfa7a575 | ||
|
|
c09991573c | ||
|
|
556b7b26d4 | ||
|
|
a011600766 | ||
|
|
2e5439c385 | ||
|
|
3d6ae08437 | ||
|
|
03228f7952 | ||
|
|
e46a8c4369 | ||
|
|
01ef92a795 | ||
|
|
61a5d18be6 | ||
|
|
9c2c48bd59 | ||
|
|
df6d49b2ac | ||
|
|
2476bf76b5 | ||
|
|
1c4a397249 | ||
|
|
4930018a55 | ||
|
|
205f4c31d6 | ||
|
|
89d3c87919 | ||
|
|
d0a861d39c | ||
|
|
9f7b96c727 | ||
|
|
e1d2a8b41d | ||
|
|
e71bf9c288 | ||
|
|
693ea3a40e | ||
|
|
921d23e73f | ||
|
|
709944c859 | ||
|
|
9341c19bd5 | ||
|
|
1cee84d9af | ||
|
|
bea5fe5474 | ||
|
|
c473f2b284 | ||
|
|
d5c163ac48 | ||
|
|
85dcef1b2e | ||
|
|
8c1acc6758 | ||
|
|
fbb7f0a6a1 | ||
|
|
3b7c8963df | ||
|
|
d58f4a73b6 | ||
|
|
09cfd85603 | ||
|
|
846a301122 | ||
|
|
6a29925733 | ||
|
|
5633350b63 | ||
|
|
5d5ffcdb36 | ||
|
|
a819835984 | ||
|
|
227721bfd3 | ||
|
|
31e2cc5f07 | ||
|
|
3f02cf0561 | ||
|
|
a57a9f0386 | ||
|
|
11a6df6e19 | ||
|
|
8d5e1feac9 | ||
|
|
e19e3d7380 | ||
|
|
78c7664be3 | ||
|
|
39b29fe0ea | ||
|
|
5636edc1d1 | ||
|
|
7dbdc75cad | ||
|
|
fd92c916b7 | ||
|
|
f5e880839e | ||
|
|
6e70172c45 | ||
|
|
22563bf671 | ||
|
|
cad95403aa | ||
|
|
d375595e08 | ||
|
|
b16d13cc35 | ||
|
|
f03471ee39 | ||
|
|
6f1f65487d | ||
|
|
b863aaedb3 | ||
|
|
b77994d01f | ||
|
|
30e01c07f7 | ||
|
|
1e018b82df | ||
|
|
49e6966037 | ||
|
|
85b1df46cd | ||
|
|
6908a2cd2b | ||
|
|
4379d818ab | ||
|
|
4434dfb79a | ||
|
|
9621f67e84 | ||
|
|
21a0684353 | ||
|
|
3601955433 | ||
|
|
d91483c485 | ||
|
|
8b3d07906f | ||
|
|
5f8a4b6be4 | ||
|
|
f8ff919787 | ||
|
|
6dfe975fc7 | ||
|
|
2650f8d3dd | ||
|
|
f172b6ceaa | ||
|
|
8086af1bf7 | ||
|
|
5813840e17 | ||
|
|
e2d595394a | ||
|
|
9b24e6fb1f | ||
|
|
0f33144935 | ||
|
|
6d384166d5 | ||
|
|
8b766dc242 | ||
|
|
1fa64c836c | ||
|
|
af700c1481 | ||
|
|
7dcf16cd97 | ||
|
|
f86b46076c | ||
|
|
6fe58d7b73 | ||
|
|
8c2a4b5cae | ||
|
|
b2c31f5166 | ||
|
|
0f4d0ff986 | ||
|
|
2b73f9cafd | ||
|
|
a86438a265 | ||
|
|
c0b5102d31 | ||
|
|
feab3a38f9 | ||
|
|
b1fb9c5c47 | ||
|
|
9fb65f46a1 | ||
|
|
7a2e85b856 | ||
|
|
f85e3e76fd | ||
|
|
c6ef02d0a6 | ||
|
|
81a6e3cf4e | ||
|
|
48a75927f6 | ||
|
|
067a85ef88 | ||
|
|
c4757313e6 | ||
|
|
88d8998bd2 | ||
|
|
97e6884487 | ||
|
|
8fd56dbc38 | ||
|
|
f9c97ad5cb | ||
|
|
f893aba522 | ||
|
|
f2740aaee2 | ||
|
|
fb15cc135a | ||
|
|
d9579914b4 | ||
|
|
5a98bfd8ef | ||
|
|
149e6ebf84 | ||
|
|
74c8e512a7 | ||
|
|
52bf9b87fa | ||
|
|
ac8d71215e | ||
|
|
c7cfedb874 | ||
|
|
faae40c9e8 | ||
|
|
288912ccea | ||
|
|
26e47c3e33 | ||
|
|
15a8ea6598 | ||
|
|
aeda6d3312 | ||
|
|
79f430efb3 | ||
|
|
4fe70de2ff | ||
|
|
56fd6d6a5b | ||
|
|
b5076edc8d | ||
|
|
4fbb9e232c | ||
|
|
b6324d9cee | ||
|
|
f668f725e8 | ||
|
|
e45041f2f2 | ||
|
|
d285076acb | ||
|
|
9047aae87c | ||
|
|
44b00edcb6 | ||
|
|
a476c2a167 | ||
|
|
1b2b168624 | ||
|
|
ce238bcd1d | ||
|
|
c119a82d83 | ||
|
|
4b97686a26 | ||
|
|
c13350b098 | ||
|
|
c9c6df4861 | ||
|
|
2e73681aae | ||
|
|
42b4c25ca3 | ||
|
|
5a5939408f | ||
|
|
7041bac8ca | ||
|
|
f8ab02fdab | ||
|
|
b75ba4b4aa | ||
|
|
baa80d08dd | ||
|
|
144d40cd38 | ||
|
|
c6b4610adf | ||
|
|
f2c5cd978b | ||
|
|
63823698c7 | ||
|
|
f6ca679a57 | ||
|
|
142c46c127 | ||
|
|
1bd59523b2 | ||
|
|
ef67925858 | ||
|
|
5dae376bcb | ||
|
|
14ed325579 | ||
|
|
b007409521 | ||
|
|
5830252df7 | ||
|
|
02ad9587fc | ||
|
|
4596336c13 | ||
|
|
2a413256ed | ||
|
|
8b9178249d | ||
|
|
1d0ba36d7c | ||
|
|
3a8211937f | ||
|
|
0d576f7011 | ||
|
|
d90e81b0e5 | ||
|
|
89be1767e9 | ||
|
|
aee6921140 | ||
|
|
a55572d52f | ||
|
|
525deafd83 | ||
|
|
8064b969be | ||
|
|
6d6e12bbd0 | ||
|
|
825ae48000 | ||
|
|
764aef3181 | ||
|
|
d226abadc1 | ||
|
|
ccd1fa3a0f | ||
|
|
65a692e3bf | ||
|
|
2daac955f4 | ||
|
|
162797e43a | ||
|
|
784b29805a | ||
|
|
67b60cff60 | ||
|
|
28443de11f | ||
|
|
59cf60c2ba | ||
|
|
8e14a1e2ec | ||
|
|
7dd2e5ec98 | ||
|
|
bd3e052130 | ||
|
|
6fbe7efcab | ||
|
|
572a574bf3 | ||
|
|
347a0a43ea | ||
|
|
28abd00a6e | ||
|
|
06354d6d1d | ||
|
|
2b31d54e18 | ||
|
|
58644b3d5c | ||
|
|
4c732b3b2d | ||
|
|
36d3a0728c | ||
|
|
b0c696e401 | ||
|
|
2a28b5421f | ||
|
|
269176e0a6 | ||
|
|
cd23a68268 | ||
|
|
48e81c8b10 | ||
|
|
649c294929 | ||
|
|
ab0dcb0de9 | ||
|
|
cb2fc8f95b | ||
|
|
5deec4391d | ||
|
|
1873e506e0 | ||
|
|
11e660a81a | ||
|
|
635ba2a7e3 | ||
|
|
1a2fb55d03 | ||
|
|
a5785e526e | ||
|
|
af9d50f5ec | ||
|
|
ac06b9c018 | ||
|
|
36d8df1442 | ||
|
|
a312c4ef1d | ||
|
|
0c6b80d370 | ||
|
|
16f9129acc | ||
|
|
1af9554cc7 | ||
|
|
cc8999cbc9 | ||
|
|
c7b4099758 | ||
|
|
f060fc228f | ||
|
|
8d3c091d2e | ||
|
|
cabf7c1613 | ||
|
|
b44149bbaf | ||
|
|
18befade7f | ||
|
|
8fd8eb703b | ||
|
|
fa0c29c1d1 | ||
|
|
65ed5c98bf | ||
|
|
f0ecf74aad | ||
|
|
c56ed0af28 | ||
|
|
ddb8231c00 | ||
|
|
bc24e8f236 | ||
|
|
2edb31e7ea | ||
|
|
ad619d9b8d | ||
|
|
61f738ce3d | ||
|
|
73ae95ed06 | ||
|
|
6d4c1aa3ea | ||
|
|
34f230a882 | ||
|
|
a68d81e495 | ||
|
|
9687c38e06 | ||
|
|
f69670c368 | ||
|
|
a0b537ef64 | ||
|
|
e8663b5bfc | ||
|
|
9f12fbde70 | ||
|
|
940b16ebd2 | ||
|
|
dc5aa0c30d | ||
|
|
b70b9c44fc | ||
|
|
88a4cf1aa4 | ||
|
|
49eefa131f | ||
|
|
9f4a60572f | ||
|
|
8f9579bcaf | ||
|
|
845dd4115d | ||
|
|
3e2a1227ce | ||
|
|
dcfec014a5 | ||
|
|
f5598ba93e | ||
|
|
a5fadb6e8a | ||
|
|
738ba2d6fd | ||
|
|
ebcc45d1fd | ||
|
|
272a911d35 | ||
|
|
a725e577be | ||
|
|
a36ead9ef1 | ||
|
|
f516072d12 | ||
|
|
a54b36b057 | ||
|
|
a18484a5dd | ||
|
|
e685c0a342 | ||
|
|
a3b1d11bb3 | ||
|
|
7a7643c6bc | ||
|
|
3643bd04f1 | ||
|
|
65c2faaced | ||
|
|
32a91ca98c | ||
|
|
9f6e98a5d0 | ||
|
|
0964a9fd50 | ||
|
|
a0b865cd07 | ||
|
|
a5f73b5c20 | ||
|
|
d10be352be | ||
|
|
6f3d8ddd96 | ||
|
|
1a14e65dcf | ||
|
|
2757dfdb75 | ||
|
|
1b5526235e | ||
|
|
d4836c5fde | ||
|
|
530810e366 | ||
|
|
9f2a4828f4 | ||
|
|
70cc3a612e | ||
|
|
6917ba6230 | ||
|
|
710c06672e |
65
.eslintrc.json
Normal file
65
.eslintrc.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/ban-types": "warn",
|
||||
"@typescript-eslint/class-name-casing": "off",
|
||||
"@typescript-eslint/indent": [
|
||||
"warn",
|
||||
4
|
||||
],
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"off",
|
||||
{
|
||||
"multiline": {
|
||||
"delimiter": "none",
|
||||
"requireLast": true
|
||||
},
|
||||
"singleline": {
|
||||
"delimiter": "semi",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-namespace-keyword": "warn",
|
||||
"@typescript-eslint/quotes": [
|
||||
"warn",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"off",
|
||||
null
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "warn",
|
||||
"arrow-parens": [
|
||||
"off",
|
||||
"as-needed"
|
||||
],
|
||||
"comma-dangle": "off",
|
||||
"eqeqeq": [
|
||||
"warn",
|
||||
"smart"
|
||||
],
|
||||
"import/order": "off",
|
||||
"no-eval": "warn",
|
||||
"no-new-wrappers": "warn",
|
||||
"no-trailing-spaces": "warn",
|
||||
"no-unsafe-finally": "warn",
|
||||
"no-var": "warn",
|
||||
"spaced-comment": "warn"
|
||||
}
|
||||
}
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -4,8 +4,8 @@
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"slevesque.shader",
|
||||
"stpn.vscode-graphql",
|
||||
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -6,4 +6,7 @@
|
||||
"*.vert.ts": "glsl",
|
||||
"*.gql.ts": "graphql"
|
||||
},
|
||||
"eslint.options": {
|
||||
"ignorePattern": ["webpack.config.js", "scripts/*"],
|
||||
}
|
||||
}
|
||||
@@ -31,12 +31,14 @@ The core of Mol* currently consists of these modules (see under `src/`):
|
||||
- `mol-state` State representation tree with state saving and automatic updates.
|
||||
- `mol-app` Components for builduing UIs.
|
||||
- `mol-plugin` Allow to define modular Mol* plugin instances utilizing `mol-state` and `mol-canvas3d`.
|
||||
- `mol-plugin-ui` React based user interface for the Mol* plugin. Some components of the UI are usable outside the main plugin and can be integrated to 3rd party solutions.
|
||||
- `mol-util` Useful things that do not fit elsewhere.
|
||||
|
||||
Moreover, the project contains the imlementation of `servers`, including
|
||||
|
||||
- `servers/model` A tool for accessing coordinate and annotation data of molecular structures.
|
||||
- `servers/volume` A tool for accessing volumetric experimental data related to molecular structures.
|
||||
- `servers/plugin-state` A basic server to store Mol* Plugin states.
|
||||
|
||||
The project also contains performance tests (`perf-tests`), `examples`, and basic proof of concept `apps` (CIF to BinaryCIF converter and JSON domain annotation to CIF converter).
|
||||
|
||||
@@ -89,10 +91,11 @@ Install CIFTools `npm install ciftools -g`
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/mmcif.ts -p mmCIF
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/ccd.ts -p CCD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/bird.ts -p BIRD
|
||||
cifschema -mip ../../../../mol-data -o src/mol-io/reader/cif/schema/cif-core.ts -p CifCore -aa
|
||||
|
||||
**GraphQL schemas**
|
||||
|
||||
node data/rcsb-graphql/codegen.js
|
||||
./node_modules/.bin/graphql-codegen -c ./data/rcsb-graphql/codegen.yml
|
||||
|
||||
### Other scripts
|
||||
**Create chem comp bond table**
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
audit.block_doi
|
||||
|
||||
database_code.depnum_ccdc_archive
|
||||
|
||||
chemical.name_systematic
|
||||
chemical.name_common
|
||||
chemical.melting_point
|
||||
|
||||
chemical_formula.moiety
|
||||
chemical_formula.sum
|
||||
chemical_formula.weight
|
||||
|
||||
atom_type.symbol
|
||||
atom_type.description
|
||||
|
||||
atom_type_scat.dispersion_real
|
||||
atom_type_scat.dispersion_imag
|
||||
atom_type_scat.source
|
||||
|
||||
space_group.crystal_system
|
||||
space_group.name_H-M_full
|
||||
space_group_symop.operation_xyz
|
||||
|
||||
cell.length_a
|
||||
cell.length_b
|
||||
cell.length_c
|
||||
cell.angle_alpha
|
||||
cell.angle_beta
|
||||
cell.angle_gamma
|
||||
cell.volume
|
||||
cell.formula_units_Z
|
||||
|
||||
atom_site.label
|
||||
atom_site.type_symbol
|
||||
atom_site.fract_x
|
||||
atom_site.fract_y
|
||||
atom_site.fract_z
|
||||
atom_site.U_iso_or_equiv
|
||||
atom_site.adp_type
|
||||
atom_site.occupancy
|
||||
atom_site.calc_flag
|
||||
atom_site.refinement_flags
|
||||
atom_site.disorder_assembly
|
||||
atom_site.disorder_group
|
||||
|
||||
atom_site.site_symmetry_multiplicity
|
||||
|
||||
atom_site_aniso.label
|
||||
atom_site_aniso.U_11
|
||||
atom_site_aniso.U_22
|
||||
atom_site_aniso.U_33
|
||||
atom_site_aniso.U_23
|
||||
atom_site_aniso.U_13
|
||||
atom_site_aniso.U_12
|
||||
|
||||
geom_bond.atom_site_label_1
|
||||
geom_bond.atom_site_label_2
|
||||
geom_bond.distance
|
||||
geom_bond.site_symmetry_2
|
||||
geom_bond.publ_flag
|
||||
|
@@ -1,22 +0,0 @@
|
||||
const { generate } = require('graphql-code-generator')
|
||||
const path = require('path')
|
||||
|
||||
const basePath = path.join(__dirname, '..', '..', 'src', 'mol-model-props', 'rcsb', 'graphql')
|
||||
|
||||
generate({
|
||||
schema: 'http://rest-staging.rcsb.org/graphql',
|
||||
documents: {
|
||||
[path.join(basePath, 'symmetry.gql.ts')]: {
|
||||
loader: path.join(__dirname, 'loader.js')
|
||||
},
|
||||
},
|
||||
generates: {
|
||||
[path.join(basePath, 'types.ts')]: {
|
||||
plugins: ['time', 'typescript-common', 'typescript-client']
|
||||
}
|
||||
},
|
||||
overwrite: true,
|
||||
config: path.join(__dirname, 'codegen.json')
|
||||
}, true).then(
|
||||
() => console.log('done')
|
||||
).catch(e => console.error(e))
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"flattenTypes": true,
|
||||
"generatorConfig": {
|
||||
"immutableTypes": true
|
||||
}
|
||||
}
|
||||
12
data/rcsb-graphql/codegen.yml
Normal file
12
data/rcsb-graphql/codegen.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
schema: https://data-beta.rcsb.org/graphql
|
||||
documents: './src/mol-model-props/rcsb/graphql/symmetry.gql.ts'
|
||||
generates:
|
||||
'./src/mol-model-props/rcsb/graphql/types.d.ts':
|
||||
plugins:
|
||||
- add: '/* eslint-disable */'
|
||||
- time
|
||||
- typescript
|
||||
- typescript-operations
|
||||
config:
|
||||
immutableTypes: true
|
||||
skipTypename: true
|
||||
@@ -1,14 +0,0 @@
|
||||
const { parse } = require('graphql');
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
module.exports = function(docString, config) {
|
||||
const str = readFileSync(docString, { encoding: 'utf-8' }).trim()
|
||||
.replace(/^export default `/, '')
|
||||
.replace(/`$/, '')
|
||||
return [
|
||||
{
|
||||
filePath: docString,
|
||||
content: parse(str)
|
||||
}
|
||||
];
|
||||
};
|
||||
15466
package-lock.json
generated
15466
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
80
package.json
80
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "molstar",
|
||||
"version": "0.4.2",
|
||||
"version": "0.5.0-dev.2",
|
||||
"description": "A comprehensive macromolecular library.",
|
||||
"homepage": "https://github.com/molstar/molstar#readme",
|
||||
"repository": {
|
||||
@@ -11,7 +11,7 @@
|
||||
"url": "https://github.com/molstar/molstar/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "tslint src/**/*.ts",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"test": "npm run lint && jest",
|
||||
"build": "npm run build-tsc && npm run build-extra && npm run build-webpack",
|
||||
"build-tsc": "tsc",
|
||||
@@ -24,7 +24,8 @@
|
||||
"serve": "http-server -p 1338",
|
||||
"model-server": "node lib/servers/model/server.js",
|
||||
"model-server-watch": "nodemon --watch lib lib/servers/model/server.js",
|
||||
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"volume-server": "node lib/servers/volume/server.js --idMap em 'test/${id}.mdb' --defaultPort 1336",
|
||||
"plugin-state": "node lib/servers/plugin-state/index.js",
|
||||
"preversion": "npm run test",
|
||||
"postversion": "git push && git push --tags",
|
||||
"prepublishOnly": "npm run test && npm run build"
|
||||
@@ -63,55 +64,66 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^1.12.2",
|
||||
"@graphql-codegen/cli": "^1.12.2",
|
||||
"@graphql-codegen/time": "^1.12.2",
|
||||
"@graphql-codegen/typescript": "^1.12.2",
|
||||
"@graphql-codegen/typescript-graphql-files-modules": "^1.12.2",
|
||||
"@graphql-codegen/typescript-graphql-request": "^1.12.2",
|
||||
"@graphql-codegen/typescript-operations": "^1.12.2",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@typescript-eslint/eslint-plugin": "^2.19.2",
|
||||
"@typescript-eslint/eslint-plugin-tslint": "^2.19.2",
|
||||
"@typescript-eslint/parser": "^2.19.2",
|
||||
"benchmark": "^2.1.4",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"concurrently": "^5.0.0",
|
||||
"cpx": "^1.5.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"concurrently": "^5.1.0",
|
||||
"cpx2": "^2.0.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"extra-watch-webpack-plugin": "^1.0.3",
|
||||
"file-loader": "^4.2.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"graphql-code-generator": "^0.18.2",
|
||||
"graphql-codegen-time": "^0.18.2",
|
||||
"graphql-codegen-typescript-template": "^0.18.2",
|
||||
"http-server": "^0.11.1",
|
||||
"jest": "^24.9.0",
|
||||
"http-server": "^0.12.1",
|
||||
"jest": "^25.1.0",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"raw-loader": "^3.1.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-sass": "^4.13.1",
|
||||
"pascal-case": "^3.1.1",
|
||||
"raw-loader": "^4.0.0",
|
||||
"resolve-url-loader": "^3.1.1",
|
||||
"sass-loader": "^8.0.0",
|
||||
"simple-git": "^1.126.0",
|
||||
"style-loader": "^1.0.0",
|
||||
"ts-jest": "^24.1.0",
|
||||
"tslint": "^5.20.1",
|
||||
"typescript": "^3.7.2",
|
||||
"webpack": "^4.41.2",
|
||||
"sass-loader": "^8.0.2",
|
||||
"simple-git": "^1.131.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"ts-jest": "^25.2.0",
|
||||
"typescript": "^3.7.5",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-cli": "^3.3.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/argparse": "^1.0.36",
|
||||
"@types/argparse": "^1.0.38",
|
||||
"@types/benchmark": "^1.0.31",
|
||||
"@types/compression": "1.0.1",
|
||||
"@types/compression": "1.7.0",
|
||||
"@types/express": "^4.17.2",
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/node": "^12.12.9",
|
||||
"@types/node-fetch": "^2.5.3",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"@types/swagger-ui-dist": "3.0.3",
|
||||
"@types/jest": "^25.1.2",
|
||||
"@types/node": "^13.7.0",
|
||||
"@types/node-fetch": "^2.5.4",
|
||||
"@types/react": "^16.9.19",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@types/swagger-ui-dist": "3.0.5",
|
||||
"argparse": "^1.0.10",
|
||||
"body-parser": "^1.19.0",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql": "^14.6.0",
|
||||
"immutable": "^3.8.2",
|
||||
"node-fetch": "^2.6.0",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"rxjs": "^6.5.3",
|
||||
"swagger-ui-dist": "^3.24.3",
|
||||
"util.promisify": "^1.0.0",
|
||||
"rxjs": "^6.5.4",
|
||||
"swagger-ui-dist": "^3.25.0",
|
||||
"util.promisify": "^1.0.1",
|
||||
"xhr2": "^0.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
|
||||
@@ -9,9 +10,8 @@ import { Model, ElementIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
|
||||
export const StripedResidues = CustomElementProperty.create<number>({
|
||||
isStatic: true,
|
||||
label: 'Residue Stripes',
|
||||
name: 'basic-wrapper-residue-striping',
|
||||
display: 'Residue Stripes',
|
||||
getData(model: Model) {
|
||||
const map = new Map<ElementIndex, number>();
|
||||
const residueIndex = model.atomicHierarchy.residueAtomSegments.index;
|
||||
@@ -24,7 +24,7 @@ export const StripedResidues = CustomElementProperty.create<number>({
|
||||
getColor(e) { return e === 0 ? Color(0xff0000) : Color(0x0000ff) },
|
||||
defaultColor: Color(0x777777)
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
return e === 0 ? 'Odd stripe' : 'Even stripe'
|
||||
}
|
||||
})
|
||||
@@ -4,9 +4,9 @@
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { PluginUIComponent } from '../../mol-plugin/ui/base';
|
||||
import { PluginUIComponent } from '../../mol-plugin-ui/base';
|
||||
import * as React from 'react';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin/ui/state/update-transform';
|
||||
import { TransformUpdaterControl } from '../../mol-plugin-ui/state/update-transform';
|
||||
|
||||
export class BasicWrapperControls extends PluginUIComponent {
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export namespace StateHelper {
|
||||
}
|
||||
|
||||
export function structure(b: StateBuilder.To<PSO.Molecule.Model>) {
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, { tags: 'structure' })
|
||||
return b.apply(StateTransforms.Model.StructureFromModel, void 0, { tags: 'structure' })
|
||||
};
|
||||
|
||||
export function selectChain(b: StateBuilder.To<PSO.Molecule.Structure>, auth_asym_id: string) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { CustomToastMessage } from './controls';
|
||||
import { EmptyLoci } from '../../mol-model/loci';
|
||||
import { StructureSelection } from '../../mol-model/structure';
|
||||
import { Script } from '../../mol-script/script';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -41,12 +41,15 @@ class BasicWrapper {
|
||||
// left: 'none',
|
||||
// right: BasicWrapperControls
|
||||
}
|
||||
},
|
||||
components: {
|
||||
remoteState: 'none'
|
||||
}
|
||||
});
|
||||
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.Descriptor.name, StripedResidues.colorTheme!);
|
||||
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
|
||||
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider!);
|
||||
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
|
||||
}
|
||||
|
||||
private download(b: StateBuilder.To<PSO.Root>, url: string) {
|
||||
@@ -60,7 +63,7 @@ class BasicWrapper {
|
||||
|
||||
return parsed
|
||||
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [StripedResidues.Descriptor.name] }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
|
||||
}
|
||||
|
||||
@@ -108,11 +111,13 @@ class BasicWrapper {
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
const renderer = this.plugin.canvas3d.props.renderer;
|
||||
const renderer = this.plugin.canvas3d!.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
|
||||
}
|
||||
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
@@ -136,7 +141,7 @@ class BasicWrapper {
|
||||
|
||||
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: StripedResidues.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.Descriptor.name).defaultValues };
|
||||
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
for (const v of visuals) {
|
||||
tree.to(v).update(old => ({ ...old, colorTheme }));
|
||||
|
||||
@@ -50,16 +50,16 @@ export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionT
|
||||
|
||||
export const StaticSuperpositionTestData: SuperpositionTestInput = [
|
||||
{ pdbId: '1aj5', auth_asym_id: 'A', matrix: Mat4.identity() },
|
||||
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows(
|
||||
[[0.406, 0.879, 0.248, -200.633],
|
||||
[0.693, -0.473, 0.544, 73.403],
|
||||
[0.596, -0.049, -0.802, -14.209],
|
||||
[0, 0, 0, 1]] )},
|
||||
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows(
|
||||
[[-0.053, -0.077, 0.996, -45.633],
|
||||
[-0.312, 0.949, 0.057, -12.255],
|
||||
[-0.949, -0.307, -0.074, 53.562],
|
||||
[0, 0, 0, 1]] )}
|
||||
{ pdbId: '1df0', auth_asym_id: 'B', matrix: Mat4.ofRows([
|
||||
[0.406, 0.879, 0.248, -200.633],
|
||||
[0.693, -0.473, 0.544, 73.403],
|
||||
[0.596, -0.049, -0.802, -14.209],
|
||||
[0, 0, 0, 1]] )},
|
||||
{ pdbId: '1dvi', auth_asym_id: 'A', matrix: Mat4.ofRows([
|
||||
[-0.053, -0.077, 0.996, -45.633],
|
||||
[-0.312, 0.949, 0.057, -12.255],
|
||||
[-0.949, -0.307, -0.074, 53.562],
|
||||
[0, 0, 0, 1]] )}
|
||||
];
|
||||
|
||||
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
|
||||
|
||||
@@ -226,8 +226,8 @@ const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif'
|
||||
const PVCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/aa-variants-v1.cif'
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
|
||||
addHelp: true,
|
||||
description: 'Create a cif file with one big table of all chem_comp_bond entries from the CCD and PVCD.'
|
||||
});
|
||||
parser.addArgument('out', {
|
||||
help: 'Generated file output path.'
|
||||
|
||||
@@ -13,7 +13,7 @@ import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/tran
|
||||
import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
|
||||
import { StateBuilder } from '../../../mol-state';
|
||||
import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
type SupportedFormats = 'cif' | 'pdb'
|
||||
type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
|
||||
@@ -97,15 +97,15 @@ class LightingDemo {
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
|
||||
...props,
|
||||
multiSample: {
|
||||
...this.plugin.canvas3d.props.multiSample,
|
||||
...this.plugin.canvas3d!.props.multiSample,
|
||||
...props.multiSample
|
||||
},
|
||||
renderer: {
|
||||
...this.plugin.canvas3d.props.renderer,
|
||||
...this.plugin.canvas3d!.props.renderer,
|
||||
...props.renderer
|
||||
},
|
||||
postprocessing: {
|
||||
...this.plugin.canvas3d.props.postprocessing,
|
||||
...this.plugin.canvas3d!.props.postprocessing,
|
||||
...props.postprocessing
|
||||
},
|
||||
}});
|
||||
|
||||
@@ -22,6 +22,7 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
case 'color-list': return `One of ${oToS(param.options)}`;
|
||||
case 'vec3': return `3D vector [x, y, z]`;
|
||||
case 'file': return `JavaScript File Handle`;
|
||||
case 'file-list': return `JavaScript FileList Handle`;
|
||||
case 'select': return `One of ${oToS(param.options)}`;
|
||||
case 'text': return 'String';
|
||||
case 'interval': return `Interval [min, max]`;
|
||||
@@ -38,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string {
|
||||
}
|
||||
}
|
||||
|
||||
function oToS(options: [string, string][]) {
|
||||
function oToS(options: readonly (readonly [string, string])[]) {
|
||||
return options.map(o => `'${o[0]}'`).join(', ');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import { openCif, downloadCif } from './helpers';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { trajectoryFromMmCIF } from '../../mol-model-formats/structure/mmcif';
|
||||
import { Sequence } from '../../mol-model/sequence';
|
||||
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
|
||||
async function downloadFromPdb(pdb: string) {
|
||||
@@ -50,8 +52,10 @@ export function residueLabel(model: Model, rI: number) {
|
||||
export function printSecStructure(model: Model) {
|
||||
console.log('\nSecondary Structure\n=============');
|
||||
const { residues } = model.atomicHierarchy;
|
||||
const { key, elements } = model.properties.secondaryStructure;
|
||||
const secondaryStructure = ModelSecondaryStructure.Provider.get(model);
|
||||
if (!secondaryStructure) return
|
||||
|
||||
const { key, elements } = secondaryStructure
|
||||
const count = residues._rowCount;
|
||||
let rI = 0;
|
||||
while (rI < count) {
|
||||
@@ -65,14 +69,14 @@ export function printSecStructure(model: Model) {
|
||||
}
|
||||
}
|
||||
|
||||
export function printLinks(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
export function printBonds(structure: Structure, showIntra: boolean, showInter: boolean) {
|
||||
if (showIntra) {
|
||||
console.log('\nIntra Unit Links\n=============');
|
||||
console.log('\nIntra Unit Bonds\n=============');
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
const elements = unit.elements;
|
||||
const { a, b, edgeCount } = unit.links;
|
||||
const { a, b, edgeCount } = unit.bonds;
|
||||
const { model } = unit;
|
||||
|
||||
if (!edgeCount) continue;
|
||||
@@ -86,20 +90,20 @@ export function printLinks(structure: Structure, showIntra: boolean, showInter:
|
||||
}
|
||||
|
||||
if (showInter) {
|
||||
console.log('\nInter Unit Links\n=============');
|
||||
const links = structure.links;
|
||||
console.log('\nInter Unit Bonds\n=============');
|
||||
const bonds = structure.interUnitBonds;
|
||||
for (const unit of structure.units) {
|
||||
if (!Unit.isAtomic(unit)) continue;
|
||||
|
||||
for (const pairLinks of links.getLinkedUnits(unit)) {
|
||||
if (!pairLinks.areUnitsOrdered || pairLinks.bondCount === 0) continue;
|
||||
for (const pairBonds of bonds.getConnectedUnits(unit)) {
|
||||
if (!pairBonds.areUnitsOrdered || pairBonds.edgeCount === 0) continue;
|
||||
|
||||
const { unitA, unitB } = pairLinks;
|
||||
console.log(`${pairLinks.unitA.id} - ${pairLinks.unitB.id}: ${pairLinks.bondCount} bond(s)`);
|
||||
const { unitA, unitB } = pairBonds;
|
||||
console.log(`${pairBonds.unitA.id} - ${pairBonds.unitB.id}: ${pairBonds.edgeCount} bond(s)`);
|
||||
|
||||
for (const aI of pairLinks.linkedElementIndices) {
|
||||
for (const link of pairLinks.getBonds(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[link.indexB])}`);
|
||||
for (const aI of pairBonds.connectedIndices) {
|
||||
for (const bond of pairBonds.getEdges(aI)) {
|
||||
console.log(`${atomLabel(unitA.model, unitA.elements[aI])} -- ${atomLabel(unitB.model, unitB.elements[bond.indexB])}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +152,7 @@ export function printRings(structure: Structure) {
|
||||
|
||||
export function printUnits(structure: Structure) {
|
||||
console.log('\nUnits\n=============');
|
||||
const l = StructureElement.Location.create();
|
||||
const l = StructureElement.Location.create(structure);
|
||||
|
||||
for (const unit of structure.units) {
|
||||
l.unit = unit;
|
||||
@@ -179,7 +183,8 @@ export function printUnits(structure: Structure) {
|
||||
|
||||
export function printSymmetryInfo(model: Model) {
|
||||
console.log('\nSymmetry Info\n=============');
|
||||
const { symmetry } = model;
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
if (!symmetry) return
|
||||
const { size, anglesInRadians } = symmetry.spacegroup.cell;
|
||||
console.log(`Spacegroup: ${symmetry.spacegroup.name} size: ${Vec3.toString(size)} angles: ${Vec3.toString(anglesInRadians)}`);
|
||||
console.log(`Assembly names: ${symmetry.assemblies.map(a => a.id).join(', ')}`);
|
||||
@@ -214,8 +219,8 @@ async function run(frame: CifFrame, args: Args) {
|
||||
if (args.units) printUnits(structure);
|
||||
if (args.sym) printSymmetryInfo(models[0]);
|
||||
if (args.rings) printRings(structure);
|
||||
if (args.intraLinks) printLinks(structure, true, false);
|
||||
if (args.interLinks) printLinks(structure, false, true);
|
||||
if (args.intraBonds) printBonds(structure, true, false);
|
||||
if (args.interBonds) printBonds(structure, false, true);
|
||||
if (args.mod) printModRes(models[0]);
|
||||
if (args.sec) printSecStructure(models[0]);
|
||||
}
|
||||
@@ -242,8 +247,8 @@ parser.addArgument(['--seq'], { help: 'print sequence', action: 'storeTrue' });
|
||||
parser.addArgument(['--units'], { help: 'print units', action: 'storeTrue' });
|
||||
parser.addArgument(['--sym'], { help: 'print symmetry', action: 'storeTrue' });
|
||||
parser.addArgument(['--rings'], { help: 'print rings', action: 'storeTrue' });
|
||||
parser.addArgument(['--intraLinks'], { help: 'print intra unit links', action: 'storeTrue' });
|
||||
parser.addArgument(['--interLinks'], { help: 'print inter unit links', action: 'storeTrue' });
|
||||
parser.addArgument(['--intraBonds'], { help: 'print intra unit bonds', action: 'storeTrue' });
|
||||
parser.addArgument(['--interBonds'], { help: 'print inter unit bonds', action: 'storeTrue' });
|
||||
parser.addArgument(['--mod'], { help: 'print modified residues', action: 'storeTrue' });
|
||||
parser.addArgument(['--sec'], { help: 'print secoundary structure', action: 'storeTrue' });
|
||||
interface Args {
|
||||
@@ -256,8 +261,8 @@ interface Args {
|
||||
units?: boolean,
|
||||
sym?: boolean,
|
||||
rings?: boolean,
|
||||
intraLinks?: boolean,
|
||||
interLinks?: boolean,
|
||||
intraBonds?: boolean,
|
||||
interBonds?: boolean,
|
||||
mod?: boolean,
|
||||
sec?: boolean,
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Table } from '../../mol-data/db';
|
||||
import { StringBuilder } from '../../mol-util';
|
||||
import { Task } from '../../mol-task';
|
||||
import { createVolumeIsosurfaceMesh } from '../../mol-repr/volume/isosurface';
|
||||
import { createEmptyTheme } from '../../mol-theme/theme';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
|
||||
|
||||
require('util.promisify').shim();
|
||||
@@ -40,7 +40,7 @@ function print(data: Volume) {
|
||||
}
|
||||
|
||||
async function doMesh(data: Volume, filename: string) {
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, createEmptyTheme(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
const mesh = await Task.create('', runtime => createVolumeIsosurfaceMesh({ runtime }, data.volume, Theme.createEmpty(), { isoValue: VolumeIsoValue.absolute(1.5) } )).run();
|
||||
console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
|
||||
|
||||
// Export the mesh in OBJ format.
|
||||
@@ -77,8 +77,8 @@ async function run(url: string, meshFilename: string) {
|
||||
}
|
||||
|
||||
const parser = new argparse.ArgumentParser({
|
||||
addHelp: true,
|
||||
description: 'Info about VolumeData from mol-model module'
|
||||
addHelp: true,
|
||||
description: 'Info about VolumeData from mol-model module'
|
||||
});
|
||||
parser.addArgument([ '--emdb', '-e' ], {
|
||||
help: 'EMDB id, for example 8116',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -11,7 +11,7 @@ import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
|
||||
import { Ingredient, CellPacking, Cell } from './data';
|
||||
import { getFromPdb, getFromCellPackDB } from './util';
|
||||
import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
|
||||
import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
|
||||
import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
|
||||
import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
|
||||
import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
|
||||
import { SymmetryOperator } from '../../../../mol-math/geometry';
|
||||
@@ -28,11 +28,10 @@ import { compile } from '../../../../mol-script/runtime/query/compiler';
|
||||
import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
|
||||
import { ThemeRegistryContext } from '../../../../mol-theme/theme';
|
||||
import { ColorTheme } from '../../../../mol-theme/color';
|
||||
import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
|
||||
import { ModelFormat } from '../../../../mol-model-formats/structure/format';
|
||||
import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
|
||||
import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
|
||||
import { Column } from '../../../../mol-data/db';
|
||||
import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
|
||||
|
||||
function getCellPackModelUrl(fileName: string, baseUrl: string) {
|
||||
return `${baseUrl}/results/${fileName}`
|
||||
@@ -124,7 +123,10 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
|
||||
}
|
||||
|
||||
function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const d = model.sourceData.data.atom_site
|
||||
if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
|
||||
|
||||
const { db } = model.sourceData.data
|
||||
const d = db.atom_site
|
||||
const n = d._rowCount
|
||||
const rowCount = n * transforms.length
|
||||
|
||||
@@ -201,8 +203,8 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
}
|
||||
|
||||
const categories = {
|
||||
entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
|
||||
entity: CifCategory.ofTable('entity', db.entity),
|
||||
chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
|
||||
atom_site: CifCategory.ofFields('atom_site', _atom_site)
|
||||
}
|
||||
|
||||
@@ -217,8 +219,8 @@ async function getCurve(name: string, transforms: Mat4[], model: Model) {
|
||||
const cif = getCifCurve(name, transforms, model)
|
||||
|
||||
const curveModelTask = Task.create('Curve Model', async ctx => {
|
||||
const format = ModelFormat.mmCIF(cif)
|
||||
const models = await _parse_mmCif(format, ctx)
|
||||
const format = MmcifFormat.fromFrame(cif)
|
||||
const models = await createModels(format.data.db, format, ctx)
|
||||
return models[0]
|
||||
})
|
||||
|
||||
@@ -279,6 +281,9 @@ export function createStructureFromCellPack(packing: CellPacking, baseUrl: strin
|
||||
})
|
||||
}
|
||||
|
||||
const RepresentationOptions = PD.arrayToOptions(['spacefill', 'gaussian-surface', 'point', 'ellipsoid'] as const)
|
||||
type RepresentationName = (typeof RepresentationOptions)[0][0]
|
||||
|
||||
export const LoadCellPackModel = StateAction.build({
|
||||
display: { name: 'Load CellPack Model' },
|
||||
params: {
|
||||
@@ -289,15 +294,11 @@ export const LoadCellPackModel = StateAction.build({
|
||||
['influenza_model1.json', 'influenza_model1'],
|
||||
['Mycoplasma1.5_mixed_pdb_fixed.cpr', 'Mycoplasma1.5_mixed_pdb_fixed'],
|
||||
['curveTest', 'Curve Test'],
|
||||
]),
|
||||
] as const),
|
||||
baseUrl: PD.Text(DefaultCellPackBaseUrl),
|
||||
preset: PD.Group({
|
||||
traceOnly: PD.Boolean(false),
|
||||
representation: PD.Select('spacefill', [
|
||||
['spacefill', 'Spacefill'],
|
||||
['gaussian-surface', 'Gaussian Surface'],
|
||||
['point', 'Point'],
|
||||
] as ['spacefill' | 'gaussian-surface' | 'point', string][])
|
||||
representation: PD.Select('spacefill', RepresentationOptions)
|
||||
}, { isExpanded: true })
|
||||
},
|
||||
from: PSO.Root
|
||||
@@ -393,7 +394,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
MS.core.rel.eq([MS.ammp('label_atom_id'), 'P'])
|
||||
])
|
||||
})
|
||||
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } })
|
||||
cellpackTree = cellpackTree.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression }, { state: { isGhost: true } }) as any
|
||||
}
|
||||
cellpackTree
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
@@ -425,7 +426,7 @@ export const LoadCellPackModel = StateAction.build({
|
||||
console.timeEnd('cellpack')
|
||||
}));
|
||||
|
||||
function getReprParams(ctx: PluginContext, params: { representation: 'spacefill' | 'gaussian-surface' | 'point', traceOnly: boolean }) {
|
||||
function getReprParams(ctx: PluginContext, params: { representation: RepresentationName, traceOnly: boolean }) {
|
||||
const { representation, traceOnly } = params
|
||||
switch (representation) {
|
||||
case 'spacefill':
|
||||
@@ -452,6 +453,11 @@ function getReprParams(ctx: PluginContext, params: { representation: 'spacefill'
|
||||
ctx.structureRepresentation.registry.get('point'),
|
||||
() => ({ ignoreHydrogens: true })
|
||||
] as [any, any]
|
||||
case 'ellipsoid':
|
||||
return [
|
||||
ctx.structureRepresentation.registry.get('orientation'),
|
||||
() => ({})
|
||||
] as [any, any]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,167 +1,168 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
// /**
|
||||
// * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
// *
|
||||
// * @author David Sehnal <david.sehnal@gmail.com>
|
||||
// */
|
||||
|
||||
import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
|
||||
import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { PluginStateObject } from '../../../mol-plugin/state/objects';
|
||||
import { ParamDefinition } from '../../../mol-util/param-definition';
|
||||
import { PluginCommands } from '../../../mol-plugin/command';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
|
||||
import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
import { UUID } from '../../../mol-util';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { Camera } from '../../../mol-canvas3d/camera';
|
||||
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
|
||||
// import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
|
||||
// import { StateTransforms } from '../../../mol-plugin/state/transforms';
|
||||
// import { createModelTree } from '../../../mol-plugin/state/actions/structure';
|
||||
// import { PluginContext } from '../../../mol-plugin/context';
|
||||
// import { PluginStateObject } from '../../../mol-plugin/state/objects';
|
||||
// import { ParamDefinition } from '../../../mol-util/param-definition';
|
||||
// import { PluginCommands } from '../../../mol-plugin/command';
|
||||
// import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
// import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
|
||||
// import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
|
||||
// import { Text } from '../../../mol-geo/geometry/text/text';
|
||||
// import { UUID } from '../../../mol-util';
|
||||
// import { ColorNames } from '../../../mol-util/color/names';
|
||||
// import { Camera } from '../../../mol-canvas3d/camera';
|
||||
// import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
|
||||
// import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
|
||||
|
||||
export const CreateJoleculeState = StateAction.build({
|
||||
display: { name: 'Jolecule State Import' },
|
||||
params: { id: ParamDefinition.Text('1mbo') },
|
||||
from: PluginStateObject.Root
|
||||
})(async ({ ref, state, params }, plugin: PluginContext) => {
|
||||
try {
|
||||
const id = params.id.trim().toLowerCase();
|
||||
const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
|
||||
// export const CreateJoleculeState = StateAction.build({
|
||||
// display: { name: 'Jolecule State Import' },
|
||||
// params: { id: ParamDefinition.Text('1mbo') },
|
||||
// from: PluginStateObject.Root
|
||||
// })(async ({ ref, state, params }, plugin: PluginContext) => {
|
||||
// try {
|
||||
// const id = params.id.trim().toLowerCase();
|
||||
// const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
|
||||
|
||||
data.sort((a, b) => a.order - b.order);
|
||||
// data.sort((a, b) => a.order - b.order);
|
||||
|
||||
await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
|
||||
plugin.state.snapshots.clear();
|
||||
// await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
|
||||
// plugin.state.snapshots.clear();
|
||||
|
||||
const template = createTemplate(plugin, state, id);
|
||||
const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
|
||||
for (const s of snapshots) {
|
||||
plugin.state.snapshots.add(s);
|
||||
}
|
||||
// const template = createTemplate(plugin, state, id);
|
||||
// const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
|
||||
// for (const s of snapshots) {
|
||||
// plugin.state.snapshots.add(s);
|
||||
// }
|
||||
|
||||
PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
|
||||
} catch (e) {
|
||||
plugin.log.error(`Jolecule Failed: ${e}`);
|
||||
}
|
||||
});
|
||||
// PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
|
||||
// } catch (e) {
|
||||
// plugin.log.error(`Jolecule Failed: ${e}`);
|
||||
// }
|
||||
// });
|
||||
|
||||
interface JoleculeSnapshot {
|
||||
order: number,
|
||||
distances: { i_atom1: number, i_atom2: number }[],
|
||||
labels: { i_atom: number, text: string }[],
|
||||
camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
|
||||
selected: number[],
|
||||
text: string
|
||||
}
|
||||
// interface JoleculeSnapshot {
|
||||
// order: number,
|
||||
// distances: { i_atom1: number, i_atom2: number }[],
|
||||
// labels: { i_atom: number, text: string }[],
|
||||
// camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
|
||||
// selected: number[],
|
||||
// text: string
|
||||
// }
|
||||
|
||||
function createTemplate(plugin: PluginContext, state: State, id: string) {
|
||||
const b = new StateBuilder.Root(state.tree);
|
||||
const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
|
||||
const model = createModelTree(data, 'cif');
|
||||
const structure = model.apply(StateTransforms.Model.StructureFromModel, {});
|
||||
complexRepresentation(plugin, structure, { hideWater: true });
|
||||
return { tree: b.getTree(), structure: structure.ref };
|
||||
}
|
||||
// function createTemplate(plugin: PluginContext, state: State, id: string) {
|
||||
// const b = new StateBuilder.Root(state.tree);
|
||||
// const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
|
||||
// const model = createModelTree(data, 'cif');
|
||||
// const structure = model.apply(StateTransforms.Model.StructureFromModel);
|
||||
// createDefaultStructureComplex(plugin, structure);
|
||||
// return { tree: b.getTree(), structure: structure.ref };
|
||||
// }
|
||||
|
||||
const labelOptions: ParamDefinition.Values<Text.Params> = {
|
||||
...ParamDefinition.getDefaultValues(Text.Params),
|
||||
tether: true,
|
||||
sizeFactor: 1.3,
|
||||
attachment: 'bottom-right',
|
||||
offsetZ: 10,
|
||||
background: true,
|
||||
backgroundMargin: 0.2,
|
||||
backgroundColor: ColorNames.skyblue,
|
||||
backgroundOpacity: 0.9
|
||||
}
|
||||
|
||||
// const distanceLabelOptions = {
|
||||
// const labelOptions: ParamDefinition.Values<Text.Params> = {
|
||||
// ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// sizeFactor: 1,
|
||||
// offsetX: 0,
|
||||
// offsetY: 0,
|
||||
// tether: true,
|
||||
// sizeFactor: 1.3,
|
||||
// attachment: 'bottom-right',
|
||||
// offsetZ: 10,
|
||||
// background: true,
|
||||
// backgroundMargin: 0.2,
|
||||
// backgroundColor: ColorNames.snow,
|
||||
// backgroundColor: ColorNames.skyblue,
|
||||
// backgroundOpacity: 0.9
|
||||
// }
|
||||
|
||||
function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
|
||||
const b = new StateBuilder.Root(template.tree);
|
||||
// // const distanceLabelOptions = {
|
||||
// // ...ParamDefinition.getDefaultValues(Text.Params),
|
||||
// // sizeFactor: 1,
|
||||
// // offsetX: 0,
|
||||
// // offsetY: 0,
|
||||
// // offsetZ: 10,
|
||||
// // background: true,
|
||||
// // backgroundMargin: 0.2,
|
||||
// // backgroundColor: ColorNames.snow,
|
||||
// // backgroundOpacity: 0.9
|
||||
// // }
|
||||
|
||||
let i = 0;
|
||||
for (const l of params.e.labels) {
|
||||
const expression = createExpression([l.i_atom]);
|
||||
const group = b.to(template.structure)
|
||||
.group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
|
||||
// function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
|
||||
// const b = new StateBuilder.Root(template.tree);
|
||||
|
||||
group
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
.apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
options: labelOptions
|
||||
});
|
||||
// let i = 0;
|
||||
// for (const l of params.e.labels) {
|
||||
// const expression = createExpression([l.i_atom]);
|
||||
// const group = b.to(template.structure)
|
||||
// .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
|
||||
|
||||
group
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
|
||||
}
|
||||
if (params.e.selected && params.e.selected.length > 0) {
|
||||
b.to(template.structure)
|
||||
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
|
||||
.apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
|
||||
}
|
||||
// TODO
|
||||
// for (const l of params.e.distances) {
|
||||
// b.to('structure')
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l. || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
// }
|
||||
return PluginStateSnapshotManager.Entry({
|
||||
id: UUID.create22(),
|
||||
data: { tree: StateTree.toJSON(b.getTree()) },
|
||||
camera: {
|
||||
current: getCameraSnapshot(params.e.camera),
|
||||
transitionStyle: 'animate',
|
||||
transitionDurationInMs: 350
|
||||
}
|
||||
}, {
|
||||
name: params.e.text
|
||||
});
|
||||
}
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression, label: 'Atom' })
|
||||
// .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// target: { name: 'static-text', params: { value: l.text || '' } },
|
||||
// options: labelOptions
|
||||
// });
|
||||
|
||||
function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
const direction = Vec3.sub(Vec3(), e.pos, e.in);
|
||||
Vec3.normalize(direction, direction);
|
||||
const up = Vec3.sub(Vec3(), e.pos, e.up);
|
||||
Vec3.normalize(up, up);
|
||||
// group
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
|
||||
// }
|
||||
// if (params.e.selected && params.e.selected.length > 0) {
|
||||
// b.to(template.structure)
|
||||
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: createExpression(params.e.selected), label: `Selected` })
|
||||
// .apply(StateTransforms.Representation.StructureRepresentation3D,
|
||||
// StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
|
||||
// }
|
||||
// // TODO
|
||||
// // for (const l of params.e.distances) {
|
||||
// // b.to('structure')
|
||||
// // .apply(StateTransforms.Model.StructureSelectionFromExpression, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
|
||||
// // .apply(StateTransforms.Representation.StructureLabels3D, {
|
||||
// // target: { name: 'static-text', params: { value: l. || '' } },
|
||||
// // options: labelOptions
|
||||
// // });
|
||||
// // }
|
||||
// return PluginStateSnapshotManager.Entry({
|
||||
// id: UUID.create22(),
|
||||
// data: { tree: StateTree.toJSON(b.getTree()) },
|
||||
// camera: {
|
||||
// current: getCameraSnapshot(params.e.camera),
|
||||
// transitionStyle: 'animate',
|
||||
// transitionDurationInMs: 350
|
||||
// }
|
||||
// }, {
|
||||
// name: params.e.text
|
||||
// });
|
||||
// }
|
||||
|
||||
const s: Camera.Snapshot = {
|
||||
mode: 'perspective',
|
||||
fov: Math.PI / 4,
|
||||
position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
target: e.pos,
|
||||
radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
fog: 50,
|
||||
up,
|
||||
};
|
||||
return s;
|
||||
}
|
||||
// function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
|
||||
// const direction = Vec3.sub(Vec3(), e.pos, e.in);
|
||||
// Vec3.normalize(direction, direction);
|
||||
// const up = Vec3.sub(Vec3(), e.pos, e.up);
|
||||
// Vec3.normalize(up, up);
|
||||
|
||||
function createExpression(atomIndices: number[]) {
|
||||
if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
// const s: Camera.Snapshot = {
|
||||
// mode: 'perspective',
|
||||
// fov: Math.PI / 4,
|
||||
// position: Vec3.scaleAndAdd(Vec3(), e.pos, direction, e.slab.zoom),
|
||||
// target: e.pos,
|
||||
// radius: (e.slab.z_back - e.slab.z_front) / 2,
|
||||
// fog: 50,
|
||||
// up,
|
||||
// };
|
||||
// return s;
|
||||
// }
|
||||
|
||||
return MS.struct.generator.atomGroups({
|
||||
'atom-test': atomIndices.length === 1
|
||||
? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
: MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
'group-by': 0
|
||||
});
|
||||
}
|
||||
// function createExpression(atomIndices: number[]) {
|
||||
// if (atomIndices.length === 0) return MS.struct.generator.empty();
|
||||
|
||||
// return MS.struct.generator.atomGroups({
|
||||
// 'atom-test': atomIndices.length === 1
|
||||
// ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
|
||||
// : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
|
||||
// 'group-by': 0
|
||||
// });
|
||||
// }
|
||||
@@ -5,16 +5,16 @@
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import '../../mol-util/polyfill';
|
||||
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
|
||||
import './index.html'
|
||||
import './favicon.ico'
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { PluginCommands } from '../../mol-plugin/command';
|
||||
import { PluginSpec } from '../../mol-plugin/spec';
|
||||
import { CreateJoleculeState } from './extensions/jolecule';
|
||||
import { LoadCellPackModel } from './extensions/cellpack/model';
|
||||
import { StructureFromCellpack } from './extensions/cellpack/state';
|
||||
require('mol-plugin/skin/light.scss')
|
||||
require('mol-plugin-ui/skin/light.scss')
|
||||
|
||||
function getParam(name: string, regex: string): string {
|
||||
let r = new RegExp(`${name}=(${regex})[&]?`, 'i');
|
||||
@@ -27,7 +27,7 @@ function init() {
|
||||
const spec: PluginSpec = {
|
||||
actions: [
|
||||
...DefaultPluginSpec.actions,
|
||||
PluginSpec.Action(CreateJoleculeState),
|
||||
// PluginSpec.Action(CreateJoleculeState),
|
||||
PluginSpec.Action(LoadCellPackModel),
|
||||
PluginSpec.Action(StructureFromCellpack),
|
||||
],
|
||||
@@ -42,7 +42,8 @@ function init() {
|
||||
controls: {
|
||||
...DefaultPluginSpec.layout && DefaultPluginSpec.layout.controls
|
||||
}
|
||||
}
|
||||
},
|
||||
config: DefaultPluginSpec.config
|
||||
};
|
||||
const plugin = createPlugin(document.getElementById('app')!, spec);
|
||||
trySetSnapshot(plugin);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { CustomElementProperty } from '../../mol-model-props/common/custom-element-property';
|
||||
import { Model, ElementIndex, ResidueIndex } from '../../mol-model/structure';
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { CustomProperty } from '../../mol-model-props/common/custom-property';
|
||||
|
||||
const EvolutionaryConservationPalette: Color[] = [
|
||||
[255, 255, 129], // insufficient
|
||||
@@ -23,13 +25,13 @@ const EvolutionaryConservationPalette: Color[] = [
|
||||
const EvolutionaryConservationDefaultColor = Color(0x999999);
|
||||
|
||||
export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
isStatic: true,
|
||||
name: 'proteopedia-wrapper-evolutionary-conservation',
|
||||
display: 'Evolutionary Conservation',
|
||||
async getData(model: Model) {
|
||||
label: 'Evolutionary Conservation',
|
||||
type: 'static',
|
||||
async getData(model: Model, ctx: CustomProperty.Context) {
|
||||
const id = model.entryId.toLowerCase();
|
||||
const req = await fetch(`https://proteopedia.org/cgi-bin/cnsrf?${id}`);
|
||||
const json = await req.json();
|
||||
const url = `https://proteopedia.org/cgi-bin/cnsrf?${id}`
|
||||
const json = await ctx.fetch({ url, type: 'json' }).runInContext(ctx.runtime)
|
||||
const annotations = (json && json.residueAnnotations) || [];
|
||||
|
||||
const conservationMap = new Map<string, number>();
|
||||
@@ -65,7 +67,7 @@ export const EvolutionaryConservation = CustomElementProperty.create<number>({
|
||||
},
|
||||
defaultColor: EvolutionaryConservationDefaultColor
|
||||
},
|
||||
format(e) {
|
||||
getLabel(e) {
|
||||
if (e === 10) return `Evolutionary Conservation: InsufficientData`;
|
||||
return e ? `Evolutionary Conservation: ${e}` : void 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import { Unit, StructureProperties, StructureElement, Link } from '../../mol-model/structure';
|
||||
import { Unit, StructureProperties, StructureElement, Bond } from '../../mol-model/structure';
|
||||
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Location } from '../../mol-model/location';
|
||||
@@ -54,7 +54,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
|
||||
|
||||
if (ctx.structure) {
|
||||
const l = StructureElement.Location.create()
|
||||
const l = StructureElement.Location.create(ctx.structure)
|
||||
const { models } = ctx.structure
|
||||
const asymIdSerialMap = new Map<string, number>()
|
||||
for (let i = 0, il = models.length; i < il; ++i) {
|
||||
@@ -71,7 +71,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
|
||||
const asym_id = getAsymId(location.unit);
|
||||
const o = asymIdSerialMap.get(asym_id(location)) || 0;
|
||||
return colors[o % colorCount].color;
|
||||
} else if (Link.isLocation(location)) {
|
||||
} else if (Bond.isLocation(location)) {
|
||||
const asym_id = getAsymId(location.aUnit)
|
||||
l.unit = location.aUnit
|
||||
l.element = location.aUnit.elements[location.aIndex]
|
||||
|
||||
@@ -9,6 +9,7 @@ import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/re
|
||||
import { BuiltInColorThemeName } from '../../mol-theme/color';
|
||||
import { AminoAcidNames } from '../../mol-model/structure/model/types';
|
||||
import { PluginContext } from '../../mol-plugin/context';
|
||||
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
|
||||
|
||||
export interface ModelInfo {
|
||||
hetResidues: { name: string, indices: ResidueIndex[] }[],
|
||||
@@ -72,10 +73,11 @@ export namespace ModelInfo {
|
||||
}
|
||||
|
||||
const preferredAssemblyId = await pref;
|
||||
const symmetry = ModelSymmetry.Provider.get(model)
|
||||
|
||||
return {
|
||||
hetResidues: hetResidues,
|
||||
assemblies: model.symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })),
|
||||
assemblies: symmetry ? symmetry.assemblies.map(a => ({ id: a.id, details: a.details, isPreferred: a.id === preferredAssemblyId })) : [],
|
||||
preferredAssemblyId
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3
|
||||
// import { Vec3 } from 'mol-math/linear-algebra';
|
||||
// import { ParamDefinition } from 'mol-util/param-definition';
|
||||
// import { Text } from 'mol-geo/geometry/text/text';
|
||||
require('../../mol-plugin/skin/light.scss')
|
||||
require('../../mol-plugin-ui/skin/light.scss')
|
||||
|
||||
class MolStarProteopediaWrapper {
|
||||
static VERSION_MAJOR = 3;
|
||||
@@ -68,9 +68,9 @@ class MolStarProteopediaWrapper {
|
||||
const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
|
||||
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
|
||||
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
|
||||
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
|
||||
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
|
||||
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
|
||||
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
|
||||
}
|
||||
|
||||
get state() {
|
||||
@@ -94,7 +94,7 @@ class MolStarProteopediaWrapper {
|
||||
const model = this.state.build().to(StateElements.Model);
|
||||
|
||||
const s = model
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [EvolutionaryConservation.propertyProvider.descriptor.name], properties: {} }, { ref: StateElements.ModelProps, state: { isGhost: false } })
|
||||
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: StateElements.Assembly });
|
||||
|
||||
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
|
||||
@@ -160,9 +160,9 @@ class MolStarProteopediaWrapper {
|
||||
root.delete(StateElements.WaterVisual);
|
||||
} else {
|
||||
root.applyOrUpdate(StateElements.WaterVisual, StateTransforms.Representation.StructureRepresentation3D,
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.water && style.water.kind) || 'ball-and-stick',
|
||||
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
|
||||
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
|
||||
(style.water && style.water.kind) || 'ball-and-stick',
|
||||
(style.water && style.water.coloring), structure, { alpha: 0.51 }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,11 +230,13 @@ class MolStarProteopediaWrapper {
|
||||
}
|
||||
|
||||
setBackground(color: number) {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const renderer = this.plugin.canvas3d.props.renderer;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
|
||||
}
|
||||
|
||||
toggleSpin() {
|
||||
if (!this.plugin.canvas3d) return;
|
||||
const trackball = this.plugin.canvas3d.props.trackball;
|
||||
const spinning = trackball.spin;
|
||||
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
|
||||
@@ -297,7 +299,7 @@ class MolStarProteopediaWrapper {
|
||||
// }
|
||||
|
||||
const tree = state.build();
|
||||
const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
|
||||
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
|
||||
|
||||
if (!params || !!params.sequence) {
|
||||
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
|
||||
@@ -383,7 +385,8 @@ class MolStarProteopediaWrapper {
|
||||
// const position = Vec3.sub(Vec3.zero(), sphere.center, asmCenter);
|
||||
// Vec3.normalize(position, position);
|
||||
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
|
||||
const snapshot = this.plugin.canvas3d.camera.getFocus(sphere.center, Math.max(sphere.radius, 5));
|
||||
const radius = Math.max(sphere.radius, 5)
|
||||
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius, radius);
|
||||
PluginCommands.Camera.SetSnapshot.dispatch(this.plugin, { snapshot, durationMs: 250 });
|
||||
}
|
||||
}
|
||||
@@ -415,8 +418,7 @@ class MolStarProteopediaWrapper {
|
||||
},
|
||||
download: async (url: string) => {
|
||||
try {
|
||||
const data = await this.plugin.runTask(this.plugin.fetch({ url }));
|
||||
const snapshot = JSON.parse(data);
|
||||
const snapshot = await this.plugin.runTask(this.plugin.fetch({ url, type: 'json' }));
|
||||
await this.plugin.state.setSnapshot(snapshot);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { PluginUIComponent } from '../../../mol-plugin/ui/base';
|
||||
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin/ui/plugin';
|
||||
import { AnimationControls } from '../../../mol-plugin/ui/state/animation';
|
||||
import { CameraSnapshots } from '../../../mol-plugin/ui/camera';
|
||||
import { PluginUIComponent } from '../../../mol-plugin-ui/base';
|
||||
import { CurrentObject, PluginContextContainer } from '../../../mol-plugin-ui/plugin';
|
||||
import { AnimationControls } from '../../../mol-plugin-ui/state/animation';
|
||||
import { CameraSnapshots } from '../../../mol-plugin-ui/camera';
|
||||
import { PluginContext } from '../../../mol-plugin/context';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin/ui/state/update-transform';
|
||||
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
|
||||
import { StateElements } from '../helpers';
|
||||
|
||||
export class ControlsWrapper extends PluginUIComponent {
|
||||
|
||||
@@ -82,12 +82,12 @@ class Camera {
|
||||
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
|
||||
}
|
||||
|
||||
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
getFocus(target: Vec3, radiusNear: number, radiusFar: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
|
||||
const fov = this.state.fov
|
||||
const { width, height } = this.viewport
|
||||
const aspect = width / height
|
||||
const aspectFactor = (height < width ? 1 : aspect)
|
||||
const targetDistance = Math.abs((radius / aspectFactor) / Math.sin(fov / 2))
|
||||
const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
|
||||
|
||||
Vec3.sub(this.deltaDirection, this.target, this.position)
|
||||
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
|
||||
@@ -96,15 +96,18 @@ class Camera {
|
||||
|
||||
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
|
||||
state.target = Vec3.clone(target)
|
||||
state.radius = radius
|
||||
state.radiusNear = radiusNear
|
||||
state.radiusFar = radiusFar
|
||||
state.position = Vec3.clone(this.newPosition)
|
||||
if (up) Vec3.matchDirection(state.up, up, state.up)
|
||||
|
||||
return state
|
||||
}
|
||||
|
||||
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
|
||||
if (radius > 0) this.setState(this.getFocus(target, radius, up, dir), durationMs);
|
||||
focus(target: Vec3, radiusNear: number, radiusFar: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
|
||||
if (radiusNear > 0 && radiusFar > 0) {
|
||||
this.setState(this.getFocus(target, radiusNear, radiusFar, up, dir), durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
project(out: Vec4, point: Vec3) {
|
||||
@@ -158,8 +161,10 @@ namespace Camera {
|
||||
up: Vec3.create(0, 1, 0),
|
||||
target: Vec3.create(0, 0, 0),
|
||||
|
||||
radius: 10,
|
||||
radiusNear: 10,
|
||||
radiusFar: 10,
|
||||
fog: 50,
|
||||
clipFar: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,8 +176,10 @@ namespace Camera {
|
||||
up: Vec3
|
||||
target: Vec3
|
||||
|
||||
radius: number
|
||||
radiusNear: number
|
||||
radiusFar: number
|
||||
fog: number
|
||||
clipFar: boolean
|
||||
}
|
||||
|
||||
export function copySnapshot(out: Snapshot, source?: Partial<Snapshot>) {
|
||||
@@ -185,8 +192,10 @@ namespace Camera {
|
||||
if (typeof source.up !== 'undefined') Vec3.copy(out.up, source.up);
|
||||
if (typeof source.target !== 'undefined') Vec3.copy(out.target, source.target);
|
||||
|
||||
if (typeof source.radius !== 'undefined') out.radius = source.radius;
|
||||
if (typeof source.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
|
||||
if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
|
||||
if (typeof source.fog !== 'undefined') out.fog = source.fog;
|
||||
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -253,17 +262,15 @@ function updatePers(camera: Camera) {
|
||||
}
|
||||
|
||||
function updateClip(camera: Camera) {
|
||||
const { radius, mode, fog } = camera.state
|
||||
const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
|
||||
|
||||
const cDist = Vec3.distance(camera.position, camera.target)
|
||||
const bRadius = Math.max(1, radius)
|
||||
|
||||
let near = cDist - bRadius
|
||||
let far = cDist + bRadius
|
||||
let near = cDist - radiusNear
|
||||
let far = cDist + (clipFar ? radiusNear : radiusFar)
|
||||
|
||||
const fogNearFactor = -(50 - fog) / 50
|
||||
let fogNear = cDist - (bRadius * fogNearFactor)
|
||||
let fogFar = cDist + bRadius
|
||||
let fogNear = cDist - (radiusNear * fogNearFactor)
|
||||
let fogFar = far
|
||||
|
||||
if (mode === 'perspective') {
|
||||
// set at least to 5 to avoid slow sphere impostor rendering
|
||||
|
||||
@@ -79,7 +79,9 @@ namespace CameraTransitionManager {
|
||||
// Lerp target, position & radius
|
||||
Vec3.lerp(out.target, source.target, target.target, t);
|
||||
Vec3.lerp(out.position, source.position, target.position, t);
|
||||
out.radius = lerp(source.radius, target.radius, t);
|
||||
out.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
|
||||
// TODO take change of `clipFar` into account
|
||||
out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
|
||||
|
||||
// Lerp fov & fog
|
||||
out.fov = lerp(source.fov, target.fov, t);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
||||
@@ -26,18 +27,21 @@ import { SetUtils } from '../mol-util/set';
|
||||
import { Canvas3dInteractionHelper } from './helper/interaction-events';
|
||||
import { PostprocessingParams, PostprocessingPass } from './passes/postprocessing';
|
||||
import { MultiSampleParams, MultiSamplePass } from './passes/multi-sample';
|
||||
import { GLRenderingContext } from '../mol-gl/webgl/compat';
|
||||
import { PixelData } from '../mol-util/image';
|
||||
import { readTexture } from '../mol-gl/compute/util';
|
||||
import { DrawPass } from './passes/draw';
|
||||
import { PickPass } from './passes/pick';
|
||||
import { Task } from '../mol-task';
|
||||
import { ImagePass, ImageProps } from './passes/image';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { isDebugMode } from '../mol-util/debug';
|
||||
|
||||
export const Canvas3DParams = {
|
||||
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']]),
|
||||
cameraFog: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
|
||||
cameraClipFar: PD.Boolean(true),
|
||||
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
|
||||
transparentBackground: PD.Boolean(false),
|
||||
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
@@ -53,29 +57,33 @@ export { Canvas3D }
|
||||
interface Canvas3D {
|
||||
readonly webgl: WebGLContext,
|
||||
|
||||
add: (repr: Representation.Any) => void
|
||||
remove: (repr: Representation.Any) => void
|
||||
update: (repr?: Representation.Any, keepBoundingSphere?: boolean) => void
|
||||
clear: () => void
|
||||
add(repr: Representation.Any): void
|
||||
remove(repr: Representation.Any): void
|
||||
/**
|
||||
* This function must be called if animate() is not set up so that add/remove actions take place.
|
||||
*/
|
||||
commit(): void
|
||||
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
|
||||
clear(): void
|
||||
|
||||
// draw: (force?: boolean) => void
|
||||
requestDraw: (force?: boolean) => void
|
||||
animate: () => void
|
||||
identify: (x: number, y: number) => PickingId | undefined
|
||||
mark: (loci: Representation.Loci, action: MarkerAction) => void
|
||||
getLoci: (pickingId: PickingId) => Representation.Loci
|
||||
requestDraw(force?: boolean): void
|
||||
animate(): void
|
||||
identify(x: number, y: number): PickingId | undefined
|
||||
mark(loci: Representation.Loci, action: MarkerAction): void
|
||||
getLoci(pickingId: PickingId): Representation.Loci
|
||||
|
||||
readonly didDraw: BehaviorSubject<now.Timestamp>
|
||||
readonly reprCount: BehaviorSubject<number>
|
||||
|
||||
handleResize: () => void
|
||||
handleResize(): void
|
||||
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
|
||||
resetCamera: () => void
|
||||
requestCameraReset(): void
|
||||
readonly camera: Camera
|
||||
downloadScreenshot: () => void
|
||||
getPixelData: (variant: GraphicsRenderVariant) => PixelData
|
||||
setProps: (props: Partial<Canvas3DProps>) => void
|
||||
getImagePass: () => ImagePass
|
||||
readonly boundingSphere: Readonly<Sphere3D>
|
||||
downloadScreenshot(): void
|
||||
getPixelData(variant: GraphicsRenderVariant): PixelData
|
||||
setProps(props: Partial<Canvas3DProps>): void
|
||||
getImagePass(): ImagePass
|
||||
|
||||
/** Returns a copy of the current Canvas3D instance props */
|
||||
readonly props: Readonly<Canvas3DProps>
|
||||
@@ -83,15 +91,15 @@ interface Canvas3D {
|
||||
readonly stats: RendererStats
|
||||
readonly interaction: Canvas3dInteractionHelper['events']
|
||||
|
||||
dispose: () => void
|
||||
dispose(): void
|
||||
}
|
||||
|
||||
const requestAnimationFrame = typeof window !== 'undefined' ? window.requestAnimationFrame : (f: (time: number) => void) => setImmediate(()=>f(Date.now()))
|
||||
const DefaultRunTask = (task: Task<unknown>) => task.run()
|
||||
|
||||
namespace Canvas3D {
|
||||
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, modifiers: ModifiersKeys }
|
||||
export interface HoverEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
export interface ClickEvent { current: Representation.Loci, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys }
|
||||
|
||||
export function fromCanvas(canvas: HTMLCanvasElement, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask) {
|
||||
const gl = getGLContext(canvas, {
|
||||
@@ -103,10 +111,45 @@ namespace Canvas3D {
|
||||
})
|
||||
if (gl === null) throw new Error('Could not create a WebGL rendering context')
|
||||
const input = InputObserver.fromElement(canvas)
|
||||
return Canvas3D.create(gl, input, props, runTask)
|
||||
const webgl = createContext(gl)
|
||||
|
||||
if (isDebugMode) {
|
||||
const loseContextExt = gl.getExtension('WEBGL_lose_context')
|
||||
if (loseContextExt) {
|
||||
canvas.addEventListener('mousedown', e => {
|
||||
if (webgl.isContextLost) return
|
||||
if (!e.shiftKey || !e.ctrlKey || !e.altKey) return
|
||||
|
||||
console.log('lose context')
|
||||
loseContextExt.loseContext()
|
||||
|
||||
setTimeout(() => {
|
||||
if (!webgl.isContextLost) return
|
||||
console.log('restore context')
|
||||
loseContextExt.restoreContext()
|
||||
}, 1000)
|
||||
}, false)
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.khronos.org/webgl/wiki/HandlingContextLost
|
||||
|
||||
canvas.addEventListener('webglcontextlost', e => {
|
||||
webgl.setContextLost()
|
||||
e.preventDefault()
|
||||
if (isDebugMode) console.log('context lost')
|
||||
}, false)
|
||||
|
||||
canvas.addEventListener('webglcontextrestored', () => {
|
||||
if (!webgl.isContextLost) return
|
||||
webgl.handleContextRestored()
|
||||
if (isDebugMode) console.log('context restored')
|
||||
}, false)
|
||||
|
||||
return Canvas3D.create(webgl, input, props, runTask)
|
||||
}
|
||||
|
||||
export function create(gl: GLRenderingContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
|
||||
export function create(webgl: WebGLContext, input: InputObserver, props: Partial<Canvas3DProps> = {}, runTask = DefaultRunTask): Canvas3D {
|
||||
const p = { ...DefaultCanvas3DParams, ...props }
|
||||
|
||||
const reprRenderObjects = new Map<Representation.Any, Set<GraphicsRenderObject>>()
|
||||
@@ -116,7 +159,7 @@ namespace Canvas3D {
|
||||
const startTime = now()
|
||||
const didDraw = new BehaviorSubject<now.Timestamp>(0 as now.Timestamp)
|
||||
|
||||
const webgl = createContext(gl)
|
||||
const { gl, contextRestored } = webgl
|
||||
|
||||
let width = gl.drawingBufferWidth
|
||||
let height = gl.drawingBufferHeight
|
||||
@@ -126,7 +169,8 @@ namespace Canvas3D {
|
||||
const camera = new Camera({
|
||||
position: Vec3.create(0, 0, 100),
|
||||
mode: p.cameraMode,
|
||||
fog: p.cameraFog
|
||||
fog: p.cameraFog,
|
||||
clipFar: p.cameraClipFar
|
||||
})
|
||||
|
||||
const controls = TrackballControls.create(input, camera, p.trackball)
|
||||
@@ -139,6 +183,11 @@ namespace Canvas3D {
|
||||
const postprocessing = new PostprocessingPass(webgl, camera, drawPass, p.postprocessing)
|
||||
const multiSample = new MultiSamplePass(webgl, camera, drawPass, postprocessing, p.multiSample)
|
||||
|
||||
const contextRestoredSub = contextRestored.subscribe(() => {
|
||||
pickPass.pickDirty = true
|
||||
draw(true)
|
||||
})
|
||||
|
||||
let drawPending = false
|
||||
let cameraResetRequested = false
|
||||
|
||||
@@ -174,8 +223,8 @@ namespace Canvas3D {
|
||||
}
|
||||
}
|
||||
|
||||
function render(variant: 'pick' | 'draw', force: boolean) {
|
||||
if (scene.isCommiting) return false
|
||||
function render(force: boolean) {
|
||||
if (webgl.isContextLost) return false
|
||||
|
||||
let didRender = false
|
||||
controls.update(currentTime)
|
||||
@@ -184,21 +233,14 @@ namespace Canvas3D {
|
||||
multiSample.update(force || cameraChanged, currentTime)
|
||||
|
||||
if (force || cameraChanged || multiSample.enabled) {
|
||||
switch (variant) {
|
||||
case 'pick':
|
||||
pickPass.render()
|
||||
break;
|
||||
case 'draw':
|
||||
renderer.setViewport(0, 0, width, height)
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render(true)
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
break;
|
||||
renderer.setViewport(0, 0, width, height)
|
||||
if (multiSample.enabled) {
|
||||
multiSample.render(true, p.transparentBackground)
|
||||
} else {
|
||||
drawPass.render(!postprocessing.enabled, p.transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(true)
|
||||
}
|
||||
pickPass.pickDirty = true
|
||||
didRender = true
|
||||
}
|
||||
|
||||
@@ -209,7 +251,7 @@ namespace Canvas3D {
|
||||
let currentTime = 0;
|
||||
|
||||
function draw(force?: boolean) {
|
||||
if (render('draw', !!force || forceNextDraw)) {
|
||||
if (render(!!force || forceNextDraw)) {
|
||||
didDraw.next(now() - startTime as now.Timestamp)
|
||||
}
|
||||
forceNextDraw = false;
|
||||
@@ -224,45 +266,88 @@ namespace Canvas3D {
|
||||
|
||||
function animate() {
|
||||
currentTime = now();
|
||||
|
||||
commit();
|
||||
|
||||
camera.transition.tick(currentTime);
|
||||
|
||||
draw(false);
|
||||
if (!camera.transition.inTransition) interactionHelper.tick(currentTime);
|
||||
if (!camera.transition.inTransition && !webgl.isContextLost) {
|
||||
interactionHelper.tick(currentTime);
|
||||
}
|
||||
requestAnimationFrame(animate)
|
||||
}
|
||||
|
||||
function identify(x: number, y: number): PickingId | undefined {
|
||||
return pickPass.identify(x, y)
|
||||
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
|
||||
}
|
||||
|
||||
function commit(renderObjects?: readonly GraphicsRenderObject[]) {
|
||||
scene.update(renderObjects, false)
|
||||
function commit() {
|
||||
commitScene();
|
||||
resolveCameraReset();
|
||||
}
|
||||
|
||||
runTask(scene.commit()).then(() => {
|
||||
if (cameraResetRequested && !scene.isCommiting) {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius)
|
||||
cameraResetRequested = false
|
||||
}
|
||||
if (debugHelper.isEnabled) debugHelper.update()
|
||||
requestDraw(true)
|
||||
reprCount.next(reprRenderObjects.size)
|
||||
})
|
||||
function resolveCameraReset() {
|
||||
if (!cameraResetRequested) return;
|
||||
const { center, radius } = scene.boundingSphere;
|
||||
camera.focus(center, radius, radius, p.cameraResetDurationMs);
|
||||
cameraResetRequested = false;
|
||||
}
|
||||
|
||||
const sceneCommitTimeoutMs = 250;
|
||||
function commitScene() {
|
||||
if (!scene.needsCommit) return;
|
||||
|
||||
const allCommited = scene.commit(sceneCommitTimeoutMs);
|
||||
if (debugHelper.isEnabled) debugHelper.update();
|
||||
if (allCommited) reprCount.next(reprRenderObjects.size);
|
||||
}
|
||||
|
||||
function add(repr: Representation.Any) {
|
||||
registerAutoUpdate(repr);
|
||||
|
||||
const oldRO = reprRenderObjects.get(repr)
|
||||
const newRO = new Set<GraphicsRenderObject>()
|
||||
repr.renderObjects.forEach(o => newRO.add(o))
|
||||
|
||||
if (oldRO) {
|
||||
if (!SetUtils.areEqual(newRO, oldRO)) {
|
||||
for (const o of Array.from(newRO)) { if (!oldRO.has(o)) scene.add(o); }
|
||||
for (const o of Array.from(oldRO)) { if (!newRO.has(o)) scene.remove(o) }
|
||||
newRO.forEach(o => { if (!oldRO.has(o)) scene.add(o) })
|
||||
oldRO.forEach(o => { if (!newRO.has(o)) scene.remove(o) })
|
||||
}
|
||||
} else {
|
||||
repr.renderObjects.forEach(o => scene.add(o))
|
||||
}
|
||||
reprRenderObjects.set(repr, newRO)
|
||||
commit(repr.renderObjects)
|
||||
|
||||
scene.update(repr.renderObjects, false)
|
||||
}
|
||||
|
||||
function remove(repr: Representation.Any) {
|
||||
unregisterAutoUpdate(repr);
|
||||
|
||||
const renderObjects = reprRenderObjects.get(repr)
|
||||
if (renderObjects) {
|
||||
renderObjects.forEach(o => scene.remove(o))
|
||||
reprRenderObjects.delete(repr)
|
||||
scene.update(repr.renderObjects, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
function registerAutoUpdate(repr: Representation.Any) {
|
||||
if (reprUpdatedSubscriptions.has(repr)) return;
|
||||
|
||||
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
||||
if (!repr.state.syncManually) add(repr);
|
||||
}))
|
||||
}
|
||||
|
||||
function unregisterAutoUpdate(repr: Representation.Any) {
|
||||
const updatedSubscription = reprUpdatedSubscriptions.get(repr);
|
||||
if (updatedSubscription) {
|
||||
updatedSubscription.unsubscribe();
|
||||
reprUpdatedSubscriptions.delete(repr);
|
||||
}
|
||||
}
|
||||
|
||||
handleResize()
|
||||
@@ -270,24 +355,9 @@ namespace Canvas3D {
|
||||
return {
|
||||
webgl,
|
||||
|
||||
add: (repr: Representation.Any) => {
|
||||
add(repr)
|
||||
reprUpdatedSubscriptions.set(repr, repr.updated.subscribe(_ => {
|
||||
if (!repr.state.syncManually) add(repr)
|
||||
}))
|
||||
},
|
||||
remove: (repr: Representation.Any) => {
|
||||
const updatedSubscription = reprUpdatedSubscriptions.get(repr)
|
||||
if (updatedSubscription) {
|
||||
updatedSubscription.unsubscribe()
|
||||
}
|
||||
const renderObjects = reprRenderObjects.get(repr)
|
||||
if (renderObjects) {
|
||||
renderObjects.forEach(o => scene.remove(o))
|
||||
reprRenderObjects.delete(repr)
|
||||
commit()
|
||||
}
|
||||
},
|
||||
add,
|
||||
remove,
|
||||
commit,
|
||||
update: (repr, keepSphere) => {
|
||||
if (repr) {
|
||||
if (!reprRenderObjects.has(repr)) return;
|
||||
@@ -297,6 +367,8 @@ namespace Canvas3D {
|
||||
}
|
||||
},
|
||||
clear: () => {
|
||||
reprUpdatedSubscriptions.forEach(v => v.unsubscribe())
|
||||
reprUpdatedSubscriptions.clear()
|
||||
reprRenderObjects.clear()
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
@@ -312,15 +384,11 @@ namespace Canvas3D {
|
||||
getLoci,
|
||||
|
||||
handleResize,
|
||||
resetCamera: () => {
|
||||
if (scene.isCommiting) {
|
||||
cameraResetRequested = true
|
||||
} else {
|
||||
camera.focus(scene.boundingSphere.center, scene.boundingSphere.radius, p.cameraResetDurationMs)
|
||||
requestDraw(true);
|
||||
}
|
||||
requestCameraReset: () => {
|
||||
cameraResetRequested = true;
|
||||
},
|
||||
camera,
|
||||
boundingSphere: scene.boundingSphere,
|
||||
downloadScreenshot: () => {
|
||||
// TODO
|
||||
},
|
||||
@@ -342,7 +410,11 @@ namespace Canvas3D {
|
||||
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
|
||||
camera.setState({ fog: props.cameraFog })
|
||||
}
|
||||
if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
|
||||
camera.setState({ clipFar: props.cameraClipFar })
|
||||
}
|
||||
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
|
||||
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
|
||||
|
||||
if (props.postprocessing) postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) multiSample.setProps(props.multiSample)
|
||||
@@ -359,7 +431,9 @@ namespace Canvas3D {
|
||||
return {
|
||||
cameraMode: camera.state.mode,
|
||||
cameraFog: camera.state.fog,
|
||||
cameraClipFar: camera.state.clipFar,
|
||||
cameraResetDurationMs: p.cameraResetDurationMs,
|
||||
transparentBackground: p.transparentBackground,
|
||||
|
||||
postprocessing: { ...postprocessing.props },
|
||||
multiSample: { ...multiSample.props },
|
||||
@@ -378,6 +452,8 @@ namespace Canvas3D {
|
||||
return interactionHelper.events
|
||||
},
|
||||
dispose: () => {
|
||||
contextRestoredSub.unsubscribe()
|
||||
|
||||
scene.clear()
|
||||
debugHelper.clear()
|
||||
input.dispose()
|
||||
|
||||
@@ -208,8 +208,8 @@ namespace TrackballControls {
|
||||
function focusCamera() {
|
||||
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
|
||||
if (factor !== 0.0) {
|
||||
const radius = Math.max(1, camera.state.radius + 10 * factor)
|
||||
camera.setState({ radius })
|
||||
const radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
|
||||
camera.setState({ radiusNear })
|
||||
}
|
||||
|
||||
if (p.staticMoving) {
|
||||
@@ -343,7 +343,7 @@ namespace TrackballControls {
|
||||
if (dragFocus) Vec2.copy(_focusEnd, mouseOnScreenVec2)
|
||||
if (dragFocusZoom) {
|
||||
const dist = Vec3.distance(camera.state.position, camera.state.target);
|
||||
camera.setState({ radius: dist / 5 })
|
||||
camera.setState({ radiusNear: dist / 5 })
|
||||
}
|
||||
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export class BoundingSphereHelper {
|
||||
})
|
||||
|
||||
this.scene.update(void 0, false)
|
||||
this.scene.syncCommit()
|
||||
this.scene.commit()
|
||||
}
|
||||
|
||||
syncVisibility() {
|
||||
|
||||
@@ -38,6 +38,7 @@ export class Canvas3dInteractionHelper {
|
||||
private inside = false;
|
||||
|
||||
private buttons: ButtonsType = ButtonsType.create(0);
|
||||
private button: ButtonsType.Flag = ButtonsType.create(0);
|
||||
private modifiers: ModifiersKeys = ModifiersKeys.None;
|
||||
|
||||
private identify(isClick: boolean, t: number) {
|
||||
@@ -50,7 +51,7 @@ export class Canvas3dInteractionHelper {
|
||||
if (!this.id) return;
|
||||
|
||||
if (isClick) {
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.click.next({ current: this.getLoci(this.id), buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ export class Canvas3dInteractionHelper {
|
||||
const loci = this.getLoci(this.id);
|
||||
// only broadcast the latest hover
|
||||
if (!Representation.Loci.areEqual(this.prevLoci, loci)) {
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: loci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
this.prevLoci = loci;
|
||||
}
|
||||
}
|
||||
@@ -78,22 +79,24 @@ export class Canvas3dInteractionHelper {
|
||||
this.inside = false;
|
||||
if (this.prevLoci.loci !== EmptyLoci) {
|
||||
this.prevLoci = Representation.Loci.Empty;
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
}
|
||||
|
||||
move(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
|
||||
move(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.inside = true;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
}
|
||||
|
||||
select(x: number, y: number, buttons: ButtonsType, modifiers: ModifiersKeys) {
|
||||
select(x: number, y: number, buttons: ButtonsType, button: ButtonsType.Flag, modifiers: ModifiersKeys) {
|
||||
this.cX = x;
|
||||
this.cY = y;
|
||||
this.buttons = buttons;
|
||||
this.button = button;
|
||||
this.modifiers = modifiers;
|
||||
this.identify(true, 0);
|
||||
}
|
||||
@@ -101,7 +104,7 @@ export class Canvas3dInteractionHelper {
|
||||
modify(modifiers: ModifiersKeys) {
|
||||
if (this.prevLoci.loci === EmptyLoci || ModifiersKeys.areEqual(modifiers, this.modifiers)) return;
|
||||
this.modifiers = modifiers;
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, modifiers: this.modifiers });
|
||||
this.events.hover.next({ current: this.prevLoci, buttons: this.buttons, button: this.button, modifiers: this.modifiers });
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -109,17 +112,17 @@ export class Canvas3dInteractionHelper {
|
||||
}
|
||||
|
||||
constructor(private canvasIdentify: Canvas3D['identify'], private getLoci: Canvas3D['getLoci'], input: InputObserver, private maxFps: number = 15) {
|
||||
input.move.subscribe(({x, y, inside, buttons, modifiers }) => {
|
||||
input.move.subscribe(({x, y, inside, buttons, button, modifiers }) => {
|
||||
if (!inside) return;
|
||||
this.move(x, y, buttons, modifiers);
|
||||
this.move(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.leave.subscribe(() => {
|
||||
this.leave();
|
||||
});
|
||||
|
||||
input.click.subscribe(({x, y, buttons, modifiers }) => {
|
||||
this.select(x, y, buttons, modifiers);
|
||||
input.click.subscribe(({x, y, buttons, button, modifiers }) => {
|
||||
this.select(x, y, buttons, button, modifiers);
|
||||
});
|
||||
|
||||
input.modifiers.subscribe(modifiers => this.modify(modifiers));
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { BoundingSphereHelper } from '../helper/bounding-sphere-helper';
|
||||
import { createTexture, Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../mol-gl/webgl/texture';
|
||||
import { Camera } from '../camera';
|
||||
|
||||
export class DrawPass {
|
||||
@@ -20,13 +20,13 @@ export class DrawPass {
|
||||
private depthTarget: RenderTarget | null
|
||||
|
||||
constructor(private webgl: WebGLContext, private renderer: Renderer, private scene: Scene, private camera: Camera, private debugHelper: BoundingSphereHelper) {
|
||||
const { gl, extensions } = webgl
|
||||
const { gl, extensions, resources } = webgl
|
||||
const width = gl.drawingBufferWidth
|
||||
const height = gl.drawingBufferHeight
|
||||
this.colorTarget = createRenderTarget(webgl, width, height)
|
||||
this.colorTarget = webgl.createRenderTarget(width, height)
|
||||
this.packedDepth = !extensions.depthTexture
|
||||
this.depthTarget = this.packedDepth ? createRenderTarget(webgl, width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : createTexture(webgl, 'image-depth', 'depth', 'ushort', 'nearest')
|
||||
this.depthTarget = this.packedDepth ? webgl.createRenderTarget(width, height) : null
|
||||
this.depthTexture = this.depthTarget ? this.depthTarget.texture : resources.texture('image-depth', 'depth', 'ushort', 'nearest')
|
||||
if (!this.packedDepth) {
|
||||
this.depthTexture.define(width, height)
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
@@ -42,29 +42,33 @@ export class DrawPass {
|
||||
}
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { webgl, renderer, scene, camera, debugHelper, colorTarget, depthTarget } = this
|
||||
if (toDrawingBuffer) {
|
||||
webgl.unbindFramebuffer()
|
||||
} else {
|
||||
colorTarget.bind()
|
||||
if (!this.packedDepth) {
|
||||
// TODO unlcear why it is not enough to call `attachFramebuffer` in `Texture.reset`
|
||||
this.depthTexture.attachFramebuffer(this.colorTarget.framebuffer, 'depth')
|
||||
}
|
||||
}
|
||||
|
||||
renderer.setViewport(0, 0, colorTarget.width, colorTarget.height)
|
||||
renderer.render(scene, camera, 'color', true)
|
||||
renderer.setViewport(0, 0, colorTarget.getWidth(), colorTarget.getHeight())
|
||||
renderer.render(scene, camera, 'color', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, camera, 'color', false)
|
||||
renderer.render(debugHelper.scene, camera, 'color', false, transparentBackground)
|
||||
}
|
||||
|
||||
// do a depth pass if not rendering to drawing buffer and
|
||||
// extensions.depthTexture is unsupported (i.e. depthTarget is set)
|
||||
if (!toDrawingBuffer && depthTarget) {
|
||||
depthTarget.bind()
|
||||
renderer.render(scene, camera, 'depth', true)
|
||||
renderer.render(scene, camera, 'depth', true, transparentBackground)
|
||||
if (debugHelper.isEnabled) {
|
||||
debugHelper.syncVisibility()
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false)
|
||||
renderer.render(debugHelper.scene, camera, 'depth', false, transparentBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Camera } from '../camera';
|
||||
import { Viewport } from '../camera/util';
|
||||
|
||||
export const ImageParams = {
|
||||
transparentBackground: PD.Boolean(false),
|
||||
multiSample: PD.Group(MultiSampleParams),
|
||||
postprocessing: PD.Group(PostprocessingParams),
|
||||
}
|
||||
@@ -26,6 +27,7 @@ export class ImagePass {
|
||||
private _width = 1024
|
||||
private _height = 768
|
||||
private _camera = new Camera()
|
||||
private _transparentBackground = false
|
||||
|
||||
private _colorTarget: RenderTarget
|
||||
get colorTarget() { return this._colorTarget }
|
||||
@@ -40,6 +42,8 @@ export class ImagePass {
|
||||
constructor(webgl: WebGLContext, private renderer: Renderer, scene: Scene, private camera: Camera, debugHelper: BoundingSphereHelper, props: Partial<ImageProps>) {
|
||||
const p = { ...PD.getDefaultValues(ImageParams), ...props }
|
||||
|
||||
this._transparentBackground = p.transparentBackground
|
||||
|
||||
this.drawPass = new DrawPass(webgl, renderer, scene, this._camera, debugHelper)
|
||||
this.postprocessing = new PostprocessingPass(webgl, this._camera, this.drawPass, p.postprocessing)
|
||||
this.multiSample = new MultiSamplePass(webgl, this._camera, this.drawPass, this.postprocessing, p.multiSample)
|
||||
@@ -48,6 +52,8 @@ export class ImagePass {
|
||||
}
|
||||
|
||||
setSize(width: number, height: number) {
|
||||
if (width === this._width && height === this._height) return
|
||||
|
||||
this._width = width
|
||||
this._height = height
|
||||
|
||||
@@ -57,6 +63,7 @@ export class ImagePass {
|
||||
}
|
||||
|
||||
setProps(props: Partial<ImageProps> = {}) {
|
||||
if (props.transparentBackground !== undefined) this._transparentBackground = props.transparentBackground
|
||||
if (props.postprocessing) this.postprocessing.setProps(props.postprocessing)
|
||||
if (props.multiSample) this.multiSample.setProps(props.multiSample)
|
||||
}
|
||||
@@ -69,10 +76,10 @@ export class ImagePass {
|
||||
this.renderer.setViewport(0, 0, this._width, this._height);
|
||||
|
||||
if (this.multiSample.enabled) {
|
||||
this.multiSample.render(false)
|
||||
this.multiSample.render(false, this._transparentBackground)
|
||||
this._colorTarget = this.multiSample.colorTarget
|
||||
} else {
|
||||
this.drawPass.render(false)
|
||||
this.drawPass.render(false, this._transparentBackground)
|
||||
if (this.postprocessing.enabled) {
|
||||
this.postprocessing.render(false)
|
||||
this._colorTarget = this.postprocessing.target
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ShaderCode } from '../../mol-gl/shader-code';
|
||||
import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { RenderTarget, createRenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
import { PostprocessingPass } from './postprocessing';
|
||||
import { DrawPass } from './draw';
|
||||
@@ -35,7 +35,7 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
|
||||
const values: Values<typeof ComposeSchema> = {
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
uWeight: ValueCell.create(1.0),
|
||||
}
|
||||
|
||||
@@ -66,9 +66,9 @@ export class MultiSamplePass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, private drawPass: DrawPass, private postprocessing: PostprocessingPass, props: Partial<MultiSampleProps>) {
|
||||
const { gl } = webgl
|
||||
this.colorTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.composeTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.colorTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.composeTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.holdTarget = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.compose = getComposeRenderable(webgl, drawPass.colorTarget.texture)
|
||||
this.props = { ...PD.getDefaultValues(MultiSampleParams), ...props }
|
||||
}
|
||||
@@ -105,15 +105,15 @@ export class MultiSamplePass {
|
||||
if (props.sampleLevel !== undefined) this.props.sampleLevel = props.sampleLevel
|
||||
}
|
||||
|
||||
render(toDrawingBuffer: boolean) {
|
||||
render(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
if (this.props.mode === 'temporal') {
|
||||
this.renderTemporalMultiSample(toDrawingBuffer)
|
||||
this.renderTemporalMultiSample(toDrawingBuffer, transparentBackground)
|
||||
} else {
|
||||
this.renderMultiSample(toDrawingBuffer)
|
||||
this.renderMultiSample(toDrawingBuffer, transparentBackground)
|
||||
}
|
||||
}
|
||||
|
||||
private renderMultiSample(toDrawingBuffer: boolean) {
|
||||
private renderMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, drawPass, postprocessing, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -131,7 +131,8 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
@@ -148,7 +149,7 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
@@ -184,7 +185,7 @@ export class MultiSamplePass {
|
||||
camera.update()
|
||||
}
|
||||
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean) {
|
||||
private renderTemporalMultiSample(toDrawingBuffer: boolean, transparentBackground: boolean) {
|
||||
const { camera, compose, composeTarget, holdTarget, postprocessing, drawPass, webgl } = this
|
||||
const { gl, state } = webgl
|
||||
|
||||
@@ -204,7 +205,7 @@ export class MultiSamplePass {
|
||||
const i = this.sampleIndex
|
||||
|
||||
if (i === 0) {
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
ValueCell.update(compose.values.uWeight, 1.0)
|
||||
ValueCell.update(compose.values.tColor, postprocessing.enabled ? postprocessing.target.texture : drawPass.colorTarget.texture)
|
||||
@@ -222,7 +223,8 @@ export class MultiSamplePass {
|
||||
ValueCell.update(compose.values.uWeight, sampleWeight)
|
||||
compose.update()
|
||||
|
||||
const { width, height } = drawPass.colorTarget
|
||||
const width = drawPass.colorTarget.getWidth()
|
||||
const height = drawPass.colorTarget.getHeight()
|
||||
|
||||
// render the scene multiple times, each slightly jitter offset
|
||||
// from the last and accumulate the results.
|
||||
@@ -233,7 +235,7 @@ export class MultiSamplePass {
|
||||
camera.update()
|
||||
|
||||
// render scene and optionally postprocess
|
||||
drawPass.render(false)
|
||||
drawPass.render(false, transparentBackground)
|
||||
if (postprocessing.enabled) postprocessing.render(false)
|
||||
|
||||
// compose rendered scene with compose target
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { WebGLContext } from '../../mol-gl/webgl/context';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import Renderer from '../../mol-gl/renderer';
|
||||
import Scene from '../../mol-gl/scene';
|
||||
import { PickingId } from '../../mol-geo/geometry/picking';
|
||||
@@ -36,9 +36,9 @@ export class PickPass {
|
||||
this.pickWidth = Math.round(width * this.pickScale)
|
||||
this.pickHeight = Math.round(height * this.pickScale)
|
||||
|
||||
this.objectPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
this.instancePickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
this.groupPickTarget = createRenderTarget(webgl, this.pickWidth, this.pickHeight)
|
||||
this.objectPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
|
||||
this.instancePickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
|
||||
this.groupPickTarget = webgl.createRenderTarget(this.pickWidth, this.pickHeight)
|
||||
|
||||
this.setupBuffers()
|
||||
}
|
||||
@@ -68,11 +68,11 @@ export class PickPass {
|
||||
const { renderer, scene, camera } = this
|
||||
renderer.setViewport(0, 0, this.pickWidth, this.pickHeight);
|
||||
this.objectPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickObject', true);
|
||||
renderer.render(scene, camera, 'pickObject', true, false);
|
||||
this.instancePickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickInstance', true);
|
||||
renderer.render(scene, camera, 'pickInstance', true, false);
|
||||
this.groupPickTarget.bind();
|
||||
renderer.render(scene, camera, 'pickGroup', true);
|
||||
renderer.render(scene, camera, 'pickGroup', true, false);
|
||||
|
||||
this.pickDirty = false
|
||||
}
|
||||
@@ -97,6 +97,8 @@ export class PickPass {
|
||||
|
||||
identify(x: number, y: number): PickingId | undefined {
|
||||
const { webgl, pickScale } = this
|
||||
if (webgl.isContextLost) return
|
||||
|
||||
const { gl } = webgl
|
||||
if (this.pickDirty) {
|
||||
this.render()
|
||||
|
||||
@@ -14,7 +14,7 @@ import { createComputeRenderItem } from '../../mol-gl/webgl/render-item';
|
||||
import { createComputeRenderable, ComputeRenderable } from '../../mol-gl/renderable';
|
||||
import { Vec2, Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition';
|
||||
import { createRenderTarget, RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { RenderTarget } from '../../mol-gl/webgl/render-target';
|
||||
import { DrawPass } from './draw';
|
||||
import { Camera } from '../../mol-canvas3d/camera';
|
||||
|
||||
@@ -27,7 +27,6 @@ const PostprocessingSchema = {
|
||||
tDepth: TextureSpec('texture', 'rgba', 'ubyte', 'nearest'),
|
||||
uTexSize: UniformSpec('v2'),
|
||||
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
dOrthographic: DefineSpec('number'),
|
||||
uNear: UniformSpec('f'),
|
||||
uFar: UniformSpec('f'),
|
||||
@@ -56,8 +55,6 @@ export const PostprocessingParams = {
|
||||
outlineEnable: PD.Boolean(false),
|
||||
outlineScale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
|
||||
outlineThreshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
|
||||
|
||||
useFog: PD.Boolean(true),
|
||||
}
|
||||
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
|
||||
|
||||
@@ -69,9 +66,8 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
|
||||
...QuadValues,
|
||||
tColor: ValueCell.create(colorTexture),
|
||||
tDepth: ValueCell.create(depthTexture),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.width, colorTexture.height)),
|
||||
uTexSize: ValueCell.create(Vec2.create(colorTexture.getWidth(), colorTexture.getHeight())),
|
||||
|
||||
dUseFog: ValueCell.create(p.useFog),
|
||||
dOrthographic: ValueCell.create(0),
|
||||
uNear: ValueCell.create(1),
|
||||
uFar: ValueCell.create(10000),
|
||||
@@ -105,7 +101,7 @@ export class PostprocessingPass {
|
||||
|
||||
constructor(private webgl: WebGLContext, private camera: Camera, drawPass: DrawPass, props: Partial<PostprocessingProps>) {
|
||||
const { gl } = webgl
|
||||
this.target = createRenderTarget(webgl, gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.target = webgl.createRenderTarget(gl.drawingBufferWidth, gl.drawingBufferHeight)
|
||||
this.props = { ...PD.getDefaultValues(PostprocessingParams), ...props }
|
||||
const { colorTarget, depthTexture, packedDepth } = drawPass
|
||||
this.renderable = getPostprocessingRenderable(webgl, colorTarget.texture, depthTexture, packedDepth, this.props)
|
||||
@@ -151,11 +147,6 @@ export class PostprocessingPass {
|
||||
ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
|
||||
}
|
||||
|
||||
if (props.useFog !== undefined) {
|
||||
this.props.useFog = props.useFog
|
||||
ValueCell.update(this.renderable.values.dUseFog, props.useFog)
|
||||
}
|
||||
|
||||
this.renderable.update()
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Column {
|
||||
export type Coordinate = { '@type': 'coord', T: number } & Base<'float'>
|
||||
|
||||
export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space, baseType: Int | Float } & Base<'tensor'>
|
||||
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<'str' | 'int'>
|
||||
export type Aliased<T> = { '@type': 'aliased', T: T } & Base<T extends string ? 'str' : 'int'>
|
||||
export type List<T extends number|string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
|
||||
|
||||
export const str: Str = { '@type': 'str', T: '', valueType: 'str' };
|
||||
@@ -92,7 +92,13 @@ namespace Column {
|
||||
return !!v && !!(v as Column<any>).schema && !!(v as Column<any>).value;
|
||||
}
|
||||
|
||||
export const enum ValueKind { Present = 0, NotPresent = 1, Unknown = 2 }
|
||||
export const enum ValueKind {
|
||||
Present = 0,
|
||||
/** Expressed in CIF as `.` */
|
||||
NotPresent = 1,
|
||||
/** Expressed in CIF as `?` */
|
||||
Unknown = 2
|
||||
}
|
||||
|
||||
export function Undefined<T extends Schema>(rowCount: number, schema: T): Column<T['T']> {
|
||||
return constColumn(schema['T'], rowCount, schema, ValueKind.NotPresent);
|
||||
@@ -131,6 +137,14 @@ namespace Column {
|
||||
return arrayColumn({ array, schema: Schema.str });
|
||||
}
|
||||
|
||||
export function ofStringAliasArray<T extends string>(array: ArrayLike<T>) {
|
||||
return arrayColumn<Schema.Aliased<T>>({ array, schema: Schema.Aliased(Schema.str) });
|
||||
}
|
||||
|
||||
export function ofStringListArray<T extends string>(array: ArrayLike<T[]>, separator = ',') {
|
||||
return arrayColumn<Schema.List<T>>({ array, schema: Schema.List<T>(separator, x => x as T) });
|
||||
}
|
||||
|
||||
export function ofIntTokens(tokens: Tokens) {
|
||||
const { count, data, indices } = tokens
|
||||
return lambdaColumn({
|
||||
@@ -290,14 +304,14 @@ function arrayColumn<T extends Column.Schema>({ array, schema, valueKind }: Colu
|
||||
return ret;
|
||||
}
|
||||
: isTyped
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
? params => ColumnHelpers.typedArrayWindow(array, params) as any as ReadonlyArray<T>
|
||||
: params => {
|
||||
const { start, end } = ColumnHelpers.getArrayBounds(rowCount, params);
|
||||
if (start === 0 && end === array.length) return array as ReadonlyArray<T['T']>;
|
||||
const ret = new (params && typeof params.array !== 'undefined' ? params.array : (array as any).constructor)(end - start) as any;
|
||||
for (let i = 0, _i = end - start; i < _i; i++) ret[i] = array[start + i];
|
||||
return ret;
|
||||
},
|
||||
areValuesEqual: (rowA, rowB) => array[rowA] === array[rowB]
|
||||
}
|
||||
}
|
||||
@@ -311,7 +325,8 @@ function windowColumn<T>(column: Column<T>, start: number, end: number): Column<
|
||||
|
||||
function windowTyped<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
const array = ColumnHelpers.typedArrayWindow(c.__array, { start, end });
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: c.valueKind }) as any;
|
||||
const vk = c.valueKind;
|
||||
return arrayColumn({ array, schema: c.schema, valueKind: row => vk(start + row) }) as any;
|
||||
}
|
||||
|
||||
function windowFull<T>(c: Column<T>, start: number, end: number): Column<T> {
|
||||
@@ -353,7 +368,8 @@ function arrayView<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {
|
||||
const array = c.__array!;
|
||||
const ret = new (array as any).constructor(map.length);
|
||||
for (let i = 0, _i = map.length; i < _i; i++) ret[i] = array[map[i]];
|
||||
return arrayColumn({ array: ret, schema: c.schema, valueKind: c.valueKind });
|
||||
const vk = c.valueKind;
|
||||
return arrayColumn({ array: ret, schema: c.schema, valueKind: row => vk(map[row]) });
|
||||
}
|
||||
|
||||
function viewFull<T>(c: Column<T>, map: ArrayLike<number>): Column<T> {
|
||||
|
||||
@@ -21,7 +21,8 @@ namespace Table {
|
||||
export type Columns<S extends Schema> = { [C in keyof S]: Column<S[C]['T']> }
|
||||
export type Row<S extends Schema> = { [C in keyof S]: S[C]['T'] }
|
||||
export type Arrays<S extends Schema> = { [C in keyof S]: ArrayLike<S[C]['T']> }
|
||||
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & { [C in keyof S]?: Column<S[C]['T']> }
|
||||
export type PartialColumns<S extends Schema> = { [C in keyof S]?: Column<S[C]['T']> }
|
||||
export type PartialTable<S extends Table.Schema> = { readonly _rowCount: number, readonly _columns: ReadonlyArray<string> } & PartialColumns<S>
|
||||
|
||||
export function is(t: any): t is Table<any> {
|
||||
return t && typeof t._rowCount === 'number' && !!t._columns && !!t._schema;
|
||||
@@ -47,6 +48,19 @@ namespace Table {
|
||||
return { _rowCount, _columns, _schema: schema, ...(columns as any) };
|
||||
}
|
||||
|
||||
export function ofPartialColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, partialColumns: PartialColumns<S>, rowCount: number): R {
|
||||
const ret = Object.create(null);
|
||||
const columns = Object.keys(schema);
|
||||
ret._rowCount = rowCount;
|
||||
ret._columns = columns;
|
||||
ret._schema = schema;
|
||||
for (const k of columns) {
|
||||
if (k in partialColumns) ret[k] = partialColumns[k]
|
||||
else ret[k] = Column.Undefined(rowCount, schema[k])
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function ofUndefinedColumns<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rowCount: number): R {
|
||||
const ret = Object.create(null);
|
||||
const columns = Object.keys(schema);
|
||||
|
||||
@@ -90,14 +90,14 @@ class LinkedListImpl<T> implements LinkedList<T> {
|
||||
if (node.previous !== null) {
|
||||
node.previous.next = node.next;
|
||||
}
|
||||
else if (/*first == item*/ node.previous === null) {
|
||||
else if (/* first == item*/ node.previous === null) {
|
||||
this.first = node.next;
|
||||
}
|
||||
|
||||
if (node.next !== null) {
|
||||
node.next.previous = node.previous;
|
||||
}
|
||||
else if (/*last == item*/ node.next === null) {
|
||||
else if (/* last == item*/ node.next === null) {
|
||||
this.last = node.previous;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import OrderedSet from '../ordered-set'
|
||||
import Interval from '../interval'
|
||||
import SortedArray from '../sorted-array';
|
||||
|
||||
describe('ordered set', () => {
|
||||
function ordSetToArray(set: OrderedSet) {
|
||||
@@ -163,6 +164,15 @@ describe('ordered set', () => {
|
||||
testEq('intersect AA', OrderedSet.intersect(arr136, OrderedSet.ofSortedArray([2, 3, 4, 6, 7])), [3, 6]);
|
||||
it('intersect AA1', () => expect(OrderedSet.union(arr136, OrderedSet.ofSortedArray([1, 3, 6]))).toBe(arr136));
|
||||
|
||||
testEq('idxIntersect 1', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([1, 2, 4]),
|
||||
SortedArray.ofSortedArray([1, 2, 3, 4, 5, 6]),
|
||||
SortedArray.ofSortedArray([2, 4, 5, 8])), [0, 2]);
|
||||
testEq('idxIntersect 2', OrderedSet.indexedIntersect(
|
||||
OrderedSet.ofSortedArray([0, 1]),
|
||||
SortedArray.ofSortedArray([1, 2]),
|
||||
SortedArray.ofSortedArray([1, 2])), [0, 1]);
|
||||
|
||||
testEq('subtract ES', OrderedSet.subtract(empty, singleton10), []);
|
||||
testEq('subtract ER', OrderedSet.subtract(empty, range1_4), []);
|
||||
testEq('subtract EA', OrderedSet.subtract(empty, arr136), []);
|
||||
|
||||
@@ -63,6 +63,16 @@ describe('sortedArray', () => {
|
||||
compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
|
||||
})
|
||||
|
||||
it('indicesOf 2', () => {
|
||||
compareArrays(
|
||||
SortedArray.indicesOf(
|
||||
SortedArray.ofSortedArray([0, 1, 2, 3, 4, 8, 9, 10]),
|
||||
SortedArray.ofSortedArray([1, 3, 4, 9, 10])
|
||||
),
|
||||
[1, 3, 4, 6, 7]
|
||||
);
|
||||
})
|
||||
|
||||
test('intersectionSize', SortedArray.intersectionSize(a1234, a2468), 2);
|
||||
|
||||
it('union1', () => {
|
||||
|
||||
@@ -286,4 +286,69 @@ export function forEach(set: OrderedSetImpl, f: (value: number, i: number, ctx:
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function forEachSegment(set: OrderedSetImpl, segment: (v: number) => number, f: (value: number, segIndex: number, ctx: any) => void, ctx: any) {
|
||||
if (I.is(set)) {
|
||||
let sI = 0;
|
||||
for (let i = I.min(set), _i = I.max(set); i <= _i; i++) {
|
||||
const s = segment(i);
|
||||
let endI = i + 1;
|
||||
while (endI < _i && segment(endI) === s) endI++;
|
||||
i = endI - 1;
|
||||
f(s, sI, ctx);
|
||||
sI++;
|
||||
}
|
||||
} else {
|
||||
let sI = 0;
|
||||
for (let i = 0, _i = set.length; i < _i; i++) {
|
||||
const s = segment(set[i]);
|
||||
let endI = i + 1;
|
||||
while (endI < _i && segment(set[endI]) === s) endI++;
|
||||
i = endI - 1;
|
||||
f(s, sI, ctx);
|
||||
sI++;
|
||||
}
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export function indexedIntersect(idxA: OrderedSetImpl, a: S, b: S): OrderedSetImpl {
|
||||
if (a === b) return idxA;
|
||||
const lenI = size(idxA), lenA = a.length, lenB = b.length;
|
||||
if (lenI === 0 || lenA === 0 || lenB === 0) return Empty;
|
||||
|
||||
const startJ = S.findPredecessorIndex(b, a[min(idxA)]);
|
||||
const endJ = S.findPredecessorIndex(b, a[max(idxA)] + 1);
|
||||
|
||||
let commonCount = 0;
|
||||
|
||||
let offset = 0;
|
||||
let O = 0;
|
||||
let j = startJ;
|
||||
while (O < lenI && j < endJ) {
|
||||
const x = a[getAt(idxA, O)], y = b[j];
|
||||
if (x < y) { O++; }
|
||||
else if (x > y) { j++; }
|
||||
else { commonCount++; O++; j++; }
|
||||
}
|
||||
|
||||
// no common elements
|
||||
if (commonCount === 0) return Empty;
|
||||
// A === B
|
||||
if (commonCount === lenA && commonCount === lenB) return idxA;
|
||||
|
||||
const indices = new Int32Array(commonCount);
|
||||
|
||||
offset = 0;
|
||||
O = 0;
|
||||
j = startJ;
|
||||
while (O < lenI && j < endJ) {
|
||||
const x = a[getAt(idxA, O)], y = b[j];
|
||||
if (x < y) { O++; }
|
||||
else if (x > y) { j++; }
|
||||
else { indices[offset++] = j; O++; j++; }
|
||||
}
|
||||
|
||||
return ofSortedArray(indices);
|
||||
}
|
||||
@@ -39,6 +39,7 @@ namespace OrderedSet {
|
||||
|
||||
export const union: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.union as any;
|
||||
export const intersect: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.intersect as any;
|
||||
export const indexedIntersect: <T extends number = number, S extends number = number>(idxA: OrderedSet<T>, a: SortedArray<S>, b: SortedArray<S>) => OrderedSet<T> = Base.indexedIntersect as any;
|
||||
/** Returns elements of `a` that are not in `b`, i.e `a` - `b` */
|
||||
export const subtract: <T extends number = number>(a: OrderedSet<T>, b: OrderedSet<T>) => OrderedSet<T> = Base.subtract as any;
|
||||
|
||||
@@ -56,6 +57,10 @@ namespace OrderedSet {
|
||||
return Base.forEach(set as any, f as any, ctx);
|
||||
}
|
||||
|
||||
export function forEachSegment<T extends number, S extends number, Ctx>(set: OrderedSet<T>, segment: (v: T) => S, f: (v: S, sI: number, ctx: Ctx) => void, ctx?: Ctx): Ctx {
|
||||
return Base.forEachSegment(set as any, segment as any, f as any, ctx);
|
||||
}
|
||||
|
||||
export function isInterval<T extends number = number>(set: OrderedSet<T>): set is Interval<T> {
|
||||
return Interval.is(set);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
import { Interval, OrderedSet, SortedArray } from '../../int';
|
||||
import { IntervalIterator } from '../interval-iterator';
|
||||
|
||||
describe('interval', () => {
|
||||
describe('interval', () => {
|
||||
function testIterator(name: string, interval: Interval, set: OrderedSet, expectedValues: { index: number[], start: number[], end: number[]}) {
|
||||
it(`iterator, ${name}`, () => {
|
||||
const intervalIt = new IntervalIterator(interval, set)
|
||||
const { index, start, end } = expectedValues
|
||||
|
||||
|
||||
let i = 0
|
||||
while (intervalIt.hasNext) {
|
||||
const segment = intervalIt.move()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -27,7 +27,7 @@ function nextIndex(n: number) {
|
||||
return ripple | ones
|
||||
};
|
||||
|
||||
export class CombinationIterator<T> implements Iterator<T[]> {
|
||||
export class CombinationIterator<T> implements Iterator<ReadonlyArray<T>> {
|
||||
private value: T[]
|
||||
private index: number
|
||||
private maxIndex: number
|
||||
|
||||
@@ -9,8 +9,6 @@ import { ValueCell } from '../../mol-util';
|
||||
import { BaseValues } from '../../mol-gl/renderable/schema';
|
||||
import { LocationIterator } from '../util/location-iterator';
|
||||
import { ParamDefinition as PD } from '../../mol-util/param-definition'
|
||||
import { Color } from '../../mol-util/color';
|
||||
import { Vec3 } from '../../mol-math/linear-algebra';
|
||||
import { TransformData, createIdentityTransform } from './transform-data';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { ColorNames } from '../../mol-util/color/names';
|
||||
@@ -38,10 +36,6 @@ export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [Visua
|
||||
export namespace BaseGeometry {
|
||||
export const Params = {
|
||||
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
|
||||
useFog: PD.Boolean(true),
|
||||
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
|
||||
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
|
||||
|
||||
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
@@ -62,24 +56,13 @@ export namespace BaseGeometry {
|
||||
return {
|
||||
alpha: ValueCell.create(props.alpha),
|
||||
uAlpha: ValueCell.create(props.alpha),
|
||||
uHighlightColor: ValueCell.create(Color.toArrayNormalized(props.highlightColor, Vec3.zero(), 0)),
|
||||
uSelectColor: ValueCell.create(Color.toArrayNormalized(props.selectColor, Vec3.zero(), 0)),
|
||||
dUseFog: ValueCell.create(props.useFog),
|
||||
|
||||
uGroupCount: ValueCell.create(counts.groupCount),
|
||||
drawCount: ValueCell.create(counts.drawCount),
|
||||
}
|
||||
}
|
||||
|
||||
export function updateValues(values: BaseValues, props: PD.Values<Params>) {
|
||||
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
|
||||
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
|
||||
}
|
||||
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
|
||||
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
|
||||
}
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
}
|
||||
|
||||
export function createRenderableState(props: Partial<PD.Values<Params>> = {}): RenderableState {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { transformPositionArray } from '../../../mol-geo/util';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { RenderableState } from '../../../mol-gl/renderable';
|
||||
import { ColorListOptions, ColorListName } from '../../../mol-util/color/lists';
|
||||
@@ -26,12 +26,14 @@ import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
const VolumeBox = Box()
|
||||
const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][]
|
||||
|
||||
export interface DirectVolume {
|
||||
readonly kind: 'direct-volume',
|
||||
|
||||
readonly gridTexture: ValueCell<Texture>,
|
||||
readonly gridTextureDim: ValueCell<Vec3>,
|
||||
readonly gridDimension: ValueCell<Vec3>,
|
||||
@@ -41,32 +43,66 @@ export interface DirectVolume {
|
||||
readonly transform: ValueCell<Mat4>
|
||||
|
||||
/** Bounding sphere of the volume */
|
||||
boundingSphere?: Sphere3D
|
||||
boundingSphere: Sphere3D
|
||||
}
|
||||
|
||||
export namespace DirectVolume {
|
||||
export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume): DirectVolume {
|
||||
const { width, height, depth } = texture
|
||||
if (directVolume) {
|
||||
ValueCell.update(directVolume.gridDimension, gridDimension)
|
||||
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
|
||||
ValueCell.update(directVolume.bboxMin, bbox.min)
|
||||
ValueCell.update(directVolume.bboxMax, bbox.max)
|
||||
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
|
||||
ValueCell.update(directVolume.transform, transform)
|
||||
return directVolume
|
||||
} else {
|
||||
return {
|
||||
kind: 'direct-volume',
|
||||
gridDimension: ValueCell.create(gridDimension),
|
||||
gridTexture: ValueCell.create(texture),
|
||||
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
||||
bboxMin: ValueCell.create(bbox.min),
|
||||
bboxMax: ValueCell.create(bbox.max),
|
||||
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
||||
transform: ValueCell.create(transform),
|
||||
}
|
||||
return directVolume ?
|
||||
update(bbox, gridDimension, transform, texture, directVolume) :
|
||||
fromData(bbox, gridDimension, transform, texture)
|
||||
}
|
||||
|
||||
function hashCode(directVolume: DirectVolume) {
|
||||
return hashFnv32a([
|
||||
directVolume.bboxSize.ref.version, directVolume.gridDimension.ref.version,
|
||||
directVolume.gridTexture.ref.version, directVolume.transform.ref.version,
|
||||
])
|
||||
}
|
||||
|
||||
function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture): DirectVolume {
|
||||
const boundingSphere = Sphere3D()
|
||||
let currentHash = -1
|
||||
|
||||
const width = texture.getWidth()
|
||||
const height = texture.getHeight()
|
||||
const depth = texture.getDepth()
|
||||
|
||||
const directVolume = {
|
||||
kind: 'direct-volume' as const,
|
||||
gridDimension: ValueCell.create(gridDimension),
|
||||
gridTexture: ValueCell.create(texture),
|
||||
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
||||
bboxMin: ValueCell.create(bbox.min),
|
||||
bboxMax: ValueCell.create(bbox.max),
|
||||
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
||||
transform: ValueCell.create(transform),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(directVolume)
|
||||
if (newHash !== currentHash) {
|
||||
const b = getBoundingSphere(directVolume.gridDimension.ref.value, directVolume.transform.ref.value)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
}
|
||||
return directVolume
|
||||
}
|
||||
|
||||
function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume: DirectVolume): DirectVolume {
|
||||
const width = texture.getWidth()
|
||||
const height = texture.getHeight()
|
||||
const depth = texture.getDepth()
|
||||
|
||||
ValueCell.update(directVolume.gridDimension, gridDimension)
|
||||
ValueCell.update(directVolume.gridTexture, texture)
|
||||
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth))
|
||||
ValueCell.update(directVolume.bboxMin, bbox.min)
|
||||
ValueCell.update(directVolume.bboxMax, bbox.max)
|
||||
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min))
|
||||
ValueCell.update(directVolume.transform, transform)
|
||||
return directVolume
|
||||
}
|
||||
|
||||
export function createEmpty(directVolume?: DirectVolume): DirectVolume {
|
||||
@@ -108,7 +144,8 @@ export namespace DirectVolume {
|
||||
|
||||
const counts = { drawCount: VolumeBox.indices.length, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(gridDimension.ref.value, gridTransform.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
|
||||
const transferTex = createTransferFunctionTexture(controlPoints, props.list)
|
||||
@@ -138,7 +175,7 @@ export namespace DirectVolume {
|
||||
dRenderMode: ValueCell.create(props.renderMode),
|
||||
tTransferTex: transferTex,
|
||||
|
||||
dGridTexType: ValueCell.create(gridTexture.ref.value.depth > 0 ? '3d' : '2d'),
|
||||
dGridTexType: ValueCell.create(gridTexture.ref.value.getDepth() > 0 ? '3d' : '2d'),
|
||||
uGridTexDim: gridTextureDim,
|
||||
tGridTex: gridTexture,
|
||||
}
|
||||
@@ -153,7 +190,6 @@ export namespace DirectVolume {
|
||||
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
|
||||
ValueCell.updateIfChanged(values.uIsoValue, props.isoValueNorm)
|
||||
ValueCell.updateIfChanged(values.uAlpha, props.alpha)
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
ValueCell.updateIfChanged(values.dRenderMode, props.renderMode)
|
||||
|
||||
const controlPoints = getControlPointsFromVec2Array(props.controlPoints)
|
||||
@@ -161,7 +197,9 @@ export namespace DirectVolume {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(values.uGridDim.ref.value, values.uTransform.ref.value, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -188,11 +226,12 @@ const mTmp = Mat4.identity()
|
||||
const mTmp2 = Mat4.identity()
|
||||
const vHalfUnit = Vec3.create(0.5, 0.5, 0.5)
|
||||
const tmpVertices = new Float32Array(VolumeBox.vertices.length)
|
||||
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4, transform: Float32Array, transformCount: number) {
|
||||
function getBoundingSphere(gridDimension: Vec3, gridTransform: Mat4) {
|
||||
tmpVertices.set(VolumeBox.vertices)
|
||||
Mat4.fromTranslation(mTmp, vHalfUnit)
|
||||
Mat4.mul(mTmp, Mat4.fromScaling(mTmp2, gridDimension), mTmp)
|
||||
Mat4.mul(mTmp, gridTransform, mTmp)
|
||||
transformPositionArray(mTmp, tmpVertices, 0, tmpVertices.length / 3)
|
||||
return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
|
||||
return calculateInvariantBoundingSphere(tmpVertices, tmpVertices.length / 3, 1)
|
||||
// return calculateBoundingSphere(tmpVertices, tmpVertices.length / 3, transform, transformCount)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -19,32 +19,30 @@ import { Spheres } from './spheres/spheres';
|
||||
import { arrayMax } from '../../mol-util/array';
|
||||
import { TransformData } from './transform-data';
|
||||
import { Theme } from '../../mol-theme/theme';
|
||||
import { RenderObjectValuesType } from '../../mol-gl/render-object';
|
||||
import { ValueOf } from '../../mol-util/type-helpers';
|
||||
import { RenderObjectValues } from '../../mol-gl/render-object';
|
||||
import { TextureMesh } from './texture-mesh/texture-mesh';
|
||||
|
||||
export type GeometryKindType = {
|
||||
'mesh': Mesh,
|
||||
'points': Points,
|
||||
'spheres': Spheres,
|
||||
'text': Text,
|
||||
'lines': Lines,
|
||||
'direct-volume': DirectVolume,
|
||||
'texture-mesh': TextureMesh,
|
||||
}
|
||||
export type GeometryKindParams = {
|
||||
'mesh': Mesh.Params,
|
||||
'points': Points.Params,
|
||||
'spheres': Spheres.Params,
|
||||
'text': Text.Params,
|
||||
'lines': Lines.Params,
|
||||
'direct-volume': DirectVolume.Params,
|
||||
'texture-mesh': TextureMesh.Params,
|
||||
}
|
||||
export type GeometryKind = keyof GeometryKindType
|
||||
export type Geometry = ValueOf<GeometryKindType>
|
||||
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
|
||||
|
||||
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryKindParams[G['kind']], V = RenderObjectValuesType[G['kind']]> {
|
||||
export type Geometry<T extends GeometryKind = GeometryKind> =
|
||||
T extends 'mesh' ? Mesh :
|
||||
T extends 'points' ? Points :
|
||||
T extends 'spheres' ? Spheres :
|
||||
T extends 'text' ? Text :
|
||||
T extends 'lines' ? Lines :
|
||||
T extends 'direct-volume' ? DirectVolume :
|
||||
T extends 'texture-mesh' ? TextureMesh : never
|
||||
|
||||
type GeometryParams<T extends GeometryKind> =
|
||||
T extends 'mesh' ? Mesh.Params :
|
||||
T extends 'points' ? Points.Params :
|
||||
T extends 'spheres' ? Spheres.Params :
|
||||
T extends 'text' ? Text.Params :
|
||||
T extends 'lines' ? Lines.Params :
|
||||
T extends 'direct-volume' ? DirectVolume.Params :
|
||||
T extends 'texture-mesh' ? TextureMesh.Params : never
|
||||
|
||||
export interface GeometryUtils<G extends Geometry, P extends PD.Params = GeometryParams<G['kind']>, V = RenderObjectValues<G['kind']>> {
|
||||
Params: P
|
||||
createEmpty(geometry?: G): G
|
||||
createValues(geometry: G, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<P>): V
|
||||
@@ -56,7 +54,7 @@ export interface GeometryUtils<G extends Geometry, P extends PD.Params = Geometr
|
||||
}
|
||||
|
||||
export namespace Geometry {
|
||||
export type Params<G extends Geometry> = GeometryKindParams[G['kind']]
|
||||
export type Params<G extends Geometry> = GeometryParams<G['kind']>
|
||||
|
||||
export function getDrawCount(geometry: Geometry): number {
|
||||
switch (geometry.kind) {
|
||||
@@ -66,7 +64,7 @@ export namespace Geometry {
|
||||
case 'text': return geometry.charCount * 2 * 3
|
||||
case 'lines': return geometry.lineCount * 2 * 3
|
||||
case 'direct-volume': return 12 * 3
|
||||
case 'texture-mesh': return geometry.vertexCount.ref.value
|
||||
case 'texture-mesh': return geometry.vertexCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ export namespace Geometry {
|
||||
case 'direct-volume':
|
||||
return 1
|
||||
case 'texture-mesh':
|
||||
return geometry.groupCount.ref.value
|
||||
return geometry.groupCount
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +94,6 @@ export namespace Geometry {
|
||||
case 'direct-volume': return DirectVolume.Utils as any
|
||||
case 'texture-mesh': return TextureMesh.Utils as any
|
||||
}
|
||||
throw new Error('unknown geometry kind')
|
||||
}
|
||||
|
||||
export function getGranularity(locationIt: LocationIterator, granularity: ColorType | SizeType) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Lines } from './lines';
|
||||
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
|
||||
@@ -12,12 +11,15 @@ import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
|
||||
export interface LinesBuilder {
|
||||
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, group: number): void
|
||||
addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, group: number): void
|
||||
addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, group: number): void
|
||||
addCage(t: Mat4, cage: Cage, group: number): void
|
||||
getLines(): Lines
|
||||
}
|
||||
|
||||
const tmpVecA = Vec3.zero()
|
||||
const tmpVecB = Vec3.zero()
|
||||
const tmpVecA = Vec3()
|
||||
const tmpVecB = Vec3()
|
||||
const tmpDir = Vec3()
|
||||
|
||||
export namespace LinesBuilder {
|
||||
export function create(initialCount = 2048, chunkSize = 1024, lines?: Lines): LinesBuilder {
|
||||
@@ -42,8 +44,29 @@ export namespace LinesBuilder {
|
||||
ChunkedArray.add3(indices, offset + 1, offset + 3, offset + 2);
|
||||
}
|
||||
|
||||
const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, group: number) => {
|
||||
const d = Vec3.distance(start, end)
|
||||
const s = Math.floor(segmentCount / 2)
|
||||
const step = 1 / segmentCount
|
||||
|
||||
Vec3.sub(tmpDir, end, start)
|
||||
for (let j = 0; j < s; ++j) {
|
||||
const f = step * (j * 2 + 1)
|
||||
Vec3.setMagnitude(tmpDir, tmpDir, d * f)
|
||||
Vec3.add(tmpVecA, start, tmpDir)
|
||||
Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2))
|
||||
Vec3.add(tmpVecB, start, tmpDir)
|
||||
add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], group)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
add,
|
||||
addFixedCountDashes,
|
||||
addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, group: number) => {
|
||||
const d = Vec3.distance(start, end)
|
||||
addFixedCountDashes(start, end, d / segmentLength, group)
|
||||
},
|
||||
addCage: (t: Mat4, cage: Cage, group: number) => {
|
||||
const { vertices, edges } = cage
|
||||
for (let i = 0, il = edges.length; i < il; i += 2) {
|
||||
@@ -60,15 +83,7 @@ export namespace LinesBuilder {
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
const sb = ChunkedArray.compact(starts, true) as Float32Array
|
||||
const eb = ChunkedArray.compact(ends, true) as Float32Array
|
||||
return {
|
||||
kind: 'lines',
|
||||
lineCount: indices.elementCount / 2,
|
||||
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
|
||||
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
|
||||
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
|
||||
}
|
||||
return Lines.create(mb, ib, gb, sb, eb, indices.elementCount / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
|
||||
GroupMapping,
|
||||
createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -17,19 +19,22 @@ import { LinesValues } from '../../../mol-gl/renderable/lines';
|
||||
import { Mesh } from '../mesh/mesh';
|
||||
import { LinesBuilder } from './lines-builder';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
/** Wide line */
|
||||
export interface Lines {
|
||||
readonly kind: 'lines',
|
||||
|
||||
/** Number of lines */
|
||||
lineCount: number,
|
||||
|
||||
/** Mapping buffer as array of xy values wrapped in a value cell */
|
||||
readonly mappingBuffer: ValueCell<Float32Array>,
|
||||
/** Index buffer as array of vertex index triplets wrapped in a value cell */
|
||||
@@ -40,24 +45,27 @@ export interface Lines {
|
||||
readonly startBuffer: ValueCell<Float32Array>,
|
||||
/** Line end buffer as array of xyz values wrapped in a value cell */
|
||||
readonly endBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the lines */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to line indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Lines {
|
||||
export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines?: Lines): Lines {
|
||||
return lines ?
|
||||
update(mappings, indices, groups, starts, ends, lineCount, lines) :
|
||||
fromArrays(mappings, indices, groups, starts, ends, lineCount)
|
||||
}
|
||||
|
||||
export function createEmpty(lines?: Lines): Lines {
|
||||
const mb = lines ? lines.mappingBuffer.ref.value : new Float32Array(0)
|
||||
const ib = lines ? lines.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = lines ? lines.groupBuffer.ref.value : new Float32Array(0)
|
||||
const sb = lines ? lines.startBuffer.ref.value : new Float32Array(0)
|
||||
const eb = lines ? lines.endBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'lines',
|
||||
lineCount: 0,
|
||||
mappingBuffer: lines ? ValueCell.update(lines.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: lines ? ValueCell.update(lines.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: lines ? ValueCell.update(lines.groupBuffer, gb) : ValueCell.create(gb),
|
||||
startBuffer: lines ? ValueCell.update(lines.startBuffer, sb) : ValueCell.create(sb),
|
||||
endBuffer: lines ? ValueCell.update(lines.endBuffer, eb) : ValueCell.create(eb),
|
||||
}
|
||||
return create(mb, ib, gb, sb, eb, 0, lines)
|
||||
}
|
||||
|
||||
export function fromMesh(mesh: Mesh, lines?: Lines) {
|
||||
@@ -81,16 +89,67 @@ export namespace Lines {
|
||||
return builder.getLines();
|
||||
}
|
||||
|
||||
export function transformImmediate(line: Lines, t: Mat4) {
|
||||
transformRangeImmediate(line, t, 0, line.lineCount)
|
||||
function hashCode(lines: Lines) {
|
||||
return hashFnv32a([
|
||||
lines.lineCount, lines.mappingBuffer.ref.version, lines.indexBuffer.ref.version,
|
||||
lines.groupBuffer.ref.version, lines.startBuffer.ref.version, lines.startBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(lines: Lines, t: Mat4, offset: number, count: number) {
|
||||
function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number): Lines {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const lines = {
|
||||
kind: 'lines' as const,
|
||||
lineCount,
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
startBuffer: ValueCell.create(starts),
|
||||
endBuffer: ValueCell.create(ends),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(lines)
|
||||
if (newHash !== currentHash) {
|
||||
const s = calculateInvariantBoundingSphere(lines.startBuffer.ref.value, lines.lineCount * 4, 4)
|
||||
const e = calculateInvariantBoundingSphere(lines.endBuffer.ref.value, lines.lineCount * 4, 4)
|
||||
|
||||
Sphere3D.expandBySphere(boundingSphere, s, e)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (lines.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(lines.groupBuffer.ref.value, lines.lineCount, 4)
|
||||
currentGroup = lines.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, lineCount: number, lines: Lines) {
|
||||
lines.lineCount = lineCount
|
||||
ValueCell.update(lines.mappingBuffer, mappings)
|
||||
ValueCell.update(lines.indexBuffer, indices)
|
||||
ValueCell.update(lines.groupBuffer, groups)
|
||||
ValueCell.update(lines.startBuffer, starts)
|
||||
ValueCell.update(lines.endBuffer, ends)
|
||||
return lines
|
||||
}
|
||||
|
||||
export function transform(lines: Lines, t: Mat4) {
|
||||
const start = lines.startBuffer.ref.value
|
||||
transformPositionArray(t, start, offset, count * 4)
|
||||
transformPositionArray(t, start, 0, lines.lineCount * 4)
|
||||
ValueCell.update(lines.startBuffer, start);
|
||||
const end = lines.endBuffer.ref.value
|
||||
transformPositionArray(t, end, offset, count * 4)
|
||||
transformPositionArray(t, end, 0, lines.lineCount * 4)
|
||||
ValueCell.update(lines.endBuffer, end);
|
||||
}
|
||||
|
||||
@@ -124,8 +183,8 @@ export namespace Lines {
|
||||
|
||||
const counts = { drawCount: lines.lineCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(lines.startBuffer.ref.value, lines.endBuffer.ref.value, lines.lineCount,
|
||||
transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aMapping: lines.mappingBuffer,
|
||||
@@ -163,10 +222,9 @@ export namespace Lines {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: LinesValues, lines: Lines) {
|
||||
const { boundingSphere, invariantBoundingSphere } = getBoundingSphere(
|
||||
values.aStart.ref.value, values.aEnd.ref.value, lines.lineCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(lines.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -174,13 +232,4 @@ export namespace Lines {
|
||||
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getBoundingSphere(lineStart: Float32Array, lineEnd: Float32Array, lineCount: number, transform: Float32Array, transformCount: number) {
|
||||
const start = calculateBoundingSphere(lineStart, lineCount * 4, transform, transformCount, 0, 4)
|
||||
const end = calculateBoundingSphere(lineEnd, lineCount * 4, transform, transformCount, 0, 4)
|
||||
return {
|
||||
boundingSphere: Sphere3D.expandBySphere(start.boundingSphere, end.boundingSphere),
|
||||
invariantBoundingSphere: Sphere3D.expandBySphere(start.invariantBoundingSphere, end.invariantBoundingSphere)
|
||||
}
|
||||
}
|
||||
34
src/mol-geo/geometry/mesh/builder/axes.ts
Normal file
34
src/mol-geo/geometry/mesh/builder/axes.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { Axes3D } from '../../../../mol-math/geometry';
|
||||
import { createCage } from '../../../primitive/cage';
|
||||
|
||||
const tmpVec = Vec3()
|
||||
const tmpMatrix = Mat4.identity()
|
||||
|
||||
const tmpVertices = new Float32Array(6 * 3)
|
||||
const tmpEdges = new Uint8Array([0, 1, 2, 3, 4, 5])
|
||||
|
||||
export function addAxes(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
|
||||
const { origin, dirA, dirB, dirC } = axes
|
||||
|
||||
Vec3.add(tmpVec, origin, dirA)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirA), tmpVertices, 0)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirA), tmpVertices, 3)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirB), tmpVertices, 6)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirB), tmpVertices, 9)
|
||||
Vec3.toArray(Vec3.add(tmpVec, origin, dirC), tmpVertices, 12)
|
||||
Vec3.toArray(Vec3.sub(tmpVec, origin, dirC), tmpVertices, 15)
|
||||
|
||||
const cage = createCage(tmpVertices, tmpEdges)
|
||||
const volume = Axes3D.volume(axes)
|
||||
const radius = (Math.cbrt(volume) / 300) * radiusScale
|
||||
|
||||
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3 } from '../../../../mol-math/linear-algebra';
|
||||
import { Box3D } from '../../../../mol-math/geometry';
|
||||
import { Vec3, Mat4 } from '../../../../mol-math/linear-algebra';
|
||||
import { Box3D, Axes3D } from '../../../../mol-math/geometry';
|
||||
import { MeshBuilder } from '../mesh-builder';
|
||||
import { CylinderProps } from '../../../primitive/cylinder';
|
||||
import { addCylinder } from './cylinder';
|
||||
import { addSphere } from './sphere';
|
||||
import { createCage } from '../../../primitive/cage';
|
||||
|
||||
const tmpStart = Vec3.zero()
|
||||
const tmpEnd = Vec3.zero()
|
||||
@@ -62,4 +63,49 @@ export function addBoundingBox(state: MeshBuilder.State, box: Box3D, radius: num
|
||||
Vec3.set(tmpEnd, min[0], max[1], max[2])
|
||||
addSphere(state, tmpEnd, radius, detail)
|
||||
addCylinder(state, tmpStart, tmpEnd, 1, cylinderProps)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const tmpBoxVecCorner = Vec3()
|
||||
const tmpBoxVecA = Vec3()
|
||||
const tmpBoxVecB = Vec3()
|
||||
const tmpBoxVecC = Vec3()
|
||||
const tmpMatrix = Mat4.identity()
|
||||
|
||||
const tmpVertices = new Float32Array(8 * 3)
|
||||
const tmpEdges = new Uint8Array([
|
||||
0, 1, 0, 3, 0, 6, 1, 2, 1, 7, 2, 3,
|
||||
2, 4, 3, 5, 4, 5, 4, 7, 5, 6, 6, 7
|
||||
])
|
||||
|
||||
export function addOrientedBox(state: MeshBuilder.State, axes: Axes3D, radiusScale: number, detail: number, radialSegments: number) {
|
||||
const { origin, dirA, dirB, dirC } = axes
|
||||
const negDirA = Vec3.negate(tmpBoxVecA, dirA)
|
||||
const negDirB = Vec3.negate(tmpBoxVecB, dirB)
|
||||
const negDirC = Vec3.negate(tmpBoxVecC, dirC)
|
||||
|
||||
let offset = 0
|
||||
const addCornerHelper = function (v1: Vec3, v2: Vec3, v3: Vec3) {
|
||||
Vec3.copy(tmpBoxVecCorner, origin)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v1)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v2)
|
||||
Vec3.add(tmpBoxVecCorner, tmpBoxVecCorner, v3)
|
||||
Vec3.toArray(tmpBoxVecCorner, tmpVertices, offset)
|
||||
offset += 3
|
||||
}
|
||||
addCornerHelper(dirA, dirB, dirC)
|
||||
addCornerHelper(dirA, dirB, negDirC)
|
||||
addCornerHelper(dirA, negDirB, negDirC)
|
||||
addCornerHelper(dirA, negDirB, dirC)
|
||||
addCornerHelper(negDirA, negDirB, negDirC)
|
||||
addCornerHelper(negDirA, negDirB, dirC)
|
||||
addCornerHelper(negDirA, dirB, dirC)
|
||||
addCornerHelper(negDirA, dirB, negDirC)
|
||||
|
||||
const cage = createCage(tmpVertices, tmpEdges)
|
||||
const volume = Axes3D.volume(axes)
|
||||
const radius = (Math.cbrt(volume) / 300) * radiusScale
|
||||
|
||||
MeshBuilder.addCage(state, tmpMatrix, cage, radius, detail, radialSegments)
|
||||
}
|
||||
90
src/mol-geo/geometry/mesh/laplacian-smoothing.ts
Normal file
90
src/mol-geo/geometry/mesh/laplacian-smoothing.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
// TODO
|
||||
|
||||
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
|
||||
// dst[3 * j] += src[3 * i];
|
||||
// dst[3 * j + 1] += src[3 * i + 1];
|
||||
// dst[3 * j + 2] += src[3 * i + 2];
|
||||
// }
|
||||
|
||||
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
|
||||
// const triCount = surface.triangleIndices.length,
|
||||
// src = surface.vertices;
|
||||
|
||||
// const triangleIndices = surface.triangleIndices;
|
||||
|
||||
// for (let i = 0; i < triCount; i += 3) {
|
||||
// const a = triangleIndices[i],
|
||||
// b = triangleIndices[i + 1],
|
||||
// c = triangleIndices[i + 2];
|
||||
|
||||
// addVertex(src, b, vs, a);
|
||||
// addVertex(src, c, vs, a);
|
||||
|
||||
// addVertex(src, a, vs, b);
|
||||
// addVertex(src, c, vs, b);
|
||||
|
||||
// addVertex(src, a, vs, c);
|
||||
// addVertex(src, b, vs, c);
|
||||
// }
|
||||
|
||||
// const vw = 2 * vertexWeight;
|
||||
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
|
||||
// const n = vertexCounts[i] + vw;
|
||||
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
|
||||
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
|
||||
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
|
||||
// const vertexCounts = new Int32Array(surface.vertexCount),
|
||||
// triCount = surface.triangleIndices.length;
|
||||
|
||||
// const tris = surface.triangleIndices;
|
||||
// for (let i = 0; i < triCount; i++) {
|
||||
// // in a triangle 2 edges touch each vertex, hence the constant.
|
||||
// vertexCounts[tris[i]] += 2;
|
||||
// }
|
||||
|
||||
// let vs = new Float32Array(surface.vertices.length);
|
||||
// let started = Utils.PerformanceMonitor.currentTime();
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
// for (let i = 0; i < iterCount; i++) {
|
||||
// if (i > 0) {
|
||||
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
|
||||
// }
|
||||
// surface.normals = void 0;
|
||||
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
|
||||
// const t = surface.vertices;
|
||||
// surface.vertices = <any>vs;
|
||||
// vs = <any>t;
|
||||
|
||||
// const time = Utils.PerformanceMonitor.currentTime();
|
||||
// if (time - started > Computation.UpdateProgressDelta) {
|
||||
// started = time;
|
||||
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
|
||||
// }
|
||||
// }
|
||||
// return surface;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// * Smooths the vertices by averaging the neighborhood.
|
||||
// *
|
||||
// * Resets normals. Might replace vertex array.
|
||||
// */
|
||||
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
|
||||
|
||||
// if (iterCount < 1) iterCount = 0;
|
||||
// if (iterCount === 0) return Computation.resolve(surface);
|
||||
|
||||
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
|
||||
// }
|
||||
@@ -1,14 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Mesh } from './mesh';
|
||||
import { getNormalMatrix } from '../../util';
|
||||
import { Primitive } from '../../primitive/primitive';
|
||||
import { Cage } from '../../../mol-geo/primitive/cage';
|
||||
import { addSphere } from './builder/sphere';
|
||||
@@ -85,7 +83,7 @@ export namespace MeshBuilder {
|
||||
const { vertices: va, normals: na, indices: ia } = primitive
|
||||
const { vertices, normals, indices, groups, currentGroup } = state
|
||||
const offset = vertices.elementCount
|
||||
const n = getNormalMatrix(tmpMat3, t)
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = va.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
|
||||
@@ -101,6 +99,27 @@ export namespace MeshBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/** Flips triangle normals and winding order */
|
||||
export function addPrimitiveFlipped(state: State, t: Mat4, primitive: Primitive) {
|
||||
const { vertices: va, normals: na, indices: ia } = primitive
|
||||
const { vertices, normals, indices, groups, currentGroup } = state
|
||||
const offset = vertices.elementCount
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = va.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, va, i), t)
|
||||
ChunkedArray.add3(vertices, tmpV[0], tmpV[1], tmpV[2]);
|
||||
// normal
|
||||
Vec3.transformMat3(tmpV, Vec3.fromArray(tmpV, na, i), n)
|
||||
ChunkedArray.add3(normals, -tmpV[0], -tmpV[1], -tmpV[2]);
|
||||
// group
|
||||
ChunkedArray.add(groups, currentGroup);
|
||||
}
|
||||
for (let i = 0, il = ia.length; i < il; i += 3) {
|
||||
ChunkedArray.add3(indices, ia[i + 2] + offset, ia[i + 1] + offset, ia[i] + offset);
|
||||
}
|
||||
}
|
||||
|
||||
export function addCage(state: State, t: Mat4, cage: Cage, radius: number, detail: number, radialSegments: number) {
|
||||
const { vertices: va, edges: ea } = cage
|
||||
const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments }
|
||||
@@ -121,15 +140,6 @@ export namespace MeshBuilder {
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const nb = ChunkedArray.compact(normals, true) as Float32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount: state.vertices.elementCount,
|
||||
triangleCount: state.indices.elementCount,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
normalsComputed: true,
|
||||
}
|
||||
return Mesh.create(vb, ib, nb, gb, state.vertices.elementCount, state.indices.elementCount, mesh)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Task } from '../../../mol-task'
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Vec3, Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra'
|
||||
import { Sphere3D } from '../../../mol-math/geometry'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray, transformDirectionArray, computeIndexedVertexNormals, GroupMapping, createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { LocationIterator } from '../../util/location-iterator';
|
||||
import { createColors } from '../color-data';
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { ChunkedArray, hashFnv32a } from '../../../mol-data/util';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { MeshValues } from '../../../mol-gl/renderable/mesh';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
@@ -41,85 +41,96 @@ export interface Mesh {
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Flag indicating if normals are computed for the current set of vertices */
|
||||
normalsComputed: boolean,
|
||||
|
||||
/** Bounding sphere of the mesh */
|
||||
boundingSphere?: Sphere3D
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to vertex indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Mesh {
|
||||
export function create(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh?: Mesh): Mesh {
|
||||
return mesh ?
|
||||
update(vertices, indices, normals, groups, vertexCount, triangleCount, mesh) :
|
||||
fromArrays(vertices, indices, normals, groups, vertexCount, triangleCount)
|
||||
}
|
||||
|
||||
export function createEmpty(mesh?: Mesh): Mesh {
|
||||
const vb = mesh ? mesh.vertexBuffer.ref.value : new Float32Array(0)
|
||||
const ib = mesh ? mesh.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const nb = mesh ? mesh.normalBuffer.ref.value : new Float32Array(0)
|
||||
const gb = mesh ? mesh.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount: 0,
|
||||
triangleCount: 0,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
normalsComputed: true,
|
||||
}
|
||||
return create(vb, ib, nb, gb, 0, 0, mesh)
|
||||
}
|
||||
|
||||
export function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, normalsComputed: boolean): Mesh {
|
||||
return {
|
||||
kind: 'mesh',
|
||||
function hashCode(mesh: Mesh) {
|
||||
return hashFnv32a([
|
||||
mesh.vertexCount, mesh.triangleCount,
|
||||
mesh.vertexBuffer.ref.version, mesh.indexBuffer.ref.version,
|
||||
mesh.normalBuffer.ref.version, mesh.groupBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number): Mesh {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const mesh = {
|
||||
kind: 'mesh' as const,
|
||||
vertexCount,
|
||||
triangleCount,
|
||||
vertexBuffer: ValueCell.create(vertices),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
normalBuffer: ValueCell.create(normals),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
normalsComputed,
|
||||
}
|
||||
}
|
||||
|
||||
export function computeNormalsImmediate(mesh: Mesh) {
|
||||
if (mesh.normalsComputed) return;
|
||||
|
||||
const normals = mesh.normalBuffer.ref.value.length >= mesh.vertexCount * 3
|
||||
? mesh.normalBuffer.ref.value : new Float32Array(mesh.vertexBuffer.ref.value.length);
|
||||
|
||||
const v = mesh.vertexBuffer.ref.value, triangles = mesh.indexBuffer.ref.value;
|
||||
|
||||
if (normals === mesh.normalBuffer.ref.value) {
|
||||
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
|
||||
normals[i] = 0; normals[i + 1] = 0; normals[i + 2] = 0;
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(mesh)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(mesh.vertexBuffer.ref.value, mesh.vertexCount, 1)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (mesh.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(mesh.groupBuffer.ref.value, mesh.vertexCount)
|
||||
currentGroup = mesh.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return mesh
|
||||
}
|
||||
|
||||
const x = Vec3.zero(), y = Vec3.zero(), z = Vec3.zero(), d1 = Vec3.zero(), d2 = Vec3.zero(), n = Vec3.zero();
|
||||
for (let i = 0, ii = 3 * mesh.triangleCount; i < ii; i += 3) {
|
||||
const a = 3 * triangles[i], b = 3 * triangles[i + 1], c = 3 * triangles[i + 2];
|
||||
function update(vertices: Float32Array, indices: Uint32Array, normals: Float32Array, groups: Float32Array, vertexCount: number, triangleCount: number, mesh: Mesh) {
|
||||
mesh.vertexCount = vertexCount
|
||||
mesh.triangleCount = triangleCount
|
||||
ValueCell.update(mesh.vertexBuffer, vertices)
|
||||
ValueCell.update(mesh.indexBuffer, indices)
|
||||
ValueCell.update(mesh.normalBuffer, normals)
|
||||
ValueCell.update(mesh.groupBuffer, groups)
|
||||
return mesh
|
||||
}
|
||||
|
||||
Vec3.fromArray(x, v, a);
|
||||
Vec3.fromArray(y, v, b);
|
||||
Vec3.fromArray(z, v, c);
|
||||
Vec3.sub(d1, z, y);
|
||||
Vec3.sub(d2, x, y);
|
||||
Vec3.cross(n, d1, d2);
|
||||
export function computeNormals(mesh: Mesh) {
|
||||
const { vertexCount, triangleCount } = mesh
|
||||
const vertices = mesh.vertexBuffer.ref.value
|
||||
const indices = mesh.indexBuffer.ref.value
|
||||
|
||||
normals[a] += n[0]; normals[a + 1] += n[1]; normals[a + 2] += n[2];
|
||||
normals[b] += n[0]; normals[b + 1] += n[1]; normals[b + 2] += n[2];
|
||||
normals[c] += n[0]; normals[c + 1] += n[1]; normals[c + 2] += n[2];
|
||||
const normals = mesh.normalBuffer.ref.value.length >= vertexCount * 3
|
||||
? mesh.normalBuffer.ref.value
|
||||
: new Float32Array(vertexCount * 3);
|
||||
|
||||
if (normals === mesh.normalBuffer.ref.value) {
|
||||
normals.fill(0, 0, vertexCount * 3)
|
||||
}
|
||||
|
||||
for (let i = 0, ii = 3 * mesh.vertexCount; i < ii; i += 3) {
|
||||
const nx = normals[i];
|
||||
const ny = normals[i + 1];
|
||||
const nz = normals[i + 2];
|
||||
const f = 1.0 / Math.sqrt(nx * nx + ny * ny + nz * nz);
|
||||
normals[i] *= f; normals[i + 1] *= f; normals[i + 2] *= f;
|
||||
|
||||
// console.log([normals[i], normals[i + 1], normals[i + 2]], [v[i], v[i + 1], v[i + 2]])
|
||||
}
|
||||
computeIndexedVertexNormals(vertices, indices, normals, vertexCount, triangleCount)
|
||||
ValueCell.update(mesh.normalBuffer, normals);
|
||||
mesh.normalsComputed = true;
|
||||
}
|
||||
|
||||
export function checkForDuplicateVertices(mesh: Mesh, fractionDigits = 3) {
|
||||
@@ -129,7 +140,7 @@ export namespace Mesh {
|
||||
const hash = (v: Vec3, d: number) => `${v[0].toFixed(d)}|${v[1].toFixed(d)}|${v[2].toFixed(d)}`
|
||||
let duplicates = 0
|
||||
|
||||
const a = Vec3.zero()
|
||||
const a = Vec3()
|
||||
for (let i = 0, il = mesh.vertexCount; i < il; ++i) {
|
||||
Vec3.fromArray(a, v, i * 3)
|
||||
const k = hash(a, fractionDigits)
|
||||
@@ -144,63 +155,15 @@ export namespace Mesh {
|
||||
return duplicates
|
||||
}
|
||||
|
||||
export function computeNormals(surface: Mesh): Task<Mesh> {
|
||||
return Task.create<Mesh>('Surface (Compute Normals)', async ctx => {
|
||||
if (surface.normalsComputed) return surface;
|
||||
|
||||
await ctx.update('Computing normals...');
|
||||
computeNormalsImmediate(surface);
|
||||
return surface;
|
||||
});
|
||||
}
|
||||
|
||||
export function transformImmediate(mesh: Mesh, t: Mat4) {
|
||||
transformRangeImmediate(mesh, t, 0, mesh.vertexCount)
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(mesh: Mesh, t: Mat4, offset: number, count: number) {
|
||||
const tmpMat3 = Mat3()
|
||||
export function transform(mesh: Mesh, t: Mat4) {
|
||||
const v = mesh.vertexBuffer.ref.value
|
||||
transformPositionArray(t, v, offset, count)
|
||||
// TODO normals transformation does not work for an unknown reason, ASR
|
||||
// if (mesh.normalBuffer.ref.value) {
|
||||
// const n = getNormalMatrix(Mat3.zero(), t)
|
||||
// transformDirectionArray(n, mesh.normalBuffer.ref.value, offset, count)
|
||||
// mesh.normalsComputed = true;
|
||||
// }
|
||||
transformPositionArray(t, v, 0, mesh.vertexCount)
|
||||
if (!Mat4.isTranslationAndUniformScaling(t)) {
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
transformDirectionArray(n, mesh.normalBuffer.ref.value, 0, mesh.vertexCount)
|
||||
}
|
||||
ValueCell.update(mesh.vertexBuffer, v);
|
||||
mesh.normalsComputed = false;
|
||||
}
|
||||
|
||||
export function computeBoundingSphere(mesh: Mesh): Task<Mesh> {
|
||||
return Task.create<Mesh>('Mesh (Compute Bounding Sphere)', async ctx => {
|
||||
if (mesh.boundingSphere) {
|
||||
return mesh;
|
||||
}
|
||||
await ctx.update('Computing bounding sphere...');
|
||||
|
||||
const vertices = mesh.vertexBuffer.ref.value;
|
||||
let x = 0, y = 0, z = 0;
|
||||
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
|
||||
x += vertices[i];
|
||||
y += vertices[i + 1];
|
||||
z += vertices[i + 2];
|
||||
}
|
||||
x /= mesh.vertexCount;
|
||||
y /= mesh.vertexCount;
|
||||
z /= mesh.vertexCount;
|
||||
let r = 0;
|
||||
for (let i = 0, _c = vertices.length; i < _c; i += 3) {
|
||||
const dx = x - vertices[i];
|
||||
const dy = y - vertices[i + 1];
|
||||
const dz = z - vertices[i + 2];
|
||||
r = Math.max(r, dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
mesh.boundingSphere = {
|
||||
center: Vec3.create(x, y, z),
|
||||
radius: Math.sqrt(r)
|
||||
}
|
||||
return mesh;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,12 +191,12 @@ export namespace Mesh {
|
||||
group.currentIndex = vertexCount
|
||||
group.elementCount = vertexCount
|
||||
|
||||
const vi = Vec3.zero()
|
||||
const vj = Vec3.zero()
|
||||
const vk = Vec3.zero()
|
||||
const ni = Vec3.zero()
|
||||
const nj = Vec3.zero()
|
||||
const nk = Vec3.zero()
|
||||
const vi = Vec3()
|
||||
const vj = Vec3()
|
||||
const vk = Vec3()
|
||||
const ni = Vec3()
|
||||
const nj = Vec3()
|
||||
const nk = Vec3()
|
||||
|
||||
function add(i: number) {
|
||||
Vec3.fromArray(vi, vb, i * 3)
|
||||
@@ -361,6 +324,7 @@ export namespace Mesh {
|
||||
doubleSided: PD.Boolean(false),
|
||||
flipSided: PD.Boolean(false),
|
||||
flatShaded: PD.Boolean(false),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -388,10 +352,8 @@ export namespace Mesh {
|
||||
|
||||
const counts = { drawCount: mesh.triangleCount * 3, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
mesh.vertexBuffer.ref.value, mesh.vertexCount,
|
||||
transform.aTransform.ref.value, instanceCount
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: mesh.vertexBuffer,
|
||||
@@ -410,6 +372,7 @@ export namespace Mesh {
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dFlatShaded: ValueCell.create(props.flatShaded),
|
||||
dFlipSided: ValueCell.create(props.flipSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,13 +387,13 @@ export namespace Mesh {
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
|
||||
ValueCell.updateIfChanged(values.dFlipSided, props.flipSided)
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: MeshValues, mesh: Mesh) {
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, mesh.vertexCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(mesh.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -439,86 +402,3 @@ export namespace Mesh {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function addVertex(src: Float32Array, i: number, dst: Float32Array, j: number) {
|
||||
// dst[3 * j] += src[3 * i];
|
||||
// dst[3 * j + 1] += src[3 * i + 1];
|
||||
// dst[3 * j + 2] += src[3 * i + 2];
|
||||
// }
|
||||
|
||||
// function laplacianSmoothIter(surface: Surface, vertexCounts: Int32Array, vs: Float32Array, vertexWeight: number) {
|
||||
// const triCount = surface.triangleIndices.length,
|
||||
// src = surface.vertices;
|
||||
|
||||
// const triangleIndices = surface.triangleIndices;
|
||||
|
||||
// for (let i = 0; i < triCount; i += 3) {
|
||||
// const a = triangleIndices[i],
|
||||
// b = triangleIndices[i + 1],
|
||||
// c = triangleIndices[i + 2];
|
||||
|
||||
// addVertex(src, b, vs, a);
|
||||
// addVertex(src, c, vs, a);
|
||||
|
||||
// addVertex(src, a, vs, b);
|
||||
// addVertex(src, c, vs, b);
|
||||
|
||||
// addVertex(src, a, vs, c);
|
||||
// addVertex(src, b, vs, c);
|
||||
// }
|
||||
|
||||
// const vw = 2 * vertexWeight;
|
||||
// for (let i = 0, _b = surface.vertexCount; i < _b; i++) {
|
||||
// const n = vertexCounts[i] + vw;
|
||||
// vs[3 * i] = (vs[3 * i] + vw * src[3 * i]) / n;
|
||||
// vs[3 * i + 1] = (vs[3 * i + 1] + vw * src[3 * i + 1]) / n;
|
||||
// vs[3 * i + 2] = (vs[3 * i + 2] + vw * src[3 * i + 2]) / n;
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function laplacianSmoothComputation(ctx: Computation.Context, surface: Surface, iterCount: number, vertexWeight: number) {
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
|
||||
// const vertexCounts = new Int32Array(surface.vertexCount),
|
||||
// triCount = surface.triangleIndices.length;
|
||||
|
||||
// const tris = surface.triangleIndices;
|
||||
// for (let i = 0; i < triCount; i++) {
|
||||
// // in a triangle 2 edges touch each vertex, hence the constant.
|
||||
// vertexCounts[tris[i]] += 2;
|
||||
// }
|
||||
|
||||
// let vs = new Float32Array(surface.vertices.length);
|
||||
// let started = Utils.PerformanceMonitor.currentTime();
|
||||
// await ctx.updateProgress('Smoothing surface...', true);
|
||||
// for (let i = 0; i < iterCount; i++) {
|
||||
// if (i > 0) {
|
||||
// for (let j = 0, _b = vs.length; j < _b; j++) vs[j] = 0;
|
||||
// }
|
||||
// surface.normals = void 0;
|
||||
// laplacianSmoothIter(surface, vertexCounts, vs, vertexWeight);
|
||||
// const t = surface.vertices;
|
||||
// surface.vertices = <any>vs;
|
||||
// vs = <any>t;
|
||||
|
||||
// const time = Utils.PerformanceMonitor.currentTime();
|
||||
// if (time - started > Computation.UpdateProgressDelta) {
|
||||
// started = time;
|
||||
// await ctx.updateProgress('Smoothing surface...', true, i + 1, iterCount);
|
||||
// }
|
||||
// }
|
||||
// return surface;
|
||||
// }
|
||||
|
||||
// /*
|
||||
// * Smooths the vertices by averaging the neighborhood.
|
||||
// *
|
||||
// * Resets normals. Might replace vertex array.
|
||||
// */
|
||||
// export function laplacianSmooth(surface: Surface, iterCount: number = 1, vertexWeight: number = 1): Computation<Surface> {
|
||||
|
||||
// if (iterCount < 1) iterCount = 0;
|
||||
// if (iterCount === 0) return Computation.resolve(surface);
|
||||
|
||||
// return computation(async ctx => await laplacianSmoothComputation(ctx, surface, iterCount, (1.1 * vertexWeight) / 1.1));
|
||||
// }
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Points } from './points';
|
||||
|
||||
@@ -26,12 +25,7 @@ export namespace PointsBuilder {
|
||||
getPoints: () => {
|
||||
const cb = ChunkedArray.compact(centers, true) as Float32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'points',
|
||||
pointCount: centers.elementCount,
|
||||
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
|
||||
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
|
||||
}
|
||||
return Points.create(cb, gb, centers.elementCount, points)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util'
|
||||
import { Mat4 } from '../../../mol-math/linear-algebra'
|
||||
import { transformPositionArray/* , transformDirectionArray, getNormalMatrix */ } from '../../util';
|
||||
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
|
||||
GroupMapping,
|
||||
createGroupMapping} from '../../util';
|
||||
import { GeometryUtils } from '../geometry';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
@@ -14,7 +16,7 @@ import { createSizes } from '../size-data';
|
||||
import { TransformData } from '../transform-data';
|
||||
import { LocationIterator } from '../../util/location-iterator';
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { Theme } from '../../../mol-theme/theme';
|
||||
import { PointsValues } from '../../../mol-gl/renderable/points';
|
||||
@@ -23,37 +25,88 @@ import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
|
||||
/** Point cloud */
|
||||
export interface Points {
|
||||
readonly kind: 'points',
|
||||
|
||||
/** Number of vertices in the point cloud */
|
||||
pointCount: number,
|
||||
|
||||
/** Center buffer as array of xyz values wrapped in a value cell */
|
||||
readonly centerBuffer: ValueCell<Float32Array>,
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the points */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to point indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Points {
|
||||
export function create(centers: Float32Array, groups: Float32Array, pointCount: number, points?: Points): Points {
|
||||
return points ?
|
||||
update(centers, groups, pointCount, points) :
|
||||
fromArrays(centers, groups, pointCount)
|
||||
}
|
||||
|
||||
export function createEmpty(points?: Points): Points {
|
||||
const cb = points ? points.centerBuffer.ref.value : new Float32Array(0)
|
||||
const gb = points ? points.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'points',
|
||||
pointCount: 0,
|
||||
centerBuffer: points ? ValueCell.update(points.centerBuffer, cb) : ValueCell.create(cb),
|
||||
groupBuffer: points ? ValueCell.update(points.groupBuffer, gb) : ValueCell.create(gb),
|
||||
return create(cb, gb, 0, points)
|
||||
}
|
||||
|
||||
function hashCode(points: Points) {
|
||||
return hashFnv32a([
|
||||
points.pointCount, points.centerBuffer.ref.version, points.groupBuffer.ref.version,
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(centers: Float32Array, groups: Float32Array, pointCount: number): Points {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const points = {
|
||||
kind: 'points' as const,
|
||||
pointCount,
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(points)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(points.centerBuffer.ref.value, points.pointCount, 1)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (points.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(points.groupBuffer.ref.value, points.pointCount)
|
||||
currentGroup = points.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
export function transformImmediate(points: Points, t: Mat4) {
|
||||
transformRangeImmediate(points, t, 0, points.pointCount)
|
||||
function update(centers: Float32Array, groups: Float32Array, pointCount: number, points: Points) {
|
||||
points.pointCount = pointCount
|
||||
ValueCell.update(points.centerBuffer, centers)
|
||||
ValueCell.update(points.groupBuffer, groups)
|
||||
return points
|
||||
}
|
||||
|
||||
export function transformRangeImmediate(points: Points, t: Mat4, offset: number, count: number) {
|
||||
export function transform(points: Points, t: Mat4) {
|
||||
const c = points.centerBuffer.ref.value
|
||||
transformPositionArray(t, c, offset, count)
|
||||
transformPositionArray(t, c, 0, points.pointCount)
|
||||
ValueCell.update(points.centerBuffer, c);
|
||||
}
|
||||
|
||||
@@ -89,10 +142,8 @@ export namespace Points {
|
||||
|
||||
const counts = { drawCount: points.pointCount, groupCount, instanceCount }
|
||||
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
points.centerBuffer.ref.value, points.pointCount,
|
||||
transform.aTransform.ref.value, transform.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: points.centerBuffer,
|
||||
@@ -129,10 +180,9 @@ export namespace Points {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: PointsValues, points: Points) {
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, points.pointCount,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.clone(points.boundingSphere)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Spheres } from './spheres';
|
||||
|
||||
@@ -50,14 +49,7 @@ export namespace SpheresBuilder {
|
||||
const mb = ChunkedArray.compact(mappings, true) as Float32Array
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
return {
|
||||
kind: 'spheres',
|
||||
sphereCount: centers.elementCount / 4,
|
||||
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb),
|
||||
}
|
||||
return Spheres.create(cb, mb, ib, gb, centers.elementCount / 4, spheres)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,15 +13,16 @@ import { Theme } from '../../../mol-theme/theme';
|
||||
import { SpheresValues } from '../../../mol-gl/renderable/spheres';
|
||||
import { createColors } from '../color-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { calculateBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { createSizes, getMaxSize } from '../size-data';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
|
||||
/** Spheres */
|
||||
export interface Spheres {
|
||||
readonly kind: 'spheres',
|
||||
|
||||
@@ -36,28 +37,85 @@ export interface Spheres {
|
||||
readonly indexBuffer: ValueCell<Uint32Array>,
|
||||
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the spheres */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to sphere indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Spheres {
|
||||
export function create(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres?: Spheres): Spheres {
|
||||
return spheres ?
|
||||
update(centers, mappings, indices, groups, sphereCount, spheres) :
|
||||
fromArrays(centers, mappings, indices, groups, sphereCount)
|
||||
}
|
||||
|
||||
export function createEmpty(spheres?: Spheres): Spheres {
|
||||
const cb = spheres ? spheres.centerBuffer.ref.value : new Float32Array(0)
|
||||
const mb = spheres ? spheres.mappingBuffer.ref.value : new Float32Array(0)
|
||||
const ib = spheres ? spheres.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = spheres ? spheres.groupBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'spheres',
|
||||
sphereCount: 0,
|
||||
centerBuffer: spheres ? ValueCell.update(spheres.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: spheres ? ValueCell.update(spheres.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
indexBuffer: spheres ? ValueCell.update(spheres.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: spheres ? ValueCell.update(spheres.groupBuffer, gb) : ValueCell.create(gb)
|
||||
return create(cb, mb, ib, gb, 0, spheres)
|
||||
}
|
||||
|
||||
function hashCode(spheres: Spheres) {
|
||||
return hashFnv32a([
|
||||
spheres.sphereCount,
|
||||
spheres.centerBuffer.ref.version, spheres.mappingBuffer.ref.version,
|
||||
spheres.indexBuffer.ref.version, spheres.groupBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromArrays(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number): Spheres {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const spheres = {
|
||||
kind: 'spheres' as const,
|
||||
sphereCount,
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(spheres)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(spheres.centerBuffer.ref.value, spheres.sphereCount * 4, 4)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (spheres.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(spheres.groupBuffer.ref.value, spheres.sphereCount, 4)
|
||||
currentGroup = spheres.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return spheres
|
||||
}
|
||||
|
||||
function update(centers: Float32Array, mappings: Float32Array, indices: Uint32Array, groups: Float32Array, sphereCount: number, spheres: Spheres) {
|
||||
spheres.sphereCount = sphereCount
|
||||
ValueCell.update(spheres.centerBuffer, centers)
|
||||
ValueCell.update(spheres.mappingBuffer, mappings)
|
||||
ValueCell.update(spheres.indexBuffer, indices)
|
||||
ValueCell.update(spheres.groupBuffer, groups)
|
||||
return spheres
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
...BaseGeometry.Params,
|
||||
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
|
||||
doubleSided: PD.Boolean(false),
|
||||
ignoreLight: PD.Boolean(false),
|
||||
}
|
||||
export type Params = typeof Params
|
||||
|
||||
@@ -87,10 +145,8 @@ export namespace Spheres {
|
||||
const counts = { drawCount: spheres.sphereCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const padding = getMaxSize(size)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
spheres.centerBuffer.ref.value, spheres.sphereCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding, 4
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: spheres.centerBuffer,
|
||||
@@ -111,6 +167,7 @@ export namespace Spheres {
|
||||
...BaseGeometry.createValues(props, counts),
|
||||
uSizeFactor: ValueCell.create(props.sizeFactor),
|
||||
dDoubleSided: ValueCell.create(props.doubleSided),
|
||||
dIgnoreLight: ValueCell.create(props.ignoreLight),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,14 +181,14 @@ export namespace Spheres {
|
||||
BaseGeometry.updateValues(values, props)
|
||||
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor)
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight)
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: SpheresValues, spheres: Spheres) {
|
||||
const padding = getMaxSize(values)
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, spheres.sphereCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding, 4
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), spheres.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ export type FontVariant = 'normal' | 'small-caps'
|
||||
export type FontWeight = 'normal' | 'bold'
|
||||
|
||||
export const FontAtlasParams = {
|
||||
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
|
||||
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
|
||||
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
|
||||
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
|
||||
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
|
||||
fontFamily: PD.Select('sans-serif', [['sans-serif', 'Sans Serif'], ['monospace', 'Monospace'], ['serif', 'Serif'], ['cursive', 'Cursive']] as [FontFamily, string][]),
|
||||
fontQuality: PD.Select(3, [[0, 'lower'], [1, 'low'], [2, 'medium'], [3, 'high'], [4, 'higher']]),
|
||||
fontStyle: PD.Select('normal', [['normal', 'Normal'], ['italic', 'Italic'], ['oblique', 'Oblique']] as [FontStyle, string][]),
|
||||
fontVariant: PD.Select('normal', [['normal', 'Normal'], ['small-caps', 'Small Caps']] as [FontVariant, string][]),
|
||||
fontWeight: PD.Select('normal', [['normal', 'Normal'], ['bold', 'Bold']] as [FontWeight, string][]),
|
||||
}
|
||||
export type FontAtlasParams = typeof FontAtlasParams
|
||||
export type FontAtlasProps = PD.Values<FontAtlasParams>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
|
||||
import { ValueCell } from '../../../mol-util/value-cell'
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { Text } from './text';
|
||||
import { getFontAtlas } from './font-atlas';
|
||||
@@ -16,7 +15,7 @@ const quadIndices = new Uint16Array([
|
||||
])
|
||||
|
||||
export interface TextBuilder {
|
||||
add(str: string, x: number, y: number, z: number, depth: number, group: number): void
|
||||
add(str: string, x: number, y: number, z: number, depth: number, scale: number, group: number): void
|
||||
getText(): Text
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ export namespace TextBuilder {
|
||||
}
|
||||
|
||||
return {
|
||||
add: (str: string, x: number, y: number, z: number, depth: number, group: number) => {
|
||||
add: (str: string, x: number, y: number, z: number, depth: number, scale: number, group: number) => {
|
||||
let bWidth = 0
|
||||
const nChar = str.length
|
||||
|
||||
@@ -111,10 +110,10 @@ export namespace TextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
const xLeft = -xShift - margin - 0.1
|
||||
const xRight = bWidth - xShift + margin + 0.1
|
||||
const yTop = bHeight - yShift + margin
|
||||
const yBottom = -yShift - margin
|
||||
const xLeft = (-xShift - margin - 0.1) * scale
|
||||
const xRight = (bWidth - xShift + margin + 0.1) * scale
|
||||
const yTop = (bHeight - yShift + margin) * scale
|
||||
const yBottom = (-yShift - margin) * scale
|
||||
|
||||
// background
|
||||
if (background) {
|
||||
@@ -137,45 +136,49 @@ export namespace TextBuilder {
|
||||
let xBaseA: number, yBaseA: number
|
||||
let xBaseB: number, yBaseB: number
|
||||
let xBaseCenter: number, yBaseCenter: number
|
||||
|
||||
const scaledTetherLength = tetherLength * scale
|
||||
const scaledTetherBaseWidth = tetherBaseWidth * scale
|
||||
|
||||
switch (attachment) {
|
||||
case 'bottom-left':
|
||||
xTip = xLeft - tetherLength / 2
|
||||
xBaseA = xLeft + tetherBaseWidth / 2
|
||||
xTip = xLeft - scaledTetherLength / 2
|
||||
xBaseA = xLeft + scaledTetherBaseWidth / 2
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = yBottom - tetherLength / 2
|
||||
yTip = yBottom - scaledTetherLength / 2
|
||||
yBaseA = yBottom
|
||||
yBaseB = yBottom + tetherBaseWidth / 2
|
||||
yBaseB = yBottom + scaledTetherBaseWidth / 2
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'bottom-center':
|
||||
xTip = 0
|
||||
xBaseA = tetherBaseWidth / 2
|
||||
xBaseB = -tetherBaseWidth / 2
|
||||
xBaseA = scaledTetherBaseWidth / 2
|
||||
xBaseB = -scaledTetherBaseWidth / 2
|
||||
xBaseCenter = 0
|
||||
yTip = yBottom - tetherLength
|
||||
yTip = yBottom - scaledTetherLength
|
||||
yBaseA = yBottom
|
||||
yBaseB = yBottom
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'bottom-right':
|
||||
xTip = xRight + tetherLength / 2
|
||||
xTip = xRight + scaledTetherLength / 2
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight - tetherBaseWidth / 2
|
||||
xBaseB = xRight - scaledTetherBaseWidth / 2
|
||||
xBaseCenter = xRight
|
||||
yTip = yBottom - tetherLength / 2
|
||||
yBaseA = yBottom + tetherBaseWidth / 2
|
||||
yTip = yBottom - scaledTetherLength / 2
|
||||
yBaseA = yBottom + scaledTetherBaseWidth / 2
|
||||
yBaseB = yBottom
|
||||
yBaseCenter = yBottom
|
||||
break
|
||||
case 'middle-left':
|
||||
xTip = xLeft - tetherLength
|
||||
xTip = xLeft - scaledTetherLength
|
||||
xBaseA = xLeft
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = 0
|
||||
yBaseA = -tetherBaseWidth / 2
|
||||
yBaseB = tetherBaseWidth / 2
|
||||
yBaseA = -scaledTetherBaseWidth / 2
|
||||
yBaseB = scaledTetherBaseWidth / 2
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'middle-center':
|
||||
@@ -189,42 +192,42 @@ export namespace TextBuilder {
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'middle-right':
|
||||
xTip = xRight + tetherLength
|
||||
xTip = xRight + scaledTetherLength
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight
|
||||
xBaseCenter = xRight
|
||||
yTip = 0
|
||||
yBaseA = tetherBaseWidth / 2
|
||||
yBaseB = -tetherBaseWidth / 2
|
||||
yBaseA = scaledTetherBaseWidth / 2
|
||||
yBaseB = -scaledTetherBaseWidth / 2
|
||||
yBaseCenter = 0
|
||||
break
|
||||
case 'top-left':
|
||||
xTip = xLeft - tetherLength / 2
|
||||
xBaseA = xLeft + tetherBaseWidth / 2
|
||||
xTip = xLeft - scaledTetherLength / 2
|
||||
xBaseA = xLeft + scaledTetherBaseWidth / 2
|
||||
xBaseB = xLeft
|
||||
xBaseCenter = xLeft
|
||||
yTip = yTop + tetherLength / 2
|
||||
yTip = yTop + scaledTetherLength / 2
|
||||
yBaseA = yTop
|
||||
yBaseB = yTop - tetherBaseWidth / 2
|
||||
yBaseB = yTop - scaledTetherBaseWidth / 2
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
case 'top-center':
|
||||
xTip = 0
|
||||
xBaseA = tetherBaseWidth / 2
|
||||
xBaseB = -tetherBaseWidth / 2
|
||||
xBaseA = scaledTetherBaseWidth / 2
|
||||
xBaseB = -scaledTetherBaseWidth / 2
|
||||
xBaseCenter = 0
|
||||
yTip = yTop + tetherLength
|
||||
yTip = yTop + scaledTetherLength
|
||||
yBaseA = yTop
|
||||
yBaseB = yTop
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
case 'top-right':
|
||||
xTip = xRight + tetherLength / 2
|
||||
xTip = xRight + scaledTetherLength / 2
|
||||
xBaseA = xRight
|
||||
xBaseB = xRight - tetherBaseWidth / 2
|
||||
xBaseB = xRight - scaledTetherBaseWidth / 2
|
||||
xBaseCenter = xRight
|
||||
yTip = yTop + tetherLength / 2
|
||||
yBaseA = yTop - tetherBaseWidth / 2
|
||||
yTip = yTop + scaledTetherLength / 2
|
||||
yBaseA = yTop - scaledTetherBaseWidth / 2
|
||||
yBaseB = yTop
|
||||
yBaseCenter = yTop
|
||||
break
|
||||
@@ -252,10 +255,15 @@ export namespace TextBuilder {
|
||||
for (let iChar = 0; iChar < nChar; ++iChar) {
|
||||
const c = fontAtlas.get(str[iChar])
|
||||
|
||||
ChunkedArray.add2(mappings, xadvance - xShift, c.nh - yShift) // top left
|
||||
ChunkedArray.add2(mappings, xadvance - xShift, -yShift) // bottom left
|
||||
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, c.nh - yShift) // top right
|
||||
ChunkedArray.add2(mappings, xadvance + c.nw - xShift, -yShift) // bottom right
|
||||
const left = (xadvance - xShift) * scale
|
||||
const right = (xadvance + c.nw - xShift) * scale
|
||||
const top = (c.nh - yShift) * scale
|
||||
const bottom = (-yShift) * scale
|
||||
|
||||
ChunkedArray.add2(mappings, left, top)
|
||||
ChunkedArray.add2(mappings, left, bottom)
|
||||
ChunkedArray.add2(mappings, right, top)
|
||||
ChunkedArray.add2(mappings, right, bottom)
|
||||
|
||||
const texWidth = fontAtlas.texture.width
|
||||
const texHeight = fontAtlas.texture.height
|
||||
@@ -281,17 +289,7 @@ export namespace TextBuilder {
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array
|
||||
const tb = ChunkedArray.compact(tcoords, true) as Float32Array
|
||||
return {
|
||||
kind: 'text',
|
||||
charCount: indices.elementCount / 2,
|
||||
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
|
||||
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
|
||||
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
|
||||
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb),
|
||||
}
|
||||
return Text.create(ft, cb,mb, db, ib, gb, tb, indices.elementCount / 2, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -15,7 +15,7 @@ import { createSizes, getMaxSize } from '../size-data';
|
||||
import { createMarkers } from '../marker-data';
|
||||
import { ColorNames } from '../../../mol-util/color/names';
|
||||
import { Sphere3D } from '../../../mol-math/geometry';
|
||||
import { calculateBoundingSphere, TextureImage, createTextureImage } from '../../../mol-gl/renderable/util';
|
||||
import { TextureImage, createTextureImage, calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
|
||||
import { TextValues } from '../../../mol-gl/renderable/text';
|
||||
import { Color } from '../../../mol-util/color';
|
||||
import { Vec3 } from '../../../mol-math/linear-algebra';
|
||||
@@ -26,6 +26,8 @@ import { createRenderObject as _createRenderObject } from '../../../mol-gl/rende
|
||||
import { BaseGeometry } from '../base';
|
||||
import { createEmptyOverpaint } from '../overpaint-data';
|
||||
import { createEmptyTransparency } from '../transparency-data';
|
||||
import { hashFnv32a } from '../../../mol-data/util';
|
||||
import { GroupMapping, createGroupMapping } from '../../util';
|
||||
|
||||
type TextAttachment = (
|
||||
'bottom-left' | 'bottom-center' | 'bottom-right' |
|
||||
@@ -38,7 +40,8 @@ export interface Text {
|
||||
readonly kind: 'text',
|
||||
|
||||
/** Number of characters in the text */
|
||||
readonly charCount: number,
|
||||
charCount: number,
|
||||
|
||||
/** Font Atlas */
|
||||
readonly fontTexture: ValueCell<TextureImage<Uint8Array>>,
|
||||
|
||||
@@ -54,9 +57,20 @@ export interface Text {
|
||||
readonly groupBuffer: ValueCell<Float32Array>,
|
||||
/** Texture coordinates buffer as array of uv values wrapped in a value cell */
|
||||
readonly tcoordBuffer: ValueCell<Float32Array>,
|
||||
|
||||
/** Bounding sphere of the text */
|
||||
readonly boundingSphere: Sphere3D
|
||||
/** Maps group ids to text indices */
|
||||
readonly groupMapping: GroupMapping
|
||||
}
|
||||
|
||||
export namespace Text {
|
||||
export function create(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text?: Text): Text {
|
||||
return text ?
|
||||
update(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount, text) :
|
||||
fromData(fontTexture, centers, mappings, depths, indices, groups, tcoords, charCount)
|
||||
}
|
||||
|
||||
export function createEmpty(text?: Text): Text {
|
||||
const ft = text ? text.fontTexture.ref.value : createTextureImage(0, 1, Uint8Array)
|
||||
const cb = text ? text.centerBuffer.ref.value : new Float32Array(0)
|
||||
@@ -65,17 +79,66 @@ export namespace Text {
|
||||
const ib = text ? text.indexBuffer.ref.value : new Uint32Array(0)
|
||||
const gb = text ? text.groupBuffer.ref.value : new Float32Array(0)
|
||||
const tb = text ? text.tcoordBuffer.ref.value : new Float32Array(0)
|
||||
return {
|
||||
kind: 'text',
|
||||
charCount: 0,
|
||||
fontTexture: text ? ValueCell.update(text.fontTexture, ft) : ValueCell.create(ft),
|
||||
centerBuffer: text ? ValueCell.update(text.centerBuffer, cb) : ValueCell.create(cb),
|
||||
mappingBuffer: text ? ValueCell.update(text.mappingBuffer, mb) : ValueCell.create(mb),
|
||||
depthBuffer: text ? ValueCell.update(text.depthBuffer, db) : ValueCell.create(db),
|
||||
indexBuffer: text ? ValueCell.update(text.indexBuffer, ib) : ValueCell.create(ib),
|
||||
groupBuffer: text ? ValueCell.update(text.groupBuffer, gb) : ValueCell.create(gb),
|
||||
tcoordBuffer: text ? ValueCell.update(text.tcoordBuffer, tb) : ValueCell.create(tb)
|
||||
return create(ft, cb, mb, db, ib, gb, tb, 0, text)
|
||||
}
|
||||
|
||||
function hashCode(text: Text) {
|
||||
return hashFnv32a([
|
||||
text.charCount, text.fontTexture.ref.version,
|
||||
text.centerBuffer.ref.version, text.mappingBuffer.ref.version,
|
||||
text.depthBuffer.ref.version, text.indexBuffer.ref.version,
|
||||
text.groupBuffer.ref.version, text.tcoordBuffer.ref.version
|
||||
])
|
||||
}
|
||||
|
||||
function fromData(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number): Text {
|
||||
|
||||
const boundingSphere = Sphere3D()
|
||||
let groupMapping: GroupMapping
|
||||
|
||||
let currentHash = -1
|
||||
let currentGroup = -1
|
||||
|
||||
const text = {
|
||||
kind: 'text' as const,
|
||||
charCount,
|
||||
fontTexture: ValueCell.create(fontTexture),
|
||||
centerBuffer: ValueCell.create(centers),
|
||||
mappingBuffer: ValueCell.create(mappings),
|
||||
depthBuffer: ValueCell.create(depths),
|
||||
indexBuffer: ValueCell.create(indices),
|
||||
groupBuffer: ValueCell.create(groups),
|
||||
tcoordBuffer: ValueCell.create(tcoords),
|
||||
get boundingSphere() {
|
||||
const newHash = hashCode(text)
|
||||
if (newHash !== currentHash) {
|
||||
const b = calculateInvariantBoundingSphere(text.centerBuffer.ref.value, text.charCount * 4, 4)
|
||||
Sphere3D.copy(boundingSphere, b)
|
||||
currentHash = newHash
|
||||
}
|
||||
return boundingSphere
|
||||
},
|
||||
get groupMapping() {
|
||||
if (text.groupBuffer.ref.version !== currentGroup) {
|
||||
groupMapping = createGroupMapping(text.groupBuffer.ref.value, text.charCount, 4)
|
||||
currentGroup = text.groupBuffer.ref.version
|
||||
}
|
||||
return groupMapping
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
function update(fontTexture: TextureImage<Uint8Array>, centers: Float32Array, mappings: Float32Array, depths: Float32Array, indices: Uint32Array, groups: Float32Array, tcoords: Float32Array, charCount: number, text: Text) {
|
||||
text.charCount = charCount
|
||||
ValueCell.update(text.fontTexture, fontTexture)
|
||||
ValueCell.update(text.centerBuffer, centers)
|
||||
ValueCell.update(text.mappingBuffer, mappings)
|
||||
ValueCell.update(text.depthBuffer, depths)
|
||||
ValueCell.update(text.indexBuffer, indices)
|
||||
ValueCell.update(text.groupBuffer, groups)
|
||||
ValueCell.update(text.tcoordBuffer, tcoords)
|
||||
return text
|
||||
}
|
||||
|
||||
export const Params = {
|
||||
@@ -130,10 +193,8 @@ export namespace Text {
|
||||
const counts = { drawCount: text.charCount * 2 * 3, groupCount, instanceCount }
|
||||
|
||||
const padding = getPadding(text.mappingBuffer.ref.value, text.depthBuffer.ref.value, text.charCount, getMaxSize(size))
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
text.centerBuffer.ref.value, text.charCount * 4,
|
||||
transform.aTransform.ref.value, instanceCount, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount)
|
||||
|
||||
return {
|
||||
aPosition: text.centerBuffer,
|
||||
@@ -194,10 +255,9 @@ export namespace Text {
|
||||
|
||||
function updateBoundingSphere(values: TextValues, text: Text) {
|
||||
const padding = getPadding(values.aMapping.ref.value, values.aDepth.ref.value, text.charCount, getMaxSize(values))
|
||||
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
|
||||
values.aPosition.ref.value, text.charCount * 4,
|
||||
values.aTransform.ref.value, values.instanceCount.ref.value, padding
|
||||
)
|
||||
const invariantBoundingSphere = Sphere3D.expand(Sphere3D(), text.boundingSphere, padding)
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
}
|
||||
@@ -215,6 +275,7 @@ export namespace Text {
|
||||
|
||||
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
||||
BaseGeometry.updateRenderableState(state, props)
|
||||
state.pickable = false
|
||||
state.opaque = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,38 +27,39 @@ export interface TextureMesh {
|
||||
readonly kind: 'texture-mesh',
|
||||
|
||||
/** Number of vertices in the texture-mesh */
|
||||
readonly vertexCount: ValueCell<number>,
|
||||
vertexCount: number,
|
||||
/** Number of groups in the texture-mesh */
|
||||
readonly groupCount: ValueCell<number>,
|
||||
groupCount: number,
|
||||
|
||||
readonly geoTextureDim: ValueCell<Vec2>,
|
||||
/** texture has vertex positions in XYZ and group id in W */
|
||||
readonly vertexGroupTexture: ValueCell<Texture>,
|
||||
readonly normalTexture: ValueCell<Texture>,
|
||||
|
||||
readonly boundingSphere: ValueCell<Sphere3D>,
|
||||
readonly boundingSphere: Sphere3D
|
||||
}
|
||||
|
||||
export namespace TextureMesh {
|
||||
export function create(vertexCount: number, groupCount: number, vertexGroupTexture: Texture, normalTexture: Texture, boundingSphere: Sphere3D, textureMesh?: TextureMesh): TextureMesh {
|
||||
const { width, height } = vertexGroupTexture
|
||||
const width = vertexGroupTexture.getWidth()
|
||||
const height = vertexGroupTexture.getHeight()
|
||||
if (textureMesh) {
|
||||
ValueCell.update(textureMesh.vertexCount, vertexCount)
|
||||
ValueCell.update(textureMesh.groupCount, groupCount)
|
||||
textureMesh.vertexCount = vertexCount
|
||||
textureMesh.groupCount = groupCount
|
||||
ValueCell.update(textureMesh.geoTextureDim, Vec2.set(textureMesh.geoTextureDim.ref.value, width, height))
|
||||
ValueCell.update(textureMesh.vertexGroupTexture, vertexGroupTexture)
|
||||
ValueCell.update(textureMesh.normalTexture, normalTexture)
|
||||
ValueCell.update(textureMesh.boundingSphere, boundingSphere)
|
||||
Sphere3D.copy(textureMesh.boundingSphere, boundingSphere)
|
||||
return textureMesh
|
||||
} else {
|
||||
return {
|
||||
kind: 'texture-mesh',
|
||||
vertexCount: ValueCell.create(vertexCount),
|
||||
groupCount: ValueCell.create(groupCount),
|
||||
vertexCount,
|
||||
groupCount,
|
||||
geoTextureDim: ValueCell.create(Vec2.create(width, height)),
|
||||
vertexGroupTexture: ValueCell.create(vertexGroupTexture),
|
||||
normalTexture: ValueCell.create(normalTexture),
|
||||
boundingSphere: ValueCell.create(boundingSphere),
|
||||
boundingSphere: Sphere3D.clone(boundingSphere),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,9 +94,9 @@ export namespace TextureMesh {
|
||||
const overpaint = createEmptyOverpaint()
|
||||
const transparency = createEmptyTransparency()
|
||||
|
||||
const counts = { drawCount: textureMesh.vertexCount.ref.value, groupCount, instanceCount }
|
||||
const counts = { drawCount: textureMesh.vertexCount, groupCount, instanceCount }
|
||||
|
||||
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere.ref.value, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
const transformBoundingSphere = calculateTransformBoundingSphere(textureMesh.boundingSphere, transform.aTransform.ref.value, transform.instanceCount.ref.value)
|
||||
|
||||
return {
|
||||
uGeoTexDim: textureMesh.geoTextureDim,
|
||||
@@ -103,9 +104,9 @@ export namespace TextureMesh {
|
||||
tNormal: textureMesh.normalTexture,
|
||||
|
||||
// aGroup is used as a vertex index here and the group id is retirieved from tPositionGroup
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount.ref.value))),
|
||||
aGroup: ValueCell.create(fillSerial(new Float32Array(textureMesh.vertexCount))),
|
||||
boundingSphere: ValueCell.create(transformBoundingSphere),
|
||||
invariantBoundingSphere: textureMesh.boundingSphere,
|
||||
invariantBoundingSphere: ValueCell.create(Sphere3D.clone(textureMesh.boundingSphere)),
|
||||
|
||||
...color,
|
||||
...marker,
|
||||
@@ -128,14 +129,7 @@ export namespace TextureMesh {
|
||||
}
|
||||
|
||||
function updateValues(values: TextureMeshValues, props: PD.Values<Params>) {
|
||||
if (Color.fromNormalizedArray(values.uHighlightColor.ref.value, 0) !== props.highlightColor) {
|
||||
ValueCell.update(values.uHighlightColor, Color.toArrayNormalized(props.highlightColor, values.uHighlightColor.ref.value, 0))
|
||||
}
|
||||
if (Color.fromNormalizedArray(values.uSelectColor.ref.value, 0) !== props.selectColor) {
|
||||
ValueCell.update(values.uSelectColor, Color.toArrayNormalized(props.selectColor, values.uSelectColor.ref.value, 0))
|
||||
}
|
||||
ValueCell.updateIfChanged(values.alpha, props.alpha) // `uAlpha` is set in renderable.render
|
||||
ValueCell.updateIfChanged(values.dUseFog, props.useFog)
|
||||
|
||||
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided)
|
||||
ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded)
|
||||
@@ -148,7 +142,7 @@ export namespace TextureMesh {
|
||||
}
|
||||
|
||||
function updateBoundingSphere(values: TextureMeshValues, textureMesh: TextureMesh) {
|
||||
const invariantBoundingSphere = textureMesh.boundingSphere.ref.value
|
||||
const invariantBoundingSphere = textureMesh.boundingSphere
|
||||
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value)
|
||||
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
|
||||
ValueCell.update(values.boundingSphere, boundingSphere)
|
||||
|
||||
@@ -61,23 +61,20 @@ export function PerforatedBox() {
|
||||
let boxCage: Cage
|
||||
export function BoxCage() {
|
||||
if (!boxCage) {
|
||||
boxCage = createCage(
|
||||
[
|
||||
0.5, 0.5, -0.5, // bottom
|
||||
-0.5, 0.5, -0.5,
|
||||
-0.5, -0.5, -0.5,
|
||||
0.5, -0.5, -0.5,
|
||||
0.5, 0.5, 0.5, // top
|
||||
-0.5, 0.5, 0.5,
|
||||
-0.5, -0.5, 0.5,
|
||||
0.5, -0.5, 0.5
|
||||
],
|
||||
[
|
||||
0, 4, 1, 5, 2, 6, 3, 7, // sides
|
||||
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
|
||||
4, 5, 5, 6, 6, 7, 7, 4 // top base
|
||||
]
|
||||
)
|
||||
boxCage = createCage([
|
||||
0.5, 0.5, -0.5, // bottom
|
||||
-0.5, 0.5, -0.5,
|
||||
-0.5, -0.5, -0.5,
|
||||
0.5, -0.5, -0.5,
|
||||
0.5, 0.5, 0.5, // top
|
||||
-0.5, 0.5, 0.5,
|
||||
-0.5, -0.5, 0.5,
|
||||
0.5, -0.5, 0.5
|
||||
], [
|
||||
0, 4, 1, 5, 2, 6, 3, 7, // sides
|
||||
0, 1, 1, 2, 2, 3, 3, 0, // bottom base
|
||||
4, 5, 5, 6, 6, 7, 7, 4 // top base
|
||||
])
|
||||
}
|
||||
return boxCage
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export function createCage(vertices: ArrayLike<number>, edges: ArrayLike<number>
|
||||
return { vertices, edges }
|
||||
}
|
||||
|
||||
export function copyCage(cage: Cage): Cage {
|
||||
export function cloneCage(cage: Cage): Cage {
|
||||
return {
|
||||
vertices: new Float32Array(cage.vertices),
|
||||
edges: new Uint32Array(cage.edges)
|
||||
|
||||
69
src/mol-geo/primitive/circle.ts
Normal file
69
src/mol-geo/primitive/circle.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Primitive } from './primitive';
|
||||
|
||||
export const DefaultCircleProps = {
|
||||
radius: 1,
|
||||
segments: 36,
|
||||
thetaStart: 0,
|
||||
thetaLength: Math.PI * 2
|
||||
}
|
||||
export type CirclerProps = Partial<typeof DefaultCircleProps>
|
||||
|
||||
export function Circle(props?: CirclerProps): Primitive {
|
||||
const { radius, segments, thetaStart, thetaLength } = { ...DefaultCircleProps, ...props }
|
||||
|
||||
const isFull = thetaLength === Math.PI * 2
|
||||
const count = isFull ? segments + 1 : segments + 2
|
||||
|
||||
const vertices = new Float32Array(count * 3)
|
||||
const normals = new Float32Array(count * 3)
|
||||
const indices = new Uint32Array(segments * 3)
|
||||
|
||||
// center
|
||||
vertices[0] = 0; vertices[1] = 0; vertices[2] = 0;
|
||||
normals[0] = 0; normals[1] = 1; normals[2] = 0;
|
||||
|
||||
// vertices & normals
|
||||
for (let s = 0, i = 3; s < segments; ++s, i += 3) {
|
||||
const segment = thetaStart + s / segments * thetaLength;
|
||||
|
||||
vertices[i] = radius * Math.sin(segment)
|
||||
vertices[i + 1] = 0
|
||||
vertices[i + 2] = radius * Math.cos(segment)
|
||||
|
||||
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
|
||||
}
|
||||
|
||||
// indices
|
||||
for (let s = 1, i = 0; s < segments; ++s, i += 3) {
|
||||
indices[i] = s; indices[i + 1] = s + 1; indices[i + 2] = 0;
|
||||
}
|
||||
|
||||
if (isFull) {
|
||||
const j = (segments - 1) * 3
|
||||
indices[j] = segments
|
||||
indices[j + 1] = 1
|
||||
indices[j + 2] = 0
|
||||
} else {
|
||||
const segment = thetaStart + thetaLength;
|
||||
const i = (segments + 1) * 3
|
||||
|
||||
vertices[i] = radius * Math.sin(segment)
|
||||
vertices[i + 1] = 0
|
||||
vertices[i + 2] = radius * Math.cos(segment)
|
||||
|
||||
normals[i] = 0; normals[i + 1] = 1; normals[i + 2] = 0;
|
||||
|
||||
const j = (segments - 1) * 3
|
||||
indices[j] = segments
|
||||
indices[j + 1] = segments + 1
|
||||
indices[j + 2] = 0
|
||||
}
|
||||
|
||||
return { vertices, normals, indices }
|
||||
}
|
||||
@@ -14,46 +14,46 @@ const b = 1 / t;
|
||||
const c = 2 - t;
|
||||
|
||||
export const dodecahedronVertices: ReadonlyArray<number> = [
|
||||
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
|
||||
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
|
||||
c, 0, a, -c, 0, a, -b, b, b, 0, a, c, b, b, b,
|
||||
b, -b, b, 0, -a, c, -b, -b, b, c, 0, -a, -c, 0, -a,
|
||||
-b, -b, -b, 0, -a, -c, b, -b, -b, b, b, -b, 0, a, -c,
|
||||
-b, b, -b, a, c, 0, -a, c, 0, -a, -c, 0, a, -c, 0
|
||||
];
|
||||
|
||||
/** indices of pentagonal faces, groups of five */
|
||||
export const dodecahedronFaces: ReadonlyArray<number> = [
|
||||
4, 3, 2, 1, 0,
|
||||
7, 6, 5, 0, 1,
|
||||
4, 3, 2, 1, 0,
|
||||
7, 6, 5, 0, 1,
|
||||
12, 11, 10, 9, 8,
|
||||
15, 14, 13, 8, 9,
|
||||
14, 3, 4, 16, 13,
|
||||
3, 14, 15, 17, 2,
|
||||
3, 14, 15, 17, 2,
|
||||
11, 6, 7, 18, 10,
|
||||
6, 11, 12, 19, 5,
|
||||
4, 0, 5, 19, 16,
|
||||
6, 11, 12, 19, 5,
|
||||
4, 0, 5, 19, 16,
|
||||
12, 8, 13, 16, 19,
|
||||
15, 9, 10, 18, 17,
|
||||
7, 1, 2, 17, 18
|
||||
7, 1, 2, 17, 18
|
||||
];
|
||||
|
||||
const dodecahedronIndices: ReadonlyArray<number> = [ // pentagonal faces
|
||||
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
|
||||
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
|
||||
4, 3, 2, 2, 1, 0, 4, 2, 0, // 4, 3, 2, 1, 0
|
||||
7, 6, 5, 5, 0, 1, 7, 5, 1, // 7, 6, 5, 0, 1
|
||||
12, 11, 10, 10, 9, 8, 12, 10, 8, // 12, 11, 10, 9, 8
|
||||
15, 14, 13, 13, 8, 9, 15, 13, 9, // 15, 14, 13, 8, 9
|
||||
14, 3, 4, 4, 16, 13, 14, 4, 13, // 14, 3, 4, 16, 13
|
||||
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
|
||||
3, 14, 15, 15, 17, 2, 3, 15, 2, // 3, 14, 15, 17, 2
|
||||
11, 6, 7, 7, 18, 10, 11, 7, 10, // 11, 6, 7, 18, 10
|
||||
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
|
||||
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
|
||||
6, 11, 12, 12, 19, 5, 6, 12, 5, // 6, 11, 12, 19, 5
|
||||
4, 0, 5, 5, 19, 16, 4, 5, 16, // 4, 0, 5, 19, 16
|
||||
12, 8, 13, 13, 16, 19, 12, 13, 19, // 12, 8, 13, 16, 19
|
||||
15, 9, 10, 10, 18, 17, 15, 10, 17, // 15, 9, 10, 18, 17
|
||||
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
|
||||
7, 1, 2, 2, 17, 18, 7, 2, 18, // 7, 1, 2, 17, 18
|
||||
];
|
||||
|
||||
const dodecahedronEdges: ReadonlyArray<number> = [
|
||||
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
|
||||
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
|
||||
0, 1, 0, 4, 0, 5, 1, 2, 1, 7, 2, 3, 2, 17, 3, 4, 3, 14, 4, 16,
|
||||
5, 6, 5, 19, 6, 7, 6, 11, 7, 18, 8, 9, 8, 12, 8, 13, 9, 10, 9, 15,
|
||||
10, 11, 10, 18, 11, 12, 12, 19, 13, 14, 13, 16, 14, 15, 15, 17, 16, 19, 17, 18,
|
||||
]
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ const t = (1 + Math.sqrt(5)) / 2;
|
||||
|
||||
const icosahedronVertices: ReadonlyArray<number> = [
|
||||
-1, t, 0, 1, t, 0, -1, -t, 0, 1, -t, 0,
|
||||
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
|
||||
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
|
||||
0, -1, t, 0, 1, t, 0, -1, -t, 0, 1, -t,
|
||||
t, 0, -1, t, 0, 1, -t, 0, -1, -t, 0, 1
|
||||
];
|
||||
|
||||
const icosahedronIndices: ReadonlyArray<number> = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -28,7 +28,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
appplyRadius(vertices, radius);
|
||||
|
||||
const normals = new Float32Array(vertices.length);
|
||||
computeIndexedVertexNormals(vertices, indices, normals)
|
||||
computeIndexedVertexNormals(vertices, indices, normals, vertices.length / 3, indices.length / 3)
|
||||
|
||||
return {
|
||||
vertices: new Float32Array(vertices),
|
||||
@@ -39,9 +39,9 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
// helper functions
|
||||
|
||||
function subdivide(detail: number) {
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const a = Vec3()
|
||||
const b = Vec3()
|
||||
const c = Vec3()
|
||||
|
||||
// iterate over all faces and apply a subdivison with the given detail value
|
||||
for (let i = 0; i < _indices.length; i += 3) {
|
||||
@@ -66,10 +66,10 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
for (let i = 0; i <= cols; ++i) {
|
||||
v[i] = []
|
||||
|
||||
const aj = Vec3.zero()
|
||||
const aj = Vec3()
|
||||
Vec3.lerp(aj, a, c, i / cols)
|
||||
|
||||
const bj = Vec3.zero()
|
||||
const bj = Vec3()
|
||||
Vec3.lerp(bj, b, c, i / cols)
|
||||
|
||||
const rows = cols - i
|
||||
@@ -77,7 +77,7 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
if (j === 0 && i === cols) {
|
||||
v[i][j] = aj
|
||||
} else {
|
||||
const abj = Vec3.zero()
|
||||
const abj = Vec3()
|
||||
Vec3.lerp(abj, aj, bj, j / rows)
|
||||
|
||||
v[i][j] = abj
|
||||
@@ -90,7 +90,6 @@ export function Polyhedron(_vertices: ArrayLike<number>, _indices: ArrayLike<num
|
||||
for (let j = 0; j < 2 * (cols - i) - 1; ++j) {
|
||||
const k = Math.floor(j / 2)
|
||||
if (j % 2 === 0) {
|
||||
builder.add
|
||||
builder.add(v[i][k + 1], v[i + 1][k], v[i][k])
|
||||
} else {
|
||||
builder.add(v[i][k + 1], v[i + 1][k + 1], v[i + 1][k])
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4, Mat3 } from '../../mol-math/linear-algebra';
|
||||
import { getNormalMatrix } from '../util';
|
||||
import { NumberArray } from '../../mol-util/type-helpers';
|
||||
|
||||
export interface Primitive {
|
||||
@@ -74,7 +73,7 @@ const tmpMat3 = Mat3.zero()
|
||||
/** Transform primitive in-place */
|
||||
export function transformPrimitive(primitive: Primitive, t: Mat4) {
|
||||
const { vertices, normals } = primitive
|
||||
const n = getNormalMatrix(tmpMat3, t)
|
||||
const n = Mat3.directionTransform(tmpMat3, t)
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
// position
|
||||
Vec3.transformMat4(tmpV, Vec3.fromArray(tmpV, vertices, i), t)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -8,20 +8,16 @@ import { createPrimitive, Primitive } from './primitive';
|
||||
import { createCage, Cage } from './cage';
|
||||
|
||||
export const tetrahedronVertices: ReadonlyArray<number> = [
|
||||
0.7071, 0, 0, -0.3535, 0.6123, 0, -0.3535, -0.6123, 0,
|
||||
0, 0, 0.7071, 0, 0, -0.7071
|
||||
|
||||
0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5
|
||||
];
|
||||
|
||||
export const tetrahedronIndices: ReadonlyArray<number> = [
|
||||
4, 1, 0, 4, 2, 1, 4, 0, 2,
|
||||
0, 1, 3, 1, 2, 3, 2, 0, 3,
|
||||
2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1
|
||||
];
|
||||
|
||||
const tetrahedronEdges: ReadonlyArray<number> = [
|
||||
0, 1, 1, 2, 2, 0,
|
||||
0, 3, 1, 3, 2, 3,
|
||||
0, 4, 1, 4, 2, 4,
|
||||
]
|
||||
|
||||
let tetrahedron: Primitive
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { Vec3, Mat4, Mat3 } from '../mol-math/linear-algebra'
|
||||
import { NumberArray } from '../mol-util/type-helpers';
|
||||
import { arrayMax } from '../mol-util/array';
|
||||
|
||||
export function normalizeVec3Array<T extends NumberArray> (a: T) {
|
||||
const n = a.length
|
||||
for (let i = 0; i < n; i += 3) {
|
||||
const x = a[ i ]
|
||||
const y = a[ i + 1 ]
|
||||
const z = a[ i + 2 ]
|
||||
export function normalizeVec3Array<T extends NumberArray> (a: T, count: number) {
|
||||
for (let i = 0, il = count * 3; i < il; i += 3) {
|
||||
const x = a[i]
|
||||
const y = a[i + 1]
|
||||
const z = a[i + 2]
|
||||
const s = 1 / Math.sqrt(x * x + y * y + z * z)
|
||||
a[ i ] = x * s
|
||||
a[ i + 1 ] = y * s
|
||||
a[ i + 2 ] = z * s
|
||||
a[i] = x * s
|
||||
a[i + 1] = y * s
|
||||
a[i + 2] = z * s
|
||||
}
|
||||
}
|
||||
|
||||
export function getNormalMatrix(out: Mat3, t: Mat4) {
|
||||
Mat3.fromMat4(out, t)
|
||||
Mat3.invert(out, out)
|
||||
Mat3.transpose(out, out)
|
||||
return out
|
||||
return a
|
||||
}
|
||||
|
||||
const tmpV3 = Vec3.zero()
|
||||
@@ -45,38 +39,33 @@ export function transformDirectionArray (n: Mat3, array: NumberArray, offset: nu
|
||||
}
|
||||
}
|
||||
|
||||
export function setArrayZero(array: NumberArray) {
|
||||
const n = array.length
|
||||
for (let i = 0; i < n; ++i) array[i] = 0
|
||||
}
|
||||
|
||||
/** iterate over the entire buffer and apply the radius to each vertex */
|
||||
/** iterate over the entire array and apply the radius to each vertex */
|
||||
export function appplyRadius(vertices: NumberArray, radius: number) {
|
||||
const v = Vec3.zero()
|
||||
const n = vertices.length
|
||||
for (let i = 0; i < n; i += 3) {
|
||||
Vec3.fromArray(v, vertices, i)
|
||||
Vec3.normalize(v, v)
|
||||
Vec3.scale(v, v, radius)
|
||||
Vec3.toArray(v, vertices, i)
|
||||
for (let i = 0, il = vertices.length; i < il; i += 3) {
|
||||
Vec3.fromArray(tmpV3, vertices, i)
|
||||
Vec3.normalize(tmpV3, tmpV3)
|
||||
Vec3.scale(tmpV3, tmpV3, radius)
|
||||
Vec3.toArray(tmpV3, vertices, i)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* indexed vertex normals weighted by triangle areas http://www.iquilezles.org/www/articles/normals/normals.htm
|
||||
* normal array must contain only zeros
|
||||
*/
|
||||
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T) {
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const cb = Vec3.zero()
|
||||
const ab = Vec3.zero()
|
||||
const a = Vec3()
|
||||
const b = Vec3()
|
||||
const c = Vec3()
|
||||
const cb = Vec3()
|
||||
const ab = Vec3()
|
||||
|
||||
for (let i = 0, il = indices.length; i < il; i += 3) {
|
||||
const ai = indices[ i ] * 3
|
||||
const bi = indices[ i + 1 ] * 3
|
||||
const ci = indices[ i + 2 ] * 3
|
||||
/**
|
||||
* indexed vertex normals weighted by triangle areas
|
||||
* http://www.iquilezles.org/www/articles/normals/normals.htm
|
||||
* - normals array must contain only zeros
|
||||
*/
|
||||
export function computeIndexedVertexNormals<T extends NumberArray> (vertices: NumberArray, indices: NumberArray, normals: T, vertexCount: number, triangleCount: number) {
|
||||
|
||||
for (let i = 0, il = triangleCount * 3; i < il; i += 3) {
|
||||
const ai = indices[i] * 3
|
||||
const bi = indices[i + 1] * 3
|
||||
const ci = indices[i + 2] * 3
|
||||
|
||||
Vec3.fromArray(a, vertices, ai)
|
||||
Vec3.fromArray(b, vertices, bi)
|
||||
@@ -86,34 +75,28 @@ export function computeIndexedVertexNormals<T extends NumberArray> (vertices: Nu
|
||||
Vec3.sub(ab, a, b)
|
||||
Vec3.cross(cb, cb, ab)
|
||||
|
||||
normals[ ai ] += cb[ 0 ]
|
||||
normals[ ai + 1 ] += cb[ 1 ]
|
||||
normals[ ai + 2 ] += cb[ 2 ]
|
||||
normals[ai] += cb[0]
|
||||
normals[ai + 1] += cb[1]
|
||||
normals[ai + 2] += cb[2]
|
||||
|
||||
normals[ bi ] += cb[ 0 ]
|
||||
normals[ bi + 1 ] += cb[ 1 ]
|
||||
normals[ bi + 2 ] += cb[ 2 ]
|
||||
normals[bi] += cb[0]
|
||||
normals[bi + 1] += cb[1]
|
||||
normals[bi + 2] += cb[2]
|
||||
|
||||
normals[ ci ] += cb[ 0 ]
|
||||
normals[ ci + 1 ] += cb[ 1 ]
|
||||
normals[ ci + 2 ] += cb[ 2 ]
|
||||
normals[ci] += cb[0]
|
||||
normals[ci + 1] += cb[1]
|
||||
normals[ci + 2] += cb[2]
|
||||
}
|
||||
|
||||
normalizeVec3Array(normals)
|
||||
return normals
|
||||
return normalizeVec3Array(normals, vertexCount)
|
||||
}
|
||||
|
||||
/** vertex normals for unindexed triangle soup, normal array must contain only zeros */
|
||||
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T) {
|
||||
setArrayZero(normals)
|
||||
|
||||
const a = Vec3.zero()
|
||||
const b = Vec3.zero()
|
||||
const c = Vec3.zero()
|
||||
const cb = Vec3.zero()
|
||||
const ab = Vec3.zero()
|
||||
|
||||
for (let i = 0, il = vertices.length; i < il; i += 9) {
|
||||
/**
|
||||
* vertex normals for unindexed triangle soup
|
||||
* - normals array must contain only zeros
|
||||
*/
|
||||
export function computeVertexNormals<T extends NumberArray> (vertices: NumberArray, normals: T, vertexCount: number) {
|
||||
for (let i = 0, il = vertexCount * 3; i < il; i += 9) {
|
||||
Vec3.fromArray(a, vertices, i)
|
||||
Vec3.fromArray(b, vertices, i + 3)
|
||||
Vec3.fromArray(c, vertices, i + 6)
|
||||
@@ -122,19 +105,58 @@ export function computeVertexNormals<T extends NumberArray> (vertices: NumberArr
|
||||
Vec3.sub(ab, a, b)
|
||||
Vec3.cross(cb, cb, ab)
|
||||
|
||||
normals[ i ] = cb[ 0 ]
|
||||
normals[ i + 1 ] = cb[ 1 ]
|
||||
normals[ i + 2 ] = cb[ 2 ]
|
||||
normals[i] = cb[0]
|
||||
normals[i + 1] = cb[1]
|
||||
normals[i + 2] = cb[2]
|
||||
|
||||
normals[ i + 3 ] = cb[ 0 ]
|
||||
normals[ i + 4 ] = cb[ 1 ]
|
||||
normals[ i + 5 ] = cb[ 2 ]
|
||||
normals[i + 3] = cb[0]
|
||||
normals[i + 4] = cb[1]
|
||||
normals[i + 5] = cb[2]
|
||||
|
||||
normals[ i + 6 ] = cb[ 0 ]
|
||||
normals[ i + 7 ] = cb[ 1 ]
|
||||
normals[ i + 8 ] = cb[ 2 ]
|
||||
normals[i + 6] = cb[0]
|
||||
normals[i + 7] = cb[1]
|
||||
normals[i + 8] = cb[2]
|
||||
}
|
||||
|
||||
normalizeVec3Array(normals)
|
||||
return normals
|
||||
}
|
||||
return normalizeVec3Array(normals, vertexCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps groups to data, range for group i is offsets[i] to offsets[i + 1]
|
||||
*/
|
||||
export type GroupMapping = {
|
||||
/** data indices */
|
||||
readonly indices: ArrayLike<number>
|
||||
/** range for group i is offsets[i] to offsets[i + 1] */
|
||||
readonly offsets: ArrayLike<number>
|
||||
}
|
||||
|
||||
/**
|
||||
* The `step` parameter allows to skip over repeated values in `groups`
|
||||
*/
|
||||
export function createGroupMapping(groups: ArrayLike<number>, dataCount: number, step = 1): GroupMapping {
|
||||
const maxId = arrayMax(groups)
|
||||
|
||||
const offsets = new Int32Array(maxId + 2)
|
||||
const bucketFill = new Int32Array(dataCount)
|
||||
const bucketSizes = new Int32Array(dataCount)
|
||||
|
||||
for (let i = 0, il = dataCount * step; i < il; i += step) ++bucketSizes[groups[i]]
|
||||
|
||||
let offset = 0
|
||||
for (let i = 0; i < dataCount; i++) {
|
||||
offsets[i] = offset
|
||||
offset += bucketSizes[i]
|
||||
}
|
||||
offsets[dataCount] = offset
|
||||
|
||||
const indices = new Int32Array(offset)
|
||||
for (let i = 0, il = dataCount * step; i < il; i += step) {
|
||||
const g = groups[i]
|
||||
const og = offsets[g] + bucketFill[g]
|
||||
indices[og] = i
|
||||
++bucketFill[g]
|
||||
}
|
||||
|
||||
return { indices, offsets }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
@@ -7,13 +7,13 @@
|
||||
*/
|
||||
|
||||
import { ChunkedArray } from '../../../mol-data/util';
|
||||
import { ValueCell, noop } from '../../../mol-util';
|
||||
import { noop } from '../../../mol-util';
|
||||
import { Mesh } from '../../geometry/mesh/mesh';
|
||||
import { AllowedContours } from './tables';
|
||||
import { LinesBuilder } from '../../geometry/lines/lines-builder';
|
||||
import { Lines } from '../../geometry/lines/lines';
|
||||
|
||||
export interface MarchinCubesBuilder<T> {
|
||||
export interface MarchinCubesBuilder<T> {
|
||||
addVertex(x: number, y: number, z: number): number
|
||||
addNormal(x: number, y: number, z: number): void
|
||||
addGroup(group: number): void
|
||||
@@ -52,17 +52,7 @@ export function MarchinCubesMeshBuilder(vertexChunkSize: number, mesh?: Mesh): M
|
||||
const nb = ChunkedArray.compact(normals, true) as Float32Array;
|
||||
const ib = ChunkedArray.compact(indices, true) as Uint32Array;
|
||||
const gb = ChunkedArray.compact(groups, true) as Float32Array;
|
||||
|
||||
return {
|
||||
kind: 'mesh',
|
||||
vertexCount,
|
||||
triangleCount,
|
||||
vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
|
||||
groupBuffer: mesh ? ValueCell.update(mesh.groupBuffer, gb) : ValueCell.create(gb),
|
||||
indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
|
||||
normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
|
||||
normalsComputed: true
|
||||
}
|
||||
return Mesh.create(vb, ib, nb, gb, vertexCount, triangleCount, mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -65,8 +65,6 @@ function createPoints() {
|
||||
...transparency,
|
||||
|
||||
uAlpha: ValueCell.create(1.0),
|
||||
uHighlightColor: ValueCell.create(Vec3.create(1.0, 0.4, 0.6)),
|
||||
uSelectColor: ValueCell.create(Vec3.create(0.2, 1.0, 0.1)),
|
||||
uInstanceCount: ValueCell.create(1),
|
||||
uGroupCount: ValueCell.create(3),
|
||||
|
||||
@@ -83,7 +81,6 @@ function createPoints() {
|
||||
dPointSizeAttenuation: ValueCell.create(true),
|
||||
dPointFilledCircle: ValueCell.create(false),
|
||||
uPointEdgeBleach: ValueCell.create(0.5),
|
||||
dUseFog: ValueCell.create(true),
|
||||
}
|
||||
const state: RenderableState = {
|
||||
visible: true,
|
||||
@@ -104,11 +101,11 @@ describe('renderer', () => {
|
||||
expect(ctx.gl.canvas.width).toBe(32)
|
||||
expect(ctx.gl.canvas.height).toBe(32)
|
||||
|
||||
expect(ctx.stats.bufferCount).toBe(0);
|
||||
expect(ctx.stats.textureCount).toBe(0);
|
||||
expect(ctx.stats.vaoCount).toBe(0);
|
||||
expect(ctx.programCache.count).toBe(0);
|
||||
expect(ctx.shaderCache.count).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(0);
|
||||
|
||||
renderer.setViewport(0, 0, 64, 48)
|
||||
expect(ctx.gl.getParameter(ctx.gl.VIEWPORT)[2]).toBe(64)
|
||||
@@ -124,25 +121,23 @@ describe('renderer', () => {
|
||||
const points = createPoints()
|
||||
|
||||
scene.add(points)
|
||||
await scene.commit().run()
|
||||
expect(ctx.stats.bufferCount).toBe(4);
|
||||
expect(ctx.stats.textureCount).toBe(5);
|
||||
expect(ctx.stats.vaoCount).toBe(5);
|
||||
expect(ctx.programCache.count).toBe(5);
|
||||
expect(ctx.shaderCache.count).toBe(10);
|
||||
scene.commit()
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(4);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
|
||||
scene.remove(points)
|
||||
await scene.commit().run()
|
||||
expect(ctx.stats.bufferCount).toBe(0);
|
||||
expect(ctx.stats.textureCount).toBe(0);
|
||||
expect(ctx.stats.vaoCount).toBe(0);
|
||||
expect(ctx.programCache.count).toBe(5);
|
||||
expect(ctx.shaderCache.count).toBe(10);
|
||||
scene.commit()
|
||||
expect(ctx.stats.resourceCounts.attribute).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.texture).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.program).toBe(5);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(10);
|
||||
|
||||
ctx.programCache.dispose()
|
||||
expect(ctx.programCache.count).toBe(0);
|
||||
|
||||
ctx.shaderCache.clear()
|
||||
expect(ctx.shaderCache.count).toBe(0);
|
||||
ctx.resources.destroy()
|
||||
expect(ctx.stats.resourceCounts.program).toBe(0);
|
||||
expect(ctx.stats.resourceCounts.shader).toBe(0);
|
||||
})
|
||||
})
|
||||
55
src/mol-gl/commit-queue.ts
Normal file
55
src/mol-gl/commit-queue.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { LinkedList } from '../mol-data/generic'
|
||||
import { GraphicsRenderObject } from './render-object'
|
||||
|
||||
type N = LinkedList.Node<GraphicsRenderObject>
|
||||
|
||||
export class CommitQueue {
|
||||
private removeList = LinkedList<GraphicsRenderObject>();
|
||||
private removeMap = new Map<GraphicsRenderObject, N>();
|
||||
private addList = LinkedList<GraphicsRenderObject>();
|
||||
private addMap = new Map<GraphicsRenderObject, N>();
|
||||
|
||||
get isEmpty() {
|
||||
return this.removeList.count === 0 && this.addList.count === 0;
|
||||
}
|
||||
|
||||
add(o: GraphicsRenderObject) {
|
||||
if (this.removeMap.has(o)) {
|
||||
const a = this.removeMap.get(o)!;
|
||||
this.removeMap.delete(o);
|
||||
this.removeList.remove(a);
|
||||
}
|
||||
if (this.addMap.has(o)) return;
|
||||
const b = this.addList.addLast(o);
|
||||
this.addMap.set(o, b);
|
||||
}
|
||||
|
||||
remove(o: GraphicsRenderObject) {
|
||||
if (this.addMap.has(o)) {
|
||||
const a = this.addMap.get(o)!;
|
||||
this.addMap.delete(o);
|
||||
this.addList.remove(a);
|
||||
}
|
||||
if (this.removeMap.has(o)) return;
|
||||
const b = this.removeList.addLast(o);
|
||||
this.removeMap.set(o, b);
|
||||
}
|
||||
|
||||
tryGetRemove() {
|
||||
const o = this.removeList.removeFirst();
|
||||
if (o) this.removeMap.delete(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
tryGetAdd() {
|
||||
const o = this.addList.removeFirst();
|
||||
if (o) this.addMap.delete(o);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,13 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
|
||||
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { QuadSchema, QuadValues } from '../util';
|
||||
import { Vec2 } from '../../../mol-math/linear-algebra';
|
||||
import { getHistopyramidSum } from './sum';
|
||||
import { Framebuffer, createFramebuffer } from '../../../mol-gl/webgl/framebuffer';
|
||||
import { Framebuffer } from '../../../mol-gl/webgl/framebuffer';
|
||||
import { isPowerOfTwo } from '../../../mol-math/misc';
|
||||
import quad_vert from '../../../mol-gl/shader/quad.vert'
|
||||
import reduction_frag from '../../../mol-gl/shader/histogram-pyramid/reduction.frag'
|
||||
@@ -55,8 +55,8 @@ function getLevelTextureFramebuffer(ctx: WebGLContext, level: number) {
|
||||
let textureFramebuffer = LevelTexturesFramebuffers[level]
|
||||
const size = Math.pow(2, level)
|
||||
if (textureFramebuffer === undefined) {
|
||||
const texture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const framebuffer = createFramebuffer(ctx.gl, ctx.stats)
|
||||
const texture = ctx.resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
const framebuffer = ctx.resources.framebuffer()
|
||||
texture.attachFramebuffer(framebuffer, 0)
|
||||
textureFramebuffer = { texture, framebuffer }
|
||||
textureFramebuffer.texture.define(size, size)
|
||||
@@ -85,22 +85,22 @@ export interface HistogramPyramid {
|
||||
}
|
||||
|
||||
export function createHistogramPyramid(ctx: WebGLContext, inputTexture: Texture, scale: Vec2): HistogramPyramid {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
|
||||
// printTexture(ctx, inputTexture, 2)
|
||||
if (inputTexture.width !== inputTexture.height || !isPowerOfTwo(inputTexture.width)) {
|
||||
if (inputTexture.getWidth() !== inputTexture.getHeight() || !isPowerOfTwo(inputTexture.getWidth())) {
|
||||
throw new Error('inputTexture must be of square power-of-two size')
|
||||
}
|
||||
|
||||
// This part set the levels
|
||||
const levels = Math.ceil(Math.log(inputTexture.width) / Math.log(2))
|
||||
const levels = Math.ceil(Math.log(inputTexture.getWidth()) / Math.log(2))
|
||||
const maxSize = Math.pow(2, levels)
|
||||
// console.log('levels', levels, 'maxSize', maxSize)
|
||||
|
||||
const pyramidTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const pyramidTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
pyramidTexture.define(maxSize, maxSize)
|
||||
|
||||
const framebuffer = framebufferCache.get('reduction').value
|
||||
const framebuffer = resources.framebuffer()
|
||||
pyramidTexture.attachFramebuffer(framebuffer, 0)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable, ComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec } from '../../renderable/schema';
|
||||
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { decodeFloatRGB } from '../../../mol-util/float-packing';
|
||||
@@ -45,14 +45,11 @@ function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
|
||||
let SumTexture: Texture
|
||||
function getSumTexture(ctx: WebGLContext) {
|
||||
if (SumTexture) return SumTexture
|
||||
SumTexture = createTexture(ctx, 'image-uint8', 'rgba', 'ubyte', 'nearest')
|
||||
SumTexture = ctx.resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest')
|
||||
SumTexture.define(1, 1)
|
||||
return SumTexture
|
||||
}
|
||||
|
||||
/** name for shared framebuffer used for histogram-pyramid operations */
|
||||
const FramebufferName = 'histogram-pyramid-sum'
|
||||
|
||||
function setRenderingDefaults(ctx: WebGLContext) {
|
||||
const { gl, state } = ctx
|
||||
state.disable(gl.CULL_FACE)
|
||||
@@ -66,12 +63,12 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
|
||||
const sumArray = new Uint8Array(4)
|
||||
export function getHistopyramidSum(ctx: WebGLContext, pyramidTopTexture: Texture) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
|
||||
const renderable = getHistopyramidSumRenderable(ctx, pyramidTopTexture)
|
||||
ctx.state.currentRenderItemId = -1
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
const sumTexture = getSumTexture(ctx)
|
||||
sumTexture.attachFramebuffer(framebuffer, 0)
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
|
||||
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Vec3, Vec2 } from '../../../mol-math/linear-algebra';
|
||||
@@ -17,9 +17,6 @@ import { getTriCount } from './tables';
|
||||
import quad_vert from '../../../mol-gl/shader/quad.vert'
|
||||
import active_voxels_frag from '../../../mol-gl/shader/marching-cubes/active-voxels.frag'
|
||||
|
||||
/** name for shared framebuffer used for gpu marching cubes operations */
|
||||
const FramebufferName = 'marching-cubes-active-voxels'
|
||||
|
||||
const ActiveVoxelsSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
@@ -67,13 +64,14 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function calcActiveVoxels(ctx: WebGLContext, volumeData: Texture, gridDim: Vec3, gridTexDim: Vec3, isoValue: number, gridScale: Vec2) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { width, height } = volumeData
|
||||
const { gl, resources } = ctx
|
||||
const width = volumeData.getWidth()
|
||||
const height = volumeData.getHeight()
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
framebuffer.bind()
|
||||
|
||||
const activeVoxelsTex = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
const activeVoxelsTex = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
activeVoxelsTex.define(width, height)
|
||||
|
||||
const renderable = getActiveVoxelsRenderable(ctx, volumeData, gridDim, gridTexDim, isoValue, gridScale)
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createComputeRenderable } from '../../renderable'
|
||||
import { WebGLContext } from '../../webgl/context';
|
||||
import { createComputeRenderItem } from '../../webgl/render-item';
|
||||
import { Values, TextureSpec, UniformSpec } from '../../renderable/schema';
|
||||
import { Texture, createTexture } from '../../../mol-gl/webgl/texture';
|
||||
import { Texture } from '../../../mol-gl/webgl/texture';
|
||||
import { ShaderCode } from '../../../mol-gl/shader-code';
|
||||
import { ValueCell } from '../../../mol-util';
|
||||
import { Vec3, Vec2, Mat4 } from '../../../mol-math/linear-algebra';
|
||||
@@ -18,9 +18,6 @@ import { getTriIndices } from './tables';
|
||||
import quad_vert from '../../../mol-gl/shader/quad.vert'
|
||||
import isosurface_frag from '../../../mol-gl/shader/marching-cubes/isosurface.frag'
|
||||
|
||||
/** name for shared framebuffer used for gpu marching cubes operations */
|
||||
const FramebufferName = 'marching-cubes-isosurface'
|
||||
|
||||
const IsosurfaceSchema = {
|
||||
...QuadSchema,
|
||||
|
||||
@@ -83,30 +80,30 @@ function setRenderingDefaults(ctx: WebGLContext) {
|
||||
}
|
||||
|
||||
export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Texture, volumeData: Texture, histogramPyramid: HistogramPyramid, gridDim: Vec3, gridTexDim: Vec3, transform: Mat4, isoValue: number, vertexGroupTexture?: Texture, normalTexture?: Texture) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
const { gl, resources } = ctx
|
||||
const { pyramidTex, height, levels, scale, count } = histogramPyramid
|
||||
|
||||
// console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim)
|
||||
// console.log('iso volumeData', volumeData)
|
||||
|
||||
const framebuffer = framebufferCache.get(FramebufferName).value
|
||||
const framebuffer = resources.framebuffer()
|
||||
|
||||
let needsClear = false
|
||||
|
||||
if (!vertexGroupTexture) {
|
||||
vertexGroupTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
} else if (vertexGroupTexture.width !== pyramidTex.width || vertexGroupTexture.height !== pyramidTex.height) {
|
||||
vertexGroupTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
vertexGroupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else if (vertexGroupTexture.getWidth() !== pyramidTex.getWidth() || vertexGroupTexture.getHeight() !== pyramidTex.getHeight()) {
|
||||
vertexGroupTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else {
|
||||
needsClear = true
|
||||
}
|
||||
|
||||
if (!normalTexture) {
|
||||
normalTexture = createTexture(ctx, 'image-float32', 'rgba', 'float', 'nearest')
|
||||
normalTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
} else if (normalTexture.width !== pyramidTex.width || normalTexture.height !== pyramidTex.height) {
|
||||
normalTexture.define(pyramidTex.width, pyramidTex.height)
|
||||
normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest')
|
||||
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else if (normalTexture.getWidth() !== pyramidTex.getWidth() || normalTexture.getHeight() !== pyramidTex.getHeight()) {
|
||||
normalTexture.define(pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
} else {
|
||||
needsClear = true
|
||||
}
|
||||
@@ -150,7 +147,7 @@ export function createIsosurfaceBuffers(ctx: WebGLContext, activeVoxelsBase: Tex
|
||||
])
|
||||
|
||||
setRenderingDefaults(ctx)
|
||||
gl.viewport(0, 0, pyramidTex.width, pyramidTex.height)
|
||||
gl.viewport(0, 0, pyramidTex.getWidth(), pyramidTex.getHeight())
|
||||
if (needsClear) gl.clear(gl.COLOR_BUFFER_BIT)
|
||||
renderable.render()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
@@ -13,7 +13,7 @@ import { Vec2 } from '../../mol-math/linear-algebra';
|
||||
import { GLRenderingContext } from '../../mol-gl/webgl/compat';
|
||||
|
||||
export const QuadPositions = new Float32Array([
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // First triangle
|
||||
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0 // Second triangle
|
||||
])
|
||||
|
||||
@@ -42,11 +42,11 @@ function getArrayForTexture(gl: GLRenderingContext, texture: Texture, size: numb
|
||||
}
|
||||
|
||||
export function readTexture(ctx: WebGLContext, texture: Texture, width?: number, height?: number) {
|
||||
const { gl, framebufferCache } = ctx
|
||||
width = defaults(width, texture.width)
|
||||
height = defaults(height, texture.height)
|
||||
const { gl, resources } = ctx
|
||||
width = defaults(width, texture.getWidth())
|
||||
height = defaults(height, texture.getHeight())
|
||||
const size = width * height * 4
|
||||
const framebuffer = framebufferCache.get('read-texture').value
|
||||
const framebuffer = resources.framebuffer()
|
||||
const array = getArrayForTexture(gl, texture, size)
|
||||
framebuffer.bind()
|
||||
texture.attachFramebuffer(framebuffer, 0)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
*/
|
||||
|
||||
import { RenderableState, Renderable } from './renderable'
|
||||
import { RenderableValues } from './renderable/schema';
|
||||
import { idFactory } from '../mol-util/id-factory';
|
||||
import { WebGLContext } from './webgl/context';
|
||||
import { DirectVolumeValues, DirectVolumeRenderable } from './renderable/direct-volume';
|
||||
@@ -20,53 +19,40 @@ const getNextId = idFactory(0, 0x7FFFFFFF)
|
||||
|
||||
export const getNextMaterialId = idFactory(0, 0x7FFFFFFF)
|
||||
|
||||
export interface BaseRenderObject<T extends RenderableValues> { id: number, type: string, values: T, state: RenderableState, materialId: number }
|
||||
export interface MeshRenderObject extends BaseRenderObject<MeshValues> { type: 'mesh' }
|
||||
export interface PointsRenderObject extends BaseRenderObject<PointsValues> { type: 'points' }
|
||||
export interface SpheresRenderObject extends BaseRenderObject<SpheresValues> { type: 'spheres' }
|
||||
export interface TextRenderObject extends BaseRenderObject<TextValues> { type: 'text' }
|
||||
export interface LinesRenderObject extends BaseRenderObject<LinesValues> { type: 'lines' }
|
||||
export interface DirectVolumeRenderObject extends BaseRenderObject<DirectVolumeValues> { type: 'direct-volume' }
|
||||
export interface TextureMeshRenderObject extends BaseRenderObject<TextureMeshValues> { type: 'texture-mesh' }
|
||||
export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectType> {
|
||||
readonly id: number,
|
||||
readonly type: T,
|
||||
readonly values: RenderObjectValues<T>,
|
||||
readonly state: RenderableState,
|
||||
readonly materialId: number
|
||||
}
|
||||
|
||||
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'texture-mesh'
|
||||
|
||||
export type RenderObjectValues<T extends RenderObjectType> =
|
||||
T extends 'mesh' ? MeshValues :
|
||||
T extends 'points' ? PointsValues :
|
||||
T extends 'spheres' ? SpheresValues :
|
||||
T extends 'text' ? TextValues :
|
||||
T extends 'lines' ? LinesValues :
|
||||
T extends 'direct-volume' ? DirectVolumeValues :
|
||||
T extends 'texture-mesh' ? TextureMeshValues : never
|
||||
|
||||
//
|
||||
|
||||
export type GraphicsRenderObject = MeshRenderObject | PointsRenderObject | SpheresRenderObject | TextRenderObject | LinesRenderObject | DirectVolumeRenderObject | TextureMeshRenderObject
|
||||
|
||||
export type RenderObjectKindType = {
|
||||
'mesh': MeshRenderObject
|
||||
'points': PointsRenderObject
|
||||
'spheres': SpheresRenderObject
|
||||
'text': TextRenderObject
|
||||
'lines': LinesRenderObject
|
||||
'direct-volume': DirectVolumeRenderObject
|
||||
'texture-mesh': TextureMeshRenderObject
|
||||
}
|
||||
export type RenderObjectValuesType = {
|
||||
'mesh': MeshValues
|
||||
'points': PointsValues
|
||||
'spheres': SpheresValues
|
||||
'text': TextValues
|
||||
'lines': LinesValues
|
||||
'direct-volume': DirectVolumeValues
|
||||
'texture-mesh': TextureMeshValues
|
||||
}
|
||||
export type RenderObjectType = keyof RenderObjectKindType
|
||||
|
||||
//
|
||||
|
||||
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValuesType[T], state: RenderableState, materialId: number): RenderObjectKindType[T] {
|
||||
return { id: getNextId(), type, values, state, materialId } as RenderObjectKindType[T]
|
||||
export function createRenderObject<T extends RenderObjectType>(type: T, values: RenderObjectValues<T>, state: RenderableState, materialId: number): GraphicsRenderObject<T> {
|
||||
return { id: getNextId(), type, values, state, materialId } as GraphicsRenderObject<T>
|
||||
}
|
||||
|
||||
export function createRenderable(ctx: WebGLContext, o: GraphicsRenderObject): Renderable<any> {
|
||||
export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>): Renderable<any> {
|
||||
switch (o.type) {
|
||||
case 'mesh': return MeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'points': return PointsRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'spheres': return SpheresRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'text': return TextRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'lines': return LinesRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values, o.state, o.materialId)
|
||||
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId)
|
||||
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId)
|
||||
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId)
|
||||
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId)
|
||||
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId)
|
||||
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId)
|
||||
case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId)
|
||||
}
|
||||
}
|
||||
throw new Error('unsupported type')
|
||||
}
|
||||
|
||||
@@ -51,9 +51,6 @@ export const DirectVolumeSchema = {
|
||||
elements: ElementsSpec('uint32'),
|
||||
|
||||
uAlpha: UniformSpec('f'),
|
||||
uHighlightColor: UniformSpec('v3'),
|
||||
uSelectColor: UniformSpec('v3'),
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
|
||||
uIsoValue: UniformSpec('f'),
|
||||
uBboxMin: UniformSpec('v3'),
|
||||
|
||||
@@ -19,7 +19,8 @@ export const MeshSchema = {
|
||||
dFlatShaded: DefineSpec('boolean'),
|
||||
dDoubleSided: DefineSpec('boolean'),
|
||||
dFlipSided: DefineSpec('boolean'),
|
||||
}
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
} as const
|
||||
export type MeshSchema = typeof MeshSchema
|
||||
export type MeshValues = Values<MeshSchema>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export type KindValue = {
|
||||
'sphere': Sphere3D
|
||||
}
|
||||
|
||||
export type Values<S extends RenderableSchema> = { [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
|
||||
export type Values<S extends RenderableSchema> = { readonly [k in keyof S]: ValueCell<KindValue[S[k]['kind']]> }
|
||||
|
||||
export function splitValues(schema: RenderableSchema, values: RenderableValues) {
|
||||
const attributeValues: AttributeValues = {}
|
||||
@@ -128,12 +128,12 @@ export function ValueSpec<K extends ValueKind>(kind: K): ValueSpec<K> {
|
||||
//
|
||||
|
||||
export type RenderableSchema = {
|
||||
[k: string]: (
|
||||
readonly [k: string]: (
|
||||
AttributeSpec<AttributeKind> | UniformSpec<UniformKind> | TextureSpec<TextureKind> |
|
||||
ValueSpec<ValueKind> | DefineSpec<DefineKind> | ElementsSpec<ElementsKind>
|
||||
)
|
||||
}
|
||||
export type RenderableValues = { [k: string]: ValueCell<any> }
|
||||
export type RenderableValues = { readonly [k: string]: ValueCell<any> }
|
||||
|
||||
//
|
||||
|
||||
@@ -148,13 +148,6 @@ export const GlobalUniformSchema = {
|
||||
uModelViewProjection: UniformSpec('m4'),
|
||||
uInvModelViewProjection: UniformSpec('m4'),
|
||||
|
||||
uLightIntensity: UniformSpec('f'),
|
||||
uAmbientIntensity: UniformSpec('f'),
|
||||
|
||||
uMetalness: UniformSpec('f'),
|
||||
uRoughness: UniformSpec('f'),
|
||||
uReflectivity: UniformSpec('f'),
|
||||
|
||||
uIsOrtho: UniformSpec('f'),
|
||||
uPixelRatio: UniformSpec('f'),
|
||||
uViewportHeight: UniformSpec('f'),
|
||||
@@ -169,16 +162,33 @@ export const GlobalUniformSchema = {
|
||||
uFogColor: UniformSpec('v3'),
|
||||
|
||||
uTransparentBackground: UniformSpec('i'),
|
||||
|
||||
// all the following could in principle be per object
|
||||
// as a kind of 'material' parameter set
|
||||
// would need to test performance implications
|
||||
uLightIntensity: UniformSpec('f'),
|
||||
uAmbientIntensity: UniformSpec('f'),
|
||||
|
||||
uMetalness: UniformSpec('f'),
|
||||
uRoughness: UniformSpec('f'),
|
||||
uReflectivity: UniformSpec('f'),
|
||||
|
||||
uPickingAlphaThreshold: UniformSpec('f'),
|
||||
|
||||
uInteriorDarkening: UniformSpec('f'),
|
||||
}
|
||||
uInteriorColorFlag: UniformSpec('i'),
|
||||
uInteriorColor: UniformSpec('v3'),
|
||||
|
||||
uHighlightColor: UniformSpec('v3'),
|
||||
uSelectColor: UniformSpec('v3'),
|
||||
} as const
|
||||
export type GlobalUniformSchema = typeof GlobalUniformSchema
|
||||
export type GlobalUniformValues = Values<GlobalUniformSchema> // { [k in keyof GlobalUniformSchema]: ValueCell<any> }
|
||||
|
||||
export const InternalSchema = {
|
||||
uObjectId: UniformSpec('i'),
|
||||
uPickable: UniformSpec('i', true),
|
||||
}
|
||||
} as const
|
||||
export type InternalSchema = typeof InternalSchema
|
||||
export type InternalValues = { [k in keyof InternalSchema]: ValueCell<any> }
|
||||
|
||||
@@ -188,7 +198,7 @@ export const ColorSchema = {
|
||||
uColorTexDim: UniformSpec('v2'),
|
||||
tColor: TextureSpec('image-uint8', 'rgb', 'ubyte', 'nearest'),
|
||||
dColorType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
|
||||
}
|
||||
} as const
|
||||
export type ColorSchema = typeof ColorSchema
|
||||
export type ColorValues = Values<ColorSchema>
|
||||
|
||||
@@ -199,14 +209,14 @@ export const SizeSchema = {
|
||||
tSize: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
dSizeType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']),
|
||||
uSizeFactor: UniformSpec('f'),
|
||||
}
|
||||
} as const
|
||||
export type SizeSchema = typeof SizeSchema
|
||||
export type SizeValues = Values<SizeSchema>
|
||||
|
||||
export const MarkerSchema = {
|
||||
uMarkerTexDim: UniformSpec('v2'),
|
||||
tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
|
||||
}
|
||||
} as const
|
||||
export type MarkerSchema = typeof MarkerSchema
|
||||
export type MarkerValues = Values<MarkerSchema>
|
||||
|
||||
@@ -214,7 +224,7 @@ export const OverpaintSchema = {
|
||||
uOverpaintTexDim: UniformSpec('v2'),
|
||||
tOverpaint: TextureSpec('image-uint8', 'rgba', 'ubyte', 'nearest'),
|
||||
dOverpaint: DefineSpec('boolean'),
|
||||
}
|
||||
} as const
|
||||
export type OverpaintSchema = typeof OverpaintSchema
|
||||
export type OverpaintValues = Values<OverpaintSchema>
|
||||
|
||||
@@ -225,7 +235,7 @@ export const TransparencySchema = {
|
||||
dTransparency: DefineSpec('boolean'),
|
||||
// dTransparencyType: DefineSpec('string', ['uniform', 'attribute', 'instance', 'group', 'group_instance']), // TODO
|
||||
dTransparencyVariant: DefineSpec('string', ['single', 'multi']),
|
||||
}
|
||||
} as const
|
||||
export type TransparencySchema = typeof TransparencySchema
|
||||
export type TransparencyValues = Values<TransparencySchema>
|
||||
|
||||
@@ -250,9 +260,6 @@ export const BaseSchema = {
|
||||
uInstanceCount: UniformSpec('i'),
|
||||
uGroupCount: UniformSpec('i'),
|
||||
|
||||
uHighlightColor: UniformSpec('v3', true),
|
||||
uSelectColor: UniformSpec('v3', true),
|
||||
|
||||
drawCount: ValueSpec('number'),
|
||||
instanceCount: ValueSpec('number'),
|
||||
|
||||
@@ -270,8 +277,6 @@ export const BaseSchema = {
|
||||
boundingSphere: ValueSpec('sphere'),
|
||||
/** bounding sphere NOT taking aTransform into account */
|
||||
invariantBoundingSphere: ValueSpec('sphere'),
|
||||
|
||||
dUseFog: DefineSpec('boolean'),
|
||||
}
|
||||
} as const
|
||||
export type BaseSchema = typeof BaseSchema
|
||||
export type BaseValues = Values<BaseSchema>
|
||||
@@ -20,6 +20,7 @@ export const SpheresSchema = {
|
||||
|
||||
padding: ValueSpec('number'),
|
||||
dDoubleSided: DefineSpec('boolean'),
|
||||
dIgnoreLight: DefineSpec('boolean'),
|
||||
}
|
||||
export type SpheresSchema = typeof SpheresSchema
|
||||
export type SpheresValues = Values<SpheresSchema>
|
||||
|
||||
@@ -22,11 +22,12 @@ export interface RendererStats {
|
||||
programCount: number
|
||||
shaderCount: number
|
||||
|
||||
bufferCount: number
|
||||
attributeCount: number
|
||||
elementsCount: number
|
||||
framebufferCount: number
|
||||
renderbufferCount: number
|
||||
textureCount: number
|
||||
vaoCount: number
|
||||
vertexArrayCount: number
|
||||
|
||||
drawCount: number
|
||||
instanceCount: number
|
||||
@@ -37,8 +38,8 @@ interface Renderer {
|
||||
readonly stats: RendererStats
|
||||
readonly props: Readonly<RendererProps>
|
||||
|
||||
clear: () => void
|
||||
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
|
||||
clear: (transparentBackground: boolean) => void
|
||||
render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => void
|
||||
setProps: (props: Partial<RendererProps>) => void
|
||||
setViewport: (x: number, y: number, width: number, height: number) => void
|
||||
dispose: () => void
|
||||
@@ -46,9 +47,13 @@ interface Renderer {
|
||||
|
||||
export const RendererParams = {
|
||||
backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
|
||||
transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
|
||||
|
||||
// the following are general 'material' parameters
|
||||
pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
|
||||
|
||||
interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
interiorColorFlag: PD.Boolean(true, { label: 'Use Interior Color' }),
|
||||
interiorColor: PD.Color(Color.fromNormalizedRgb(0.3, 0.3, 0.3)),
|
||||
|
||||
lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
@@ -56,6 +61,9 @@ export const RendererParams = {
|
||||
metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
|
||||
|
||||
highlightColor: PD.Color(Color.fromNormalizedRgb(1.0, 0.4, 0.6)),
|
||||
selectColor: PD.Color(Color.fromNormalizedRgb(0.2, 1.0, 0.1)),
|
||||
}
|
||||
export type RendererProps = PD.Values<typeof RendererParams>
|
||||
|
||||
@@ -95,6 +103,15 @@ namespace Renderer {
|
||||
uViewportHeight: ValueCell.create(viewport.height),
|
||||
uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
|
||||
|
||||
uCameraPosition: ValueCell.create(Vec3()),
|
||||
uNear: ValueCell.create(1),
|
||||
uFar: ValueCell.create(10000),
|
||||
uFogNear: ValueCell.create(1),
|
||||
uFogFar: ValueCell.create(10000),
|
||||
uFogColor: ValueCell.create(bgColor),
|
||||
uTransparentBackground: ValueCell.create(0),
|
||||
|
||||
// the following are general 'material' uniforms
|
||||
uLightIntensity: ValueCell.create(p.lightIntensity),
|
||||
uAmbientIntensity: ValueCell.create(p.ambientIntensity),
|
||||
|
||||
@@ -102,16 +119,14 @@ namespace Renderer {
|
||||
uRoughness: ValueCell.create(p.roughness),
|
||||
uReflectivity: ValueCell.create(p.reflectivity),
|
||||
|
||||
uCameraPosition: ValueCell.create(Vec3()),
|
||||
uNear: ValueCell.create(1),
|
||||
uFar: ValueCell.create(10000),
|
||||
uFogNear: ValueCell.create(1),
|
||||
uFogFar: ValueCell.create(10000),
|
||||
uFogColor: ValueCell.create(bgColor),
|
||||
|
||||
uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
|
||||
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
|
||||
|
||||
uInteriorDarkening: ValueCell.create(p.interiorDarkening),
|
||||
uInteriorColorFlag: ValueCell.create(p.interiorColorFlag ? 1 : 0),
|
||||
uInteriorColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.interiorColor)),
|
||||
|
||||
uHighlightColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.highlightColor)),
|
||||
uSelectColor: ValueCell.create(Color.toVec3Normalized(Vec3(), p.selectColor)),
|
||||
}
|
||||
const globalUniformList = Object.entries(globalUniforms)
|
||||
|
||||
@@ -161,7 +176,7 @@ namespace Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
|
||||
const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean, transparentBackground: boolean) => {
|
||||
ValueCell.update(globalUniforms.uModel, scene.view)
|
||||
ValueCell.update(globalUniforms.uView, camera.view)
|
||||
ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
|
||||
@@ -181,6 +196,8 @@ namespace Renderer {
|
||||
ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
|
||||
ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
|
||||
|
||||
ValueCell.update(globalUniforms.uTransparentBackground, transparentBackground ? 1 : 0)
|
||||
|
||||
globalUniformsNeedUpdate = true
|
||||
state.currentRenderItemId = -1
|
||||
|
||||
@@ -194,7 +211,7 @@ namespace Renderer {
|
||||
|
||||
if (clear) {
|
||||
if (variant === 'color') {
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
|
||||
} else {
|
||||
state.clearColor(1, 1, 1, 1)
|
||||
}
|
||||
@@ -211,8 +228,10 @@ namespace Renderer {
|
||||
state.enable(gl.BLEND)
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
const r = renderables[i]
|
||||
state.depthMask(r.values.uAlpha.ref.value === 1.0)
|
||||
if (!r.state.opaque) renderObject(r, variant)
|
||||
if (!r.state.opaque) {
|
||||
state.depthMask(false)
|
||||
renderObject(r, variant)
|
||||
}
|
||||
}
|
||||
} else { // picking & depth
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
@@ -224,32 +243,39 @@ namespace Renderer {
|
||||
}
|
||||
|
||||
return {
|
||||
clear: () => {
|
||||
clear: (transparentBackground: boolean) => {
|
||||
state.depthMask(true)
|
||||
state.colorMask(true, true, true, true)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
|
||||
state.clearColor(bgColor[0], bgColor[1], bgColor[2], transparentBackground ? 0 : 1)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
},
|
||||
render,
|
||||
|
||||
setProps: (props: Partial<RendererProps>) => {
|
||||
if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
|
||||
p.pickingAlphaThreshold = props.pickingAlphaThreshold
|
||||
ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold)
|
||||
}
|
||||
if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
|
||||
p.interiorDarkening = props.interiorDarkening
|
||||
ValueCell.update(globalUniforms.uInteriorDarkening, p.interiorDarkening)
|
||||
}
|
||||
if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
|
||||
p.backgroundColor = props.backgroundColor
|
||||
Color.toVec3Normalized(bgColor, p.backgroundColor)
|
||||
ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
|
||||
}
|
||||
if (props.transparentBackground !== undefined && props.transparentBackground !== p.transparentBackground) {
|
||||
p.transparentBackground = props.transparentBackground
|
||||
ValueCell.update(globalUniforms.uTransparentBackground, p.transparentBackground ? 1 : 0)
|
||||
|
||||
if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
|
||||
p.pickingAlphaThreshold = props.pickingAlphaThreshold
|
||||
ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold)
|
||||
}
|
||||
|
||||
if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
|
||||
p.interiorDarkening = props.interiorDarkening
|
||||
ValueCell.update(globalUniforms.uInteriorDarkening, p.interiorDarkening)
|
||||
}
|
||||
if (props.interiorColorFlag !== undefined && props.interiorColorFlag !== p.interiorColorFlag) {
|
||||
p.interiorColorFlag = props.interiorColorFlag
|
||||
ValueCell.update(globalUniforms.uInteriorColorFlag, p.interiorColorFlag ? 1 : 0)
|
||||
}
|
||||
if (props.interiorColor !== undefined && props.interiorColor !== p.interiorColor) {
|
||||
p.interiorColor = props.interiorColor
|
||||
ValueCell.update(globalUniforms.uInteriorColor, Color.toVec3Normalized(globalUniforms.uInteriorColor.ref.value, p.interiorColor))
|
||||
}
|
||||
|
||||
if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
|
||||
p.lightIntensity = props.lightIntensity
|
||||
ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
|
||||
@@ -271,6 +297,15 @@ namespace Renderer {
|
||||
p.reflectivity = props.reflectivity
|
||||
ValueCell.update(globalUniforms.uReflectivity, p.reflectivity)
|
||||
}
|
||||
|
||||
if (props.highlightColor !== undefined && props.highlightColor !== p.highlightColor) {
|
||||
p.highlightColor = props.highlightColor
|
||||
ValueCell.update(globalUniforms.uHighlightColor, Color.toVec3Normalized(globalUniforms.uHighlightColor.ref.value, p.highlightColor))
|
||||
}
|
||||
if (props.selectColor !== undefined && props.selectColor !== p.selectColor) {
|
||||
p.selectColor = props.selectColor
|
||||
ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor))
|
||||
}
|
||||
},
|
||||
setViewport: (x: number, y: number, width: number, height: number) => {
|
||||
gl.viewport(x, y, width, height)
|
||||
@@ -286,14 +321,15 @@ namespace Renderer {
|
||||
},
|
||||
get stats(): RendererStats {
|
||||
return {
|
||||
programCount: ctx.programCache.count,
|
||||
shaderCount: ctx.shaderCache.count,
|
||||
programCount: ctx.stats.resourceCounts.program,
|
||||
shaderCount: ctx.stats.resourceCounts.shader,
|
||||
|
||||
bufferCount: stats.bufferCount,
|
||||
framebufferCount: stats.framebufferCount,
|
||||
renderbufferCount: stats.renderbufferCount,
|
||||
textureCount: stats.textureCount,
|
||||
vaoCount: stats.vaoCount,
|
||||
attributeCount: ctx.stats.resourceCounts.attribute,
|
||||
elementsCount: ctx.stats.resourceCounts.elements,
|
||||
framebufferCount: ctx.stats.resourceCounts.framebuffer,
|
||||
renderbufferCount: ctx.stats.resourceCounts.renderbuffer,
|
||||
textureCount: ctx.stats.resourceCounts.texture,
|
||||
vertexArrayCount: ctx.stats.resourceCounts.vertexArray,
|
||||
|
||||
drawCount: stats.drawCount,
|
||||
instanceCount: stats.instanceCount,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
|
||||
*
|
||||
* @author Alexander Rose <alexander.rose@weirdbyte.de>
|
||||
* @author David Sehnal <david.sehnal@gmail.com>
|
||||
*/
|
||||
|
||||
import { Renderable } from './renderable'
|
||||
@@ -12,8 +13,9 @@ import { Object3D } from './object3d';
|
||||
import { Sphere3D } from '../mol-math/geometry';
|
||||
import { Vec3 } from '../mol-math/linear-algebra';
|
||||
import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
|
||||
import { RuntimeContext, Task } from '../mol-task';
|
||||
import { AsyncQueue } from '../mol-util/async-queue';
|
||||
import { CommitQueue } from './commit-queue';
|
||||
import { now } from '../mol-util/now';
|
||||
import { arraySetRemove } from '../mol-util/array';
|
||||
|
||||
const boundaryHelper = new BoundaryHelper();
|
||||
function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D {
|
||||
@@ -56,13 +58,12 @@ interface Scene extends Object3D {
|
||||
readonly count: number
|
||||
readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>>
|
||||
readonly boundingSphere: Sphere3D
|
||||
readonly isCommiting: boolean
|
||||
|
||||
update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean) => void
|
||||
update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean, isRemoving?: boolean) => void
|
||||
add: (o: GraphicsRenderObject) => void // Renderable<any>
|
||||
remove: (o: GraphicsRenderObject) => void
|
||||
syncCommit: () => void
|
||||
commit: () => Task<void>
|
||||
commit: (maxTimeMs?: number) => boolean
|
||||
readonly needsCommit: boolean
|
||||
has: (o: GraphicsRenderObject) => boolean
|
||||
clear: () => void
|
||||
forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void
|
||||
@@ -78,7 +79,7 @@ namespace Scene {
|
||||
|
||||
const object3d = Object3D.create()
|
||||
|
||||
const add = (o: GraphicsRenderObject) => {
|
||||
function add(o: GraphicsRenderObject) {
|
||||
if (!renderableMap.has(o)) {
|
||||
const renderable = createRenderable(ctx, o)
|
||||
renderables.push(renderable)
|
||||
@@ -91,47 +92,52 @@ namespace Scene {
|
||||
}
|
||||
}
|
||||
|
||||
const remove = (o: GraphicsRenderObject) => {
|
||||
function remove(o: GraphicsRenderObject) {
|
||||
const renderable = renderableMap.get(o)
|
||||
if (renderable) {
|
||||
renderable.dispose()
|
||||
renderables.splice(renderables.indexOf(renderable), 1)
|
||||
arraySetRemove(renderables, renderable);
|
||||
renderableMap.delete(o)
|
||||
boundingSphereDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
const commitQueue = new AsyncQueue<any>();
|
||||
const toAdd: GraphicsRenderObject[] = []
|
||||
const toRemove: GraphicsRenderObject[] = []
|
||||
const commitBulkSize = 100;
|
||||
function commit(maxTimeMs: number) {
|
||||
const start = now();
|
||||
|
||||
type CommitParams = { toAdd: GraphicsRenderObject[], toRemove: GraphicsRenderObject[] }
|
||||
let i = 0;
|
||||
|
||||
const step = 100
|
||||
const handle = async (ctx: RuntimeContext, arr: GraphicsRenderObject[], fn: (o: GraphicsRenderObject) => void, message: string) => {
|
||||
for (let i = 0, il = arr.length; i < il; i += step) {
|
||||
if (ctx.shouldUpdate) await ctx.update({ message, current: i, max: il })
|
||||
for (let j = i, jl = Math.min(i + step, il); j < jl; ++j) {
|
||||
fn(arr[j])
|
||||
}
|
||||
while (true) {
|
||||
const o = commitQueue.tryGetRemove();
|
||||
if (!o) break;
|
||||
remove(o);
|
||||
if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const o = commitQueue.tryGetAdd();
|
||||
if (!o) break;
|
||||
add(o);
|
||||
if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false;
|
||||
}
|
||||
|
||||
renderables.sort(renderableSort)
|
||||
return true;
|
||||
}
|
||||
|
||||
const commit = async (ctx: RuntimeContext, p: CommitParams) => {
|
||||
await handle(ctx, p.toRemove, remove, 'Removing GraphicsRenderObjects')
|
||||
await handle(ctx, p.toAdd, add, 'Adding GraphicsRenderObjects')
|
||||
if (ctx.shouldUpdate) await ctx.update({ message: 'Sorting GraphicsRenderObjects' })
|
||||
renderables.sort(renderableSort)
|
||||
}
|
||||
// const toAdd: GraphicsRenderObject[] = []
|
||||
// const toRemove: GraphicsRenderObject[] = []
|
||||
const commitQueue = new CommitQueue();
|
||||
|
||||
return {
|
||||
get view () { return object3d.view },
|
||||
get position () { return object3d.position },
|
||||
get direction () { return object3d.direction },
|
||||
get up () { return object3d.up },
|
||||
get isCommiting () { return commitQueue.length > 0 },
|
||||
// get isCommiting () { return commitQueue.length > 0 },
|
||||
|
||||
update(objects, keepBoundingSphere) {
|
||||
update(objects, keepBoundingSphere, isRemoving) {
|
||||
Object3D.update(object3d)
|
||||
if (objects) {
|
||||
for (let i = 0, il = objects.length; i < il; ++i) {
|
||||
@@ -139,44 +145,17 @@ namespace Scene {
|
||||
if (!o) continue;
|
||||
o.update();
|
||||
}
|
||||
} else {
|
||||
} else if (!isRemoving) {
|
||||
for (let i = 0, il = renderables.length; i < il; ++i) {
|
||||
renderables[i].update()
|
||||
}
|
||||
}
|
||||
if (!keepBoundingSphere) boundingSphereDirty = true
|
||||
},
|
||||
add: (o: GraphicsRenderObject) => {
|
||||
toAdd.push(o)
|
||||
},
|
||||
remove: (o: GraphicsRenderObject) => {
|
||||
toRemove.push(o)
|
||||
},
|
||||
syncCommit: () => {
|
||||
for (let i = 0, il = toRemove.length; i < il; ++i) remove(toRemove[i])
|
||||
toRemove.length = 0
|
||||
for (let i = 0, il = toAdd.length; i < il; ++i) add(toAdd[i])
|
||||
toAdd.length = 0
|
||||
renderables.sort(renderableSort)
|
||||
},
|
||||
commit: () => {
|
||||
const params = { toAdd: [ ...toAdd ], toRemove: [ ...toRemove ] }
|
||||
toAdd.length = 0
|
||||
toRemove.length = 0
|
||||
|
||||
return Task.create('Commiting GraphicsRenderObjects', async ctx => {
|
||||
const removed = await commitQueue.enqueue(params);
|
||||
if (!removed) return;
|
||||
|
||||
try {
|
||||
await commit(ctx, params);
|
||||
} finally {
|
||||
commitQueue.handled(params);
|
||||
}
|
||||
}, () => {
|
||||
commitQueue.remove(params);
|
||||
})
|
||||
},
|
||||
add: (o: GraphicsRenderObject) => commitQueue.add(o),
|
||||
remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
|
||||
commit: (maxTime = Number.MAX_VALUE) => commit(maxTime),
|
||||
get needsCommit() { return !commitQueue.isEmpty; },
|
||||
has: (o: GraphicsRenderObject) => {
|
||||
return renderableMap.has(o)
|
||||
},
|
||||
|
||||
@@ -30,6 +30,7 @@ export interface ShaderCode {
|
||||
}
|
||||
|
||||
import apply_fog from './shader/chunks/apply-fog.glsl'
|
||||
import apply_interior_color from './shader/chunks/apply-interior-color.glsl'
|
||||
import apply_light_color from './shader/chunks/apply-light-color.glsl'
|
||||
import apply_marker_color from './shader/chunks/apply-marker-color.glsl'
|
||||
import assign_color_varying from './shader/chunks/assign-color-varying.glsl'
|
||||
@@ -39,6 +40,7 @@ import assign_material_color from './shader/chunks/assign-material-color.glsl'
|
||||
import assign_normal from './shader/chunks/assign-normal.glsl'
|
||||
import assign_position from './shader/chunks/assign-position.glsl'
|
||||
import assign_size from './shader/chunks/assign-size.glsl'
|
||||
import check_picking_alpha from './shader/chunks/check-picking-alpha.glsl'
|
||||
import color_frag_params from './shader/chunks/color-frag-params.glsl'
|
||||
import color_vert_params from './shader/chunks/color-vert-params.glsl'
|
||||
import common_frag_params from './shader/chunks/common-frag-params.glsl'
|
||||
@@ -54,6 +56,7 @@ import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest
|
||||
|
||||
const ShaderChunks: { [k: string]: string } = {
|
||||
apply_fog,
|
||||
apply_interior_color,
|
||||
apply_light_color,
|
||||
apply_marker_color,
|
||||
assign_color_varying,
|
||||
@@ -63,6 +66,7 @@ const ShaderChunks: { [k: string]: string } = {
|
||||
assign_normal,
|
||||
assign_position,
|
||||
assign_size,
|
||||
check_picking_alpha,
|
||||
color_frag_params,
|
||||
color_vert_params,
|
||||
common_frag_params,
|
||||
@@ -197,13 +201,17 @@ const glsl300FragPrefixCommon = `
|
||||
#define gl_FragColor out_FragData0
|
||||
#define gl_FragDepthEXT gl_FragDepth
|
||||
|
||||
#define enabledStandardDerivatives
|
||||
#define enabledFragDepth
|
||||
#define requiredDrawBuffers
|
||||
`
|
||||
|
||||
function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
|
||||
const prefix = [ '#version 300 es' ]
|
||||
if (shaderExtensions.standardDerivatives) {
|
||||
prefix.push('#define enabledStandardDerivatives')
|
||||
}
|
||||
if (shaderExtensions.fragDepth) {
|
||||
prefix.push('#define enabledFragDepth')
|
||||
}
|
||||
if (extensions.drawBuffers) {
|
||||
const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number
|
||||
for (let i = 0, il = maxDrawBuffers; i < il; ++i) {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
export default `
|
||||
#ifdef dUseFog
|
||||
float depth = length(vViewPosition);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
|
||||
if (uTransparentBackground == 0) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
} else {
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
}
|
||||
#endif
|
||||
float depth = length(vViewPosition);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
|
||||
if (uTransparentBackground == 0) {
|
||||
gl_FragColor.rgb = mix(gl_FragColor.rgb, uFogColor, fogFactor);
|
||||
} else {
|
||||
float fogAlpha = (1.0 - fogFactor) * gl_FragColor.a;
|
||||
gl_FragColor.a = fogAlpha;
|
||||
}
|
||||
`
|
||||
9
src/mol-gl/shader/chunks/apply-interior-color.glsl.ts
Normal file
9
src/mol-gl/shader/chunks/apply-interior-color.glsl.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export default `
|
||||
if (interior) {
|
||||
if (uInteriorColorFlag == 1) {
|
||||
gl_FragColor.rgb = uInteriorColor;
|
||||
} else {
|
||||
gl_FragColor.rgb *= 1.0 - uInteriorDarkening;
|
||||
}
|
||||
}
|
||||
`
|
||||
@@ -13,12 +13,6 @@ export default `
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance)
|
||||
if (gl_FrontFacing == false) {
|
||||
material.rgb *= 1.0 - uInteriorDarkening;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mix material with overpaint
|
||||
#if defined(dOverpaint) && (defined(dColorType_uniform) || defined(dColorType_attribute) || defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance))
|
||||
material.rgb = mix(material.rgb, vOverpaint.rgb, vOverpaint.a);
|
||||
|
||||
7
src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts
Normal file
7
src/mol-gl/shader/chunks/check-picking-alpha.glsl.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export default `
|
||||
float depth = length(vViewPosition);
|
||||
float fogFactor = smoothstep(uFogNear, uFogFar, depth);
|
||||
float alpha = (1.0 - fogFactor) * uAlpha;
|
||||
if (uAlpha < uPickingAlphaThreshold || alpha < 0.1)
|
||||
discard; // ignore so the element below can be picked
|
||||
`
|
||||
@@ -25,4 +25,7 @@ uniform int uPickable;
|
||||
uniform int uTransparentBackground;
|
||||
|
||||
uniform float uInteriorDarkening;
|
||||
uniform int uInteriorColorFlag;
|
||||
uniform vec3 uInteriorColor;
|
||||
bool interior;
|
||||
`
|
||||
@@ -11,7 +11,7 @@ float intMod(const in float a, const in float b) { return a - b * float(int(a) /
|
||||
float pow2(const in float x) { return x*x; }
|
||||
|
||||
const float maxFloat = 10000.0; // NOTE constant also set in TypeScript
|
||||
const float floatLogFactor = log(maxFloat + 1.0);
|
||||
const float floatLogFactor = 9.210440366976517; // log(maxFloat + 1.0);
|
||||
float encodeFloatLog(const in float value) { return log(value + 1.0) / floatLogFactor; }
|
||||
float decodeFloatLog(const in float value) { return exp(value * floatLogFactor) - 1.0; }
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ void main(){
|
||||
#include assign_material_color
|
||||
|
||||
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
|
||||
if (uAlpha < uPickingAlphaThreshold)
|
||||
discard; // ignore so the element below can be picked
|
||||
#include check_picking_alpha
|
||||
gl_FragColor = material;
|
||||
#elif defined(dColorType_depth)
|
||||
gl_FragColor = material;
|
||||
|
||||
@@ -50,6 +50,11 @@ void main(){
|
||||
vec4 start = modelView * vec4(aStart, 1.0);
|
||||
vec4 end = modelView * vec4(aEnd, 1.0);
|
||||
|
||||
// assign position
|
||||
vec3 position = (aMapping.y < 0.5) ? aStart : aEnd;
|
||||
vec4 mvPosition = modelView * vec4(position, 1.0);
|
||||
vViewPosition = mvPosition.xyz;
|
||||
|
||||
// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
|
||||
// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
|
||||
// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user