Compare commits

...

304 Commits

Author SHA1 Message Date
Alexander Rose
112cfcac90 0.6.0-dev.6 2020-03-20 14:21:38 -07:00
David Sehnal
8016fcbd54 mol-plugin-state: use 'deposited' as default value for structure if no assembly is present 2020-03-20 21:37:23 +01:00
David Sehnal
6de97f1d03 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-20 21:22:54 +01:00
David Sehnal
204075bbe0 updateStructureComponent bugfix 2020-03-20 21:22:40 +01:00
David Sehnal
eb448bce37 fix volume streaming with custom props enabled 2020-03-20 20:59:23 +01:00
Alexander Rose
3d09b5cb67 use shallow cloning in PD.merge 2020-03-20 12:53:28 -07:00
Alexander Rose
35d06040f7 update debugHelper when visibility changes 2020-03-20 12:22:07 -07:00
Alexander Rose
0868e81944 improved download density action
- combined pdb providers
2020-03-20 12:08:44 -07:00
Alexander Rose
7efbeb7d0f improved download structure action
- updated pdb-dev url
- added archival pdbe
- combined pdb providers
2020-03-20 11:12:52 -07:00
David Sehnal
815f61b550 StructureHierarchyManagerState: auto-select newly added refs 2020-03-20 16:17:47 +01:00
David Sehnal
c3a90ab499 mol-canvas3d: shouldResetCamera check 2020-03-20 13:35:20 +01:00
David Sehnal
321126afa2 show current selection desc in StructureComponentControls header 2020-03-20 11:52:34 +01:00
Alexander Rose
9ad4a9c3c9 recreate vao to fix attempted binding of deleted attributes 2020-03-19 17:29:04 -07:00
Alexander Rose
b12f7c7ce4 add .name property to ShaderCode 2020-03-19 17:26:43 -07:00
Alexander Rose
6d7d3c0794 unitcell label tweaks 2020-03-19 14:24:32 -07:00
Alexander Rose
92b988a8d5 added PD.merge, always use props from structure.root for custom properties 2020-03-19 14:07:48 -07:00
Alexander Rose
18952ee2bd deepClone fix 2020-03-19 14:03:39 -07:00
David Sehnal
bff07888f9 StructureBuilder.tryCreateComponent* refactoring 2020-03-19 20:32:50 +01:00
David Sehnal
8e6b0b220a do not auto-attach interactions in component manager 2020-03-19 20:12:29 +01:00
David Sehnal
74acb0d078 mol-model-state: fix custom props transform bug 2020-03-19 19:14:11 +01:00
Alexander Rose
d4727eea02 removed console.log statement 2020-03-19 10:07:48 -07:00
Alexander Rose
76d14eba0c fix broken units-representation visual state update 2020-03-19 10:01:57 -07:00
David Sehnal
90d05d9260 fix bug in structure repr interaction 2020-03-19 17:43:11 +01:00
David Sehnal
23b24bbb6c custom props are now included by default
+ structure parent helper now takes decorators into account
+ ui & api tweaks
2020-03-19 15:33:37 +01:00
David Sehnal
91bc6f07c5 mol-plugin-state: added TrajectoryHierarchy presets
refactored representation presets
2020-03-19 12:56:47 +01:00
David Sehnal
9b2181667d mol-plugin-state: do not create empty unit cells 2020-03-19 10:59:35 +01:00
Alexander Rose
40347e3e46 0.6.0-dev.5 2020-03-18 21:16:44 -07:00
Alexander Rose
ed277f6e16 Merge remote-tracking branch 'origin/master' into objects 2020-03-18 21:12:52 -07:00
Alexander Rose
8f2085d8b4 0.6.0-dev.4 2020-03-18 20:59:42 -07:00
Alexander Rose
b5a48ad201 package updates 2020-03-18 20:59:01 -07:00
Alexander Rose
14b900791f wip, unitcell ref 2020-03-18 20:49:30 -07:00
Alexander Rose
de80799e68 allow any Loci in CameraManager.focusLoci 2020-03-18 20:44:54 -07:00
Alexander Rose
53eca387fc take renderable visibility into account for scene bounding-sphere 2020-03-18 20:35:14 -07:00
Alexander Rose
fc3005f271 ui tweaks 2020-03-18 16:47:57 -07:00
Alexander Rose
c95fdff00c tweaked StructureSelectionCategorys 2020-03-18 16:04:09 -07:00
Alexander Rose
3cd9042c72 added helpers to Model, better InitVolumeStreaming.isApplicable check 2020-03-18 15:47:58 -07:00
Alexander Rose
4d3914426e improved LociLabelManager to handle multiple loci 2020-03-18 15:46:29 -07:00
David Sehnal
af1fb7041e removed apps/model-server-query 2020-03-18 18:28:19 +01:00
David Sehnal
e2eb1bf223 model-server: config tweak 2020-03-18 13:49:54 +01:00
David Sehnal
5a64a6f1a3 mol-model: fixed a bug in secondary structure export 2020-03-18 13:45:43 +01:00
David Sehnal
88aa9303d7 model-server: fixed data_source bug, allow fetching source data over http(s) 2020-03-18 13:20:08 +01:00
Alexander Rose
5c77eec184 bounding-sphere fixes
- fixed renderable.spec
- simplify extrema for calculateInvariantBoundingSphere when possible
- fixed Sphere3D.expand
2020-03-17 23:19:26 -07:00
Alexander Rose
a931ed7c01 Merge branch 'master' into objects 2020-03-17 21:46:22 -07:00
Alexander Rose
94cd2b618c wip 2020-03-17 20:17:41 -07:00
Alexander Rose
9c97fc258d better box3d calculation in boundary-helper 2020-03-17 16:35:57 -07:00
Alexander Rose
0ac1cfe555 improved bounding sphere- drop hierarchy in favor of extrema points 2020-03-17 16:28:34 -07:00
Alexander Rose
3942f1bc33 added PickRequired type helper 2020-03-17 15:21:06 -07:00
Alexander Rose
a66e38a901 use unit/structure bounding-sphere in visuals geometry 2020-03-17 12:15:39 -07:00
Alexander Rose
f65f4f4aeb seperate grid and boundary calculation 2020-03-17 12:14:36 -07:00
Alexander Rose
f28b13bf87 shader: fixed broken flat-shaded & flip-sided 2020-03-17 09:48:28 -07:00
David Sehnal
8f211a0785 servers readme tweak 2020-03-17 15:50:05 +01:00
David Sehnal
ba1dfb2851 remove preprocess app from webpack config 2020-03-17 15:35:06 +01:00
David Sehnal
964d56752e 0.6.0-dev.3 2020-03-17 15:32:11 +01:00
David Sehnal
cb6a66eba5 0.6.0-dev.2 2020-03-17 15:25:18 +01:00
David Sehnal
94adf7259d model and volume server configuration and docs 2020-03-17 14:46:28 +01:00
David Sehnal
3c831b549a volume-server JSON config 2020-03-17 12:34:32 +01:00
Alexander Rose
7ccf36a0fa moved unitcell representation to mol-repr/shape 2020-03-16 22:38:46 -07:00
Alexander Rose
b0ee640c12 calc bespoke boundingSphere for unitcell 2020-03-16 22:18:09 -07:00
Alexander Rose
cd6d41a035 allow direct setting of geo bounding sphere
- support mesh, lines, text, spheres, points
2020-03-16 21:20:07 -07:00
Alexander Rose
9c8f1f3e11 wip 2020-03-16 17:20:20 -07:00
Alexander Rose
7c50a5a456 HierarchyHelper: check if the split spheres actually result in a smaller radius 2020-03-16 16:12:13 -07:00
David Sehnal
f3b6703fd4 mol-plugin-ui unsubscribe events 2020-03-16 20:05:06 +01:00
Alexander Rose
60f2716b5a fix missing camera reset from scene with only empty objects 2020-03-16 11:42:11 -07:00
David Sehnal
d7007ef99d CSS oveflow: hidden related fixes 2020-03-16 19:22:01 +01:00
David Sehnal
4aca41cdbb Controls Help in volume streaming controls 2020-03-16 15:54:47 +01:00
David Sehnal
7e31fac99a StructureHierarchy.childRoot for structure/model
- to correctly support decorators like custom props
2020-03-16 15:44:25 +01:00
David Sehnal
3bcf1cb6b5 VolumeStreaming manager & UI 2020-03-16 15:14:56 +01:00
David Sehnal
a29cc74304 StructureSourceControls tweak 2020-03-16 12:00:44 +01:00
David Sehnal
95c43d0864 fix camera.radius = 0 (leads to NaNs) 2020-03-16 11:59:32 +01:00
David Sehnal
369b958335 Refactor Bindings and show them in "Simple settings" 2020-03-16 11:51:05 +01:00
David Sehnal
9a17da8f65 Component UI improvements 2020-03-16 10:59:22 +01:00
Alexander Rose
0c4ba20d6e Merge branch 'master' of https://github.com/molstar/molstar 2020-03-15 23:46:55 -07:00
Alexander Rose
22936ff6c9 canvas3d settings improvements
- added clipping params
- show clipping radius
- ranmed render-style to lighting
2020-03-15 23:46:27 -07:00
David Sehnal
3369ad0bdc Change ParameterControls property name 2020-03-15 23:31:40 +01:00
David Sehnal
20853ef60b StructureSource: hide "actions button" when empty 2020-03-15 23:29:10 +01:00
David Sehnal
da0c66bd8e disable "Selected" button when empty 2020-03-15 23:25:08 +01:00
David Sehnal
44664917ff StateTree: always show remove all button 2020-03-15 23:22:36 +01:00
David Sehnal
ce26e002c7 StructureSource UI 2020-03-15 23:20:00 +01:00
David Sehnal
345b9f546c inline actions and updates in StateTree
mol-plugin code cleanup
2020-03-15 19:50:39 +01:00
David Sehnal
f4ed5e301d mol-plugin: code cleanup 2020-03-15 17:37:54 +01:00
David Sehnal
4818f851b3 measurements ui improvements 2020-03-15 16:54:25 +01:00
David Sehnal
6f2c47c0ca spinSpeed param update 2020-03-15 16:00:53 +01:00
David Sehnal
5e6bc0f0df ui tweaks 2020-03-15 15:47:30 +01:00
David Sehnal
083daa0b76 state undo label 2020-03-15 15:40:29 +01:00
David Sehnal
139a10ba8c do not freeze immer objects 2020-03-15 15:29:50 +01:00
David Sehnal
0b512487f5 add PD.Group.pivot, simplified viewport settings 2020-03-15 15:11:55 +01:00
David Sehnal
a85adfdf1f refactored Repr/Color/Size registry & built-ins
updated component UI
2020-03-15 14:32:04 +01:00
David Sehnal
0171b785af optimized Slider control 2020-03-15 01:37:47 +01:00
David Sehnal
4c1c484af9 PurePluginUIComponent fix 2020-03-15 00:25:53 +01:00
David Sehnal
db7585b6b6 CameraManager 2020-03-15 00:16:25 +01:00
David Sehnal
d263f6d929 allow to undo more than 1 step 2020-03-14 23:17:53 +01:00
David Sehnal
034c28e487 selection history updates
updated measurements ui
added focusLoci/Sphere
Loci stats label fixes
2020-03-14 22:44:36 +01:00
David Sehnal
9a88f57ce6 Unify PluginCommands.Highlight and HighlighAll
Select newly created models and structure for create all models
2020-03-14 20:40:40 +01:00
David Sehnal
28415646a2 StructureComponentManager.updateRepresentations tweak 2020-03-14 17:18:04 +01:00
David Sehnal
b03295ef81 undo button tweak 2020-03-14 16:49:13 +01:00
David Sehnal
7d3e849ff3 undo tweak 2020-03-14 15:24:03 +01:00
David Sehnal
926f20a6a4 mol-state: undo support 2020-03-14 15:21:45 +01:00
David Sehnal
c12d577ce6 Add intersect to Component modify options 2020-03-14 14:17:27 +01:00
David Sehnal
aa8d3a9841 tweak non-convalent interactions size factor 2020-03-14 13:41:59 +01:00
David Sehnal
f11e03eb85 Component UI: show modify operations only if available 2020-03-14 13:36:25 +01:00
David Sehnal
ab996b5189 Selection UI improvements 2020-03-14 13:19:03 +01:00
Alexander Rose
4c91e730a2 various params tweaks
- added more descriptions
- cleaned-up structure related params
- added missing quality categorisation
- clearer picking/granularity param
2020-03-13 23:39:35 -07:00
Alexander Rose
5ca5f44c61 tweaked set svg icons 2020-03-13 18:26:01 -07:00
Alexander Rose
f8a89b1c0d add HighlightMany plugin command 2020-03-13 18:12:30 -07:00
Alexander Rose
99dacef747 check structure equality for StructureElement.Loci 2020-03-13 18:10:20 -07:00
Alexander Rose
24fc37105e revert eslint indent rule (conflicts with switch statement) 2020-03-13 17:16:11 -07:00
Alexander Rose
cbd493d834 use sequential list as default for model-index color theme 2020-03-13 16:59:31 -07:00
Alexander Rose
53dbd1aedf ignore type params in eslint indent rule 2020-03-13 16:27:17 -07:00
Alexander Rose
6ab1af54aa added svg icon shapes for set operations 2020-03-13 15:59:47 -07:00
Alexander Rose
615d9758bb Merge branch 'master' of https://github.com/molstar/molstar 2020-03-13 10:17:20 -07:00
David Sehnal
341f9f8c91 repr/theme/size providers now contain unique name 2020-03-13 17:23:12 +01:00
David Sehnal
ba7d4a5215 customize current interaction, wip refactroring repr registries 2020-03-13 16:33:12 +01:00
David Sehnal
428187ad81 refactoring and tweaks 2020-03-13 13:47:20 +01:00
David Sehnal
fe96172fcd wip structure tools 2020-03-13 13:21:02 +01:00
David Sehnal
8b13be698c mol-plugin-state: StructureHierarchy tweaks 2020-03-13 12:23:04 +01:00
David Sehnal
5edc924e4f unify structure representation params creation 2020-03-13 11:55:06 +01:00
David Sehnal
78c50a4339 build config 2020-03-13 09:49:01 +01:00
Alexander Rose
607284585c intersect modifier for ui selections 2020-03-12 17:46:08 -07:00
Alexander Rose
f7af352f03 package updates 2020-03-12 17:45:10 -07:00
David Sehnal
008ec2c88c use tslib to provide helper functions (saves a lot of code) 2020-03-12 22:53:12 +01:00
David Sehnal
c91074ad91 add watch-viewer npm task for faster build times 2020-03-12 22:19:43 +01:00
David Sehnal
51fbb619e5 UI customization via spec & tweaks 2020-03-12 21:14:20 +01:00
David Sehnal
062c4b335b fix typo 2020-03-12 20:39:46 +01:00
David Sehnal
d4107e273d Merge branch 'master' of https://github.com/molstar/molstar 2020-03-12 20:33:14 +01:00
David Sehnal
4dad67fc2e improved Representation params 2020-03-12 20:33:02 +01:00
David Sehnal
64b0713742 represention preset common props 2020-03-12 20:19:24 +01:00
Alexander Rose
22c45e788e improved renderer and postprocessing params 2020-03-12 11:20:06 -07:00
David Sehnal
75de544669 wip source controls 2020-03-12 18:38:29 +01:00
David Sehnal
768ec10809 removed LociHighligh command extend 2020-03-12 17:29:16 +01:00
David Sehnal
4d5d9be399 wip: source structure manager 2020-03-12 17:26:51 +01:00
David Sehnal
7f4afc111e StructureHierarchyManager.currentTrajectories 2020-03-12 15:06:43 +01:00
David Sehnal
0365f883dd mol-plugin: customuze UI layout in viewport params 2020-03-12 14:04:42 +01:00
David Sehnal
6f98549f31 mol-state: doNotUpdateCurrent false as default 2020-03-12 13:23:49 +01:00
David Sehnal
77920818d9 icon tweak 2020-03-12 13:19:49 +01:00
David Sehnal
0682d54ee2 ActionMenu.Item.disabled 2020-03-12 13:17:11 +01:00
David Sehnal
fbcc3b2fa2 Measurement UI, updated "more options" icon in MappedParam 2020-03-12 12:46:26 +01:00
David Sehnal
c3f47b2ecb mol-plugin-state: component representation update 2020-03-12 12:16:34 +01:00
David Sehnal
8b9f59ac5a mol-plugin: component/selection managers/UI 2020-03-12 12:01:31 +01:00
David Sehnal
362dcabe5c PD.Color.isExpanded property 2020-03-12 07:49:33 +01:00
Alexander Rose
6e205661bd tweak 2020-03-11 23:02:52 -07:00
Alexander Rose
c1a8627702 fix BoundaryHelper edge cases & naming tweaks 2020-03-11 23:00:25 -07:00
Alexander Rose
2c5253943c lint and test fixes 2020-03-11 18:22:16 -07:00
Alexander Rose
03668216fa use EPOS-based boundary-helper throughout 2020-03-11 17:56:04 -07:00
Alexander Rose
37ef234803 wip, improved bounding sphere calculation
- Sphere3D.Hierarchy (one level)
- use hierarchy when merging spheres
- split sphere into two when data unevenly spread
2020-03-11 16:58:21 -07:00
David Sehnal
d25aa97fca ui tweaks 2020-03-11 23:29:36 +01:00
David Sehnal
17dc973305 mol-plugin-state: update label/desc for in custom props transform 2020-03-11 22:08:42 +01:00
David Sehnal
eed4e4a134 tweak 2020-03-11 21:03:21 +01:00
David Sehnal
5fd28c6cad mol-plugin-state: do not update interaction params if they didnt change 2020-03-11 21:00:50 +01:00
David Sehnal
0f69ea197d fix custom property transforms, isBusy behavior, other tweaks 2020-03-11 20:46:57 +01:00
David Sehnal
e1a214e1a8 fix component merging 2020-03-11 18:55:51 +01:00
David Sehnal
e7e14750cb Merge branch 'master' of https://github.com/molstar/molstar 2020-03-11 18:49:46 +01:00
David Sehnal
cb83013967 component ui tweak 2020-03-11 18:49:33 +01:00
Alexander Rose
0a84e1bb7b wip, bounding sphere of spheres calculation improvements 2020-03-11 10:22:51 -07:00
David Sehnal
aee7f4988a removed overpaint and repr helpers 2020-03-11 18:18:42 +01:00
David Sehnal
e762c402fa mol-plugin: component manager options 2020-03-11 18:17:06 +01:00
David Sehnal
4375cae70d structure builder tweaks 2020-03-11 17:26:12 +01:00
David Sehnal
90268a9041 Bundle.fromSubStructure 2020-03-11 16:46:52 +01:00
David Sehnal
a3ebc4df45 mol-plugin: modify component manager/ui 2020-03-11 16:42:59 +01:00
David Sehnal
b2743c76e0 wip mol-plugin managers 2020-03-11 12:58:53 +01:00
Alexander Rose
969a70d571 wip, use EPOS for boundary calculation 2020-03-10 18:50:29 -07:00
Alexander Rose
e6a538dbd7 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-10 17:38:18 -07:00
Alexander Rose
9cffce55c9 added EPOS for bounding sphere calculation 2020-03-10 17:37:57 -07:00
David Sehnal
7bc91d7e99 mol-plugin-state: StructureHierarchy fix 2020-03-10 22:50:41 +01:00
David Sehnal
943f5feb59 mol-plugin-state: TrajectoryFormatProvider 2020-03-10 22:12:41 +01:00
David Sehnal
dc72dbbc97 mol-plugin: PluginContext refactoring 2020-03-10 20:16:41 +01:00
David Sehnal
1513a1e2d2 mol-plugin: code cleanup and refactoring 2020-03-10 19:41:56 +01:00
David Sehnal
82989cae57 mol-plugin-ui: Selection/measurement UI updates 2020-03-10 18:27:20 +01:00
David Sehnal
74f8a5053e refactored StructureSelectionManager 2020-03-10 17:43:32 +01:00
David Sehnal
79f7ea209a wip StructureSelectionManager 2020-03-10 17:14:00 +01:00
David Sehnal
ba65e35c51 refactoring 2020-03-10 15:24:27 +01:00
David Sehnal
5bc4e3ce3b css tweaks 2020-03-10 15:13:43 +01:00
David Sehnal
1c7ac60c11 mol-plugin: state.isBusy behavior 2020-03-10 15:01:24 +01:00
David Sehnal
00cd54b69c mol-state: nested transactions 2020-03-10 14:53:05 +01:00
David Sehnal
90e3a3f0c7 ActionMenu tweak 2020-03-10 13:27:27 +01:00
David Sehnal
5eef3fc42a mol-plugin: Structure components UI 2020-03-10 13:10:47 +01:00
David Sehnal
83a8474731 mol-state: accept partial params for transforms; refactored measurements 2020-03-10 12:15:42 +01:00
Alexander Rose
a5e13c5152 wip, measurements controls improvements 2020-03-09 19:03:15 -07:00
Alexander Rose
b19f53d380 added distanceLabel, angleLabel, dihedralLabel 2020-03-09 19:02:30 -07:00
Alexander Rose
abadef1d2e moved FiniteArray to type helpers 2020-03-09 19:01:17 -07:00
Alexander Rose
a3d976c5b8 consistency fix, Vec3.dihedralAngle now returns radians 2020-03-09 19:00:56 -07:00
Alexander Rose
414b20f06d added Sphere3D.fromSphere3Ds and Loci.getBundleBoundingSphere 2020-03-09 19:00:07 -07:00
David Sehnal
d03a442b6c StructureHierarchyManager tweak 2020-03-09 18:46:00 +01:00
David Sehnal
e5acce03e5 mol-plugin-ui: StructureComponentControls 2020-03-09 18:42:27 +01:00
David Sehnal
fe714d4515 wip: StructureHierarchyManager 2020-03-09 17:27:59 +01:00
David Sehnal
7959344bca Current Interaction -> Focus, Fix StructureSelectionFromBundle 2020-03-08 17:12:40 +01:00
David Sehnal
b9582f171d rename 2020-03-08 15:41:03 +01:00
David Sehnal
410123d933 plugin/state: helpers & fixes 2020-03-08 15:34:15 +01:00
David Sehnal
ae484443d8 decorator transforms 2020-03-08 14:40:23 +01:00
David Sehnal
9d97e56f03 mol-state: StateObjectSelector.update 2020-03-08 13:39:18 +01:00
David Sehnal
01269ec1ef mol-state: use produce in "old params" update 2020-03-08 13:28:34 +01:00
David Sehnal
a204264bcc label fix 2020-03-07 15:53:06 +01:00
David Sehnal
eb0a048926 mol-plugin-ui: strongly typed icons & icons preview HTML 2020-03-07 14:43:57 +01:00
Alexander Rose
ba1509b37a wip, measurements ui 2020-03-06 18:02:47 -08:00
Alexander Rose
ad379e7a32 improved Interactions params 2020-03-06 16:12:46 -08:00
Alexander Rose
9d45beea3b ui: add option to cycle through select options on click 2020-03-06 16:12:16 -08:00
Alexander Rose
b2d134aeb4 added ObjectKeys type helper function 2020-03-06 15:07:20 -08:00
Alexander Rose
d500b8ea19 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 13:07:24 -08:00
Alexander Rose
8b3df4b373 plugin-state: preset improvements 2020-03-06 13:07:04 -08:00
David Sehnal
f01fc7c8ba mol-state: log error in transaction 2020-03-06 20:35:57 +01:00
David Sehnal
0e6da0bffa Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 19:10:58 +01:00
David Sehnal
eab8b1c2bf mol-plugin-state: fix in repr presets 2020-03-06 18:46:41 +01:00
Alexander Rose
8b42a0fede Merge branch 'master' of https://github.com/molstar/molstar 2020-03-06 08:49:13 -08:00
David Sehnal
4bbe078230 custom property reference counting 2020-03-06 17:03:19 +01:00
David Sehnal
aabb931d27 wip Custom Property references 2020-03-06 16:53:24 +01:00
David Sehnal
b1c1b21975 mol-plugin-state: representation components/presets 2020-03-06 14:57:31 +01:00
David Sehnal
7b5dc3ef96 PD.ValuesFor 2020-03-06 14:12:33 +01:00
David Sehnal
1f831f90e0 mol-plugin-state: StructureBuilder 2020-03-06 13:54:14 +01:00
Alexander Rose
ef2c1b51e9 tweaked geometry param info categories 2020-03-05 16:40:19 -08:00
David Sehnal
ace73c041b fix CustomElementProperty color theme 2020-03-05 10:44:55 +01:00
David Sehnal
3c70fe5303 support hiding remote state 2020-03-05 10:03:51 +01:00
Alexander Rose
137e23c025 wip, interactions options for representation panel 2020-03-04 18:18:56 -08:00
Alexander Rose
969a19d515 added defaultParams to CustomProperty.Provider 2020-03-04 18:18:05 -08:00
Alexander Rose
7536abe96c workaround for buggy gl_FrontFacing
- e.g. on some integrated Intel GPUs
2020-03-04 16:27:11 -08:00
Alexander Rose
8e20e163f9 Merge branch 'master' of https://github.com/molstar/molstar 2020-03-04 15:09:16 -08:00
Alexander Rose
adbe8e1f67 take parent structure into account for surface calculations
- new option `includeParent`
2020-03-04 15:08:46 -08:00
David Sehnal
5db134f34f mol-plugin-state: StructureComponent 2020-03-04 19:20:52 +01:00
David Sehnal
80cf7c1dd2 wip state builders 2020-03-04 15:01:46 +01:00
David Sehnal
9e5cc184ed State transactions, better error message for failed downloads 2020-03-04 13:44:38 +01:00
David Sehnal
a5185b456c mol-plugin-state: DataBuilder 2020-03-04 12:56:17 +01:00
Alexander Rose
0615198ad2 0.5.6 2020-03-03 15:13:50 -08:00
Alexander Rose
6cd422f5b3 package updates 2020-03-03 15:13:16 -08:00
Alexander Rose
4416178d6f fix error handling in readData 2020-03-03 14:52:11 -08:00
David Sehnal
b8cef99dc1 ParameterControls fix 2020-03-03 23:04:56 +01:00
David Sehnal
4dcb68af33 ParamDefinition category and hideIf support 2020-03-03 22:45:20 +01:00
David Sehnal
b4c70ab14a fix camera reset when loading stored state 2020-03-03 17:29:51 +01:00
David Sehnal
8436e17af9 removed PD.categories, added PD.isEssential & UI support 2020-03-03 16:05:56 +01:00
David Sehnal
bda04cbdc7 ParamDefinition categories (wip) 2020-03-03 15:27:10 +01:00
David Sehnal
7287947a55 added mol-plugin-state, refactored PluginCommand 2020-03-03 13:46:34 +01:00
David Sehnal
fb5eb1e19c Merge branch 'master' into plugin 2020-03-03 13:11:51 +01:00
David Sehnal
d735dcdc3e mol-plugin-ui: ActionMenu helpers 2020-03-03 13:09:51 +01:00
Alexander Rose
83d8a61400 0.5.5 2020-03-02 17:22:34 -08:00
Alexander Rose
b72f57c040 assembly symmetry tweaks 2020-03-02 17:21:07 -08:00
Alexander Rose
8a7ef1c704 added StructureOverpaintHelper.setFromExpression and alpha prop 2020-03-02 16:28:10 -08:00
Alexander Rose
8731bc13d1 StructureElement.Loci helpers
- firstElement
- firstResidue
- firstChain
2020-03-02 16:27:37 -08:00
Alexander Rose
d103cda0c8 StructureSelectionQuery improvements
- added categories
- StructureSelectionQueryList with standard residues
2020-03-02 14:04:27 -08:00
Alexander Rose
ca4e864e46 StructureRepresentationHelper.getRepresentationParams tweaks 2020-03-02 13:02:27 -08:00
Alexander Rose
0e25950d51 don't remove C! assembly symmetry 2020-03-02 13:01:35 -08:00
David Sehnal
2c29a90b5e fix 2020-03-02 15:32:24 +01:00
David Sehnal
2900836208 mol-canvas3d: requestCameraReset options 2020-03-02 15:28:20 +01:00
Alexander Rose
928a1df525 handle nullish values in SelectControl 2020-02-29 22:09:13 -08:00
Alexander Rose
16e0dff551 molecule types improvements 2020-02-29 22:08:29 -08:00
David Sehnal
7e443d5c9b mol-plugin-ui: simplified ActionMenu, fixed residue/amk selection 2020-02-29 18:15:39 +01:00
David Sehnal
172ae17966 mol-plugin-ui: SelectControl label fix 2020-02-29 15:40:28 +01:00
David Sehnal
8cbf7b5d0a mol-plugin-ui: Controls improvements 2020-02-29 15:27:21 +01:00
Alexander Rose
2c40b85880 0.5.4 2020-02-28 18:29:15 -08:00
Alexander Rose
ad388f23ae package updates 2020-02-28 18:28:39 -08:00
Alexander Rose
27b559a2d8 tweaked color-theme categories 2020-02-28 18:08:33 -08:00
Alexander Rose
c6d19e14c5 improved naming of PDBe and RCSB PDB provided properties 2020-02-28 17:46:10 -08:00
Alexander Rose
b807dca2d8 wip, StructureRepresentationHelper tweaks 2020-02-28 17:13:00 -08:00
Alexander Rose
2cae6e3f59 pass correct themeDataCtx in StructureRepresentation3DHelpers.createParams 2020-02-28 17:12:09 -08:00
Alexander Rose
69c73f3dcd StructureSelectionQuery improvements
- delayed query compilation to work with CustomPropSymbol
- optional async ensureCustomProperties method
- added hasClash, isBuried, isAccessible built-ins
- integrate .ensureCustomProperties with StructureSelectionHelper
2020-02-28 17:11:06 -08:00
Alexander Rose
4456ab2cd5 whitespace 2020-02-28 17:06:13 -08:00
Alexander Rose
bdda18de23 added mol-script symbol to ValidationReport 2020-02-28 17:05:22 -08:00
Alexander Rose
1b2c2f3d41 added Tag enum with common strings to AssemblySymmetry 2020-02-28 17:04:55 -08:00
Alexander Rose
fd19d29ef6 accessible surface area improvements
- added mol-script symbols
- helpers to get normalized value and flag
2020-02-28 17:04:03 -08:00
David Sehnal
1f0c0fd756 mol-geo: VisualQuality label fix 2020-02-27 16:56:28 +01:00
David Sehnal
83698cc52d mol-plugin-ui: ActionMenu fixes 2020-02-27 16:53:11 +01:00
David Sehnal
70b94deb20 tweak 2020-02-27 16:19:46 +01:00
David Sehnal
2da3df6e4d ParamDefinition.Select grouping & used in theme definitions 2020-02-27 16:17:23 +01:00
David Sehnal
509e633a69 mol-plugin-ui: use ActionMenu in SelectControl 2020-02-27 15:09:47 +01:00
David Sehnal
17fac2b82a mol-plugin-ui: ActionMenu refactoring 2020-02-27 14:36:48 +01:00
Alexander Rose
c543c4e10a 0.5.3 2020-02-26 17:20:24 -08:00
Alexander Rose
10073800dc added amino acids and nucleic bases queries 2020-02-26 17:14:40 -08:00
Alexander Rose
04c38250b4 tweaked action-menu offset 2020-02-26 17:14:08 -08:00
Alexander Rose
5ccb329af1 added SetUtils.toArray 2020-02-26 17:13:34 -08:00
Alexander Rose
3cecb53bc5 added model.properties.structAsymMap 2020-02-26 15:04:48 -08:00
Alexander Rose
0daf431d68 added and use cross-link loci/location 2020-02-26 14:23:51 -08:00
Alexander Rose
3e0c4242ad simplified interactions loci/location 2020-02-26 14:23:03 -08:00
Alexander Rose
17b775c377 areDataLociEqual, use shallowEqual to compare data objects 2020-02-26 14:22:05 -08:00
Alexander Rose
4525b98288 SecondaryStructure improvements
- use SecondaryStructureProvider in StructureProperties
- renamed mmCIF source to model
2020-02-26 11:43:15 -08:00
Alexander Rose
755699d479 fix structure/model props attachment 2020-02-26 11:37:57 -08:00
Alexander Rose
a84309b800 add mon_nstd_flag when deriving chemComp 2020-02-26 10:37:36 -08:00
Alexander Rose
16863535b8 cleanup, removed comment 2020-02-26 10:36:37 -08:00
Alexander Rose
dd9773d72e fix pdb parser: need cif-category not table 2020-02-26 10:36:12 -08:00
Alexander Rose
ec45f6c0ee removed 'modified residues'
- use non-standard flag in polymers residues instead to show interesting residues
- 'modified residues' is of limited value to get parent, better rely on chem_comp.type
2020-02-26 09:51:54 -08:00
David Sehnal
59235630bb mol-plugin-ui: ActionMenu, use in selection manager to test 2020-02-26 18:43:56 +01:00
David Sehnal
11ed0ca89b added draft of ParamMapping interface and control 2020-02-26 16:39:24 +01:00
Alexander Rose
188ea6e8e2 skin: fix select button hidden issue in FF 2020-02-25 22:06:52 -08:00
Alexander Rose
293b464d9f moved cross-link-restraint to props 2020-02-25 17:40:08 -08:00
Alexander Rose
0e91aa521e added IntervalControl, use 'step' for precision, allow 'range' in Vec3 2020-02-25 17:39:34 -08:00
Alexander Rose
5272593cc3 ermoved deprecated StructureAssemblyFromModel 2020-02-25 15:10:12 -08:00
Alexander Rose
c5f336b0e4 fix Assembly Symmetry Cluster coloring for deposited model 2020-02-25 14:53:37 -08:00
Alexander Rose
b1a6fa3ffc package updates, remove tslint related package 2020-02-25 14:52:55 -08:00
Alexander Rose
fe47134934 0.5.2 2020-02-25 12:00:59 -08:00
Alexander Rose
131cc606f0 added support to build custom assembly from symmetry operations and asym ids 2020-02-25 11:57:58 -08:00
Alexander Rose
3681f01fad added Spacegroup.getOperatorXyz 2020-02-25 11:56:48 -08:00
Alexander Rose
e966c112ab assymbly-symmetry: filter C1, export AssemblySymmetry3D 2020-02-24 17:25:49 -08:00
David Sehnal
d7b232b00b Merge branch 'master' into plugin 2020-02-24 10:12:35 +01:00
Alexander Rose
7986509ad3 0.5.1 2020-02-21 16:54:42 -08:00
Alexander Rose
73dcf970f3 fix fog handling so fog can be fully switched off 2020-02-21 16:26:14 -08:00
Alexander Rose
9377aa2d05 renamed renderstyle 'toon' to 'flat'
- reflects what the options is actually doing
2020-02-21 16:05:35 -08:00
Alexander Rose
686fa5a5ed package updates 2020-02-21 16:05:01 -08:00
Alexander Rose
c946ae6eab updated schemas, emmit .ts instead of .d.ts 2020-02-21 16:01:41 -08:00
David Sehnal
9b2f1d9415 updated to TypeScript 3.8 2020-02-21 17:13:10 +01:00
Alexander Rose
f6c28aa8e2 0.5.0 2020-02-20 16:25:38 -08:00
Alexander Rose
64c72aa6f5 0.5.0-dev.3 2020-02-20 10:56:21 -08:00
Alexander Rose
0a22917773 add unknown/any DNA/RNA base names N/DN 2020-02-20 10:23:39 -08:00
David Sehnal
3c4888e52b mol-canvas3d: tweak commit and camera reset 2020-02-20 12:55:21 +01:00
David Sehnal
2c327cfdf6 vscode tasks 2020-02-12 12:06:52 +01:00
David Sehnal
307f2efc97 Merge branch 'master' into plugin 2020-02-12 12:02:51 +01:00
David Sehnal
64c51f0d94 Merge branch 'master' into plugin 2020-02-10 11:51:29 +01:00
David Sehnal
7a0b4c4d23 Viewer: load from URL 2019-12-24 11:37:43 +01:00
David Sehnal
f45edbc4f0 mol-theme: truncate too long entry names 2019-12-24 11:27:47 +01:00
David Sehnal
65d3355b18 mol-plugin: DataManager [wip] 2019-12-23 17:19:38 +01:00
404 changed files with 14598 additions and 9304 deletions

View File

@@ -35,7 +35,7 @@
"@typescript-eslint/quotes": [
"warn",
"single",
{
{
"avoidEscape": true,
"allowTemplateLiterals": true
}

14
.vscode/tasks.json vendored
View File

@@ -9,6 +9,20 @@
"problemMatcher": [
"$tsc"
]
},
{
"type": "npm",
"script": "build-tsc",
"problemMatcher": [
"$tsc"
]
},
{
"type": "npm",
"script": "watch",
"problemMatcher": [
"$tsc"
]
}
]
}

View File

@@ -31,6 +31,7 @@ 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-state` State transformations, builders, and managers.
- `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.
@@ -61,6 +62,8 @@ This project builds on experience from previous solutions:
### Build automatically on file save:
npm run watch
If working on just the viewer, ``npm run watch-viewer`` will provide shorter compile times.
### Build with debug mode enabled:
DEBUG=molstar npm run watch

View File

@@ -1,7 +1,7 @@
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':
'./src/mol-model-props/rcsb/graphql/types.ts':
plugins:
- add: '/* eslint-disable */'
- time

View File

@@ -6,133 +6,64 @@ Model Server is a tool for preprocessing and querying macromolecular structure d
Installing and Running
=====================
Getting the code (use node 8+):
Requires nodejs 8+.
## From GitHub
```
git clone https://github.com/molstar/molstar
npm install
```
Customize configuration at ``src/server/model/config.ts`` to point to your data and which custom properties to include (see the [Custom Properties](#custom-properties) section). Alternatively, the config can be edited in the compiled version in ``build/node_modules/servers/model/config.js``.
Afterwards, build the project:
Afterwards, build the project source:
```
npm run build
npm run build-tsc
```
(or run watch mode for automatic rebuilds: ``npm run watch``)
and run the server by
Running the server locally for testing:
```
npm run model-server
```
or
```
node build/node_modules/servers/model/server
node lib/servers/model/server/server
```
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
## From NPM
```
npm install --production molstar
./model-server
```
(or ``node node_modules\.bin\model-server`` in Windows).
The NPM package contains all the tools mentioned here as "binaries":
- ``model-server``
- ``model-server-query``
- ``model-server-preprocess``
## Memory issues
### Production use
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Memory issues
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
Preprocessor
============
## Preprocessor
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. See the [Custom Properties](#custom-properties) section for providing custom properties.
The preprocessor application allows to add custom data to CIF files and/or convert CIF to BinaryCIF. ``node lib/servers/model/preprocess`` or ``model-server-preprocess`` binary from the NPM package.
## Usage
The app works in two modes: single files and folders.
## Local Mode
Single files:
```
node build\node_modules\servers\model\preprocess -i input.cif [-oc output.cif] [-ob output.bcif] [--cfg config.json]
```
Folder:
```
node build\node_modules\servers\model\preprocess -fin input_folder [-foc output_cif_folder] [-fob output_bcif_folder] [--cfg config.json]
```
## Config
The config speficies the maximum number of processes to use (in case of folder processing) and defines sources and parameters for custom properties.
Example:
```json
{
"numProcesses": 4,
"customProperties": {
"sources": [
"./properties/pdbe"
],
"params": {
"PDBe": {
"UseFileSource": false,
"API": {
"residuewise_outlier_summary": "https://www.ebi.ac.uk/pdbe/api/validation/residuewise_outlier_summary/entry",
"preferred_assembly": "https://www.ebi.ac.uk/pdbe/api/pdb/entry/summary",
"struct_ref_domain": "https://www.ebi.ac.uk/pdbe/api/mappings/sequence_domains"
}
}
}
}
}
```
The server can be run in local/file based mode using ``node lib/servers/model/query`` (``model-server-query`` binary from the NPM package).
Custom Properties
=================
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.
This feature is still in development.
Local Mode
==========
The server can be run in local/file based mode:
```
node build/node_modules/servers/model/server jobs.json
```
where ``jobs.json`` is an array of
```ts
type LocalInput = {
input: string,
output: string,
query: QueryName,
modelNums?: number[],
params?: any,
binary?: boolean
}[]
```
For example
```json
[
{
"input": "c:/test/quick/1tqn.cif",
"output": "c:/test/quick/localapi/1tqn_full.cif",
"query": "full"
},
{
"input": "c:/test/quick/1tqn.cif",
"output": "c:/test/quick/localapi/1tqn_full.bcif",
"query": "full",
"params": {}
},
{
"input": "c:/test/quick/1cbs_updated.cif",
"output": "c:/test/quick/localapi/1cbs_ligint.cif",
"query": "residueInteraction",
"params": {
"atom_site": { "label_comp_id": "REA" }
}
}
]
```
It is possible to provide property descriptors that transform data to internal representation and define how it should be exported into one or mode CIF categories. Examples of this are located in the ``mol-model-props`` module and are linked to the server in the config and ``servers/model/properties``.

View File

@@ -7,107 +7,64 @@ It uses the text based CIF and BinaryCIF formats to deliver the data to the clie
For quick info about the benefits of using the server, check out the [examples](examples.md).
Installing the Server
Installing and Running
=====================
- Install [Node.js](https://nodejs.org/en/) (tested on Node 6.* and 7.*; x64 version is strongly preferred).
- Get the code.
- Prepare the data.
- Run the server.
Requires nodejs 8+.
Preparing the Data
------------------
## From GitHub
```
git clone https://github.com/molstar/molstar
npm install
```
Afterwards, build the project source:
```
npm run build-tsc
```
and run the server by
```
node lib/servers/volume/server
```
## From NPM
```
npm install --production molstar
./volume-server
```
(or ``node node_modules\.bin\volume-server`` in Windows).
The NPM package contains all the tools mentioned here as "binaries":
- ``volume-server``
- ``volume-server-pack``
- ``volume-server-query``
### Production use
In production it is required to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Memory issues
Sometimes nodejs might run into problems with memory. This is usually resolved by adding the ``--max-old-space-size=8192`` parameter.
## Preparing the Data
For the server to work, CCP4/MAP (models 0, 1, 2 are supported) input data need to be converted into a custom block format.
To achieve this, use the ``pack`` application.
To achieve this, use the ``pack`` application (``node lib/servers/volume/pack`` or ``volume-server-pack`` binary from the NPM package).
- To prepare data from x-ray based methods, use:
## Local Mode
```
node pack -xray main.ccp4 diff.ccp4 out.mdb
```
- For EM data, use:
```
node pack -em em.map out.mdb
```
Running the Server
------------------
- Install production dependencies:
```
npm install --only=production
```
- Update ``server-config.js`` to link to your data and optionally tweak the other parameters.
- Run it:
```
node server
```
In production it is a good idea to use a service that will keep the server running, such as [forever.js](https://github.com/foreverjs/forever).
### Local Mode
The program ``local`` in the build folder can be used to query the data without running a http server.
- ``node local`` prints the program usage.
- ``node local jobs.json`` takes a list of jobs to execute in JSON format. A job entry is defined by this interface:
```TypeScript
interface JobEntry {
source: {
filename: string,
name: string,
id: string
},
query: {
kind: 'box' | 'cell',
space?: 'fractional' | 'cartesian',
bottomLeft?: number[],
topRight?: number[],
}
params: {
/** Determines the detail level as specified in server-config */
detail?: number,
/**
* Determines the sampling level:
* 1: Original data
* 2: Downsampled by factor 1/2
* ...
* N: downsampled 1/2^(N-1)
*/
forcedSamplingLevel?: number,
asBinary: boolean,
},
outputFolder: string
}
```
Example ``jobs.json`` file content:
```TypeScript
[{
source: {
filename: `g:/test/mdb/emd-8116.mdb`,
name: 'em',
id: '8116',
},
query: {
kind: 'cell'
},
params: {
detail: 4,
asBinary: true
},
outputFolder: 'g:/test/local-test'
}]
```
The program ``lib/servers/volume/pack`` (``volume-server-query`` in NPM package) can be used to query the data without running a http server.
## Navigating the Source Code
@@ -122,8 +79,8 @@ The source code is split into 2 mains parts: ``pack`` and ``server``:
Consuming the Data
==================
The data can be consumed in any (modern) browser using the [CIFTools.js library](https://github.com/dsehnal/CIFTools.js) (or any other piece of code that can read text or binary CIF).
The data can be consumed in any (modern) browser using the [ciftools library](https://github.com/molstar/ciftools) (or any other piece of code that can read text or binary CIF).
The [Data Format](DataFormat.md) document gives a detailed description of the server response format.
As a reference/example of the server usage, please see the implementation in [LiteMol](https://github.com/dsehnal/LiteMol) ([CIF.ts + Data.ts](https://github.com/dsehnal/LiteMol/tree/master/src/lib/Core/Formats/Density), [UI](https://github.com/dsehnal/LiteMol/tree/master/src/Viewer/Extensions/DensityStreaming)) or in Mol*.
As a reference/example of the server usage is available in Mol* ``mol-plugin`` module.

2644
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "molstar",
"version": "0.5.0-dev.2",
"version": "0.6.0-dev.6",
"description": "A comprehensive macromolecular library.",
"homepage": "https://github.com/molstar/molstar#readme",
"repository": {
@@ -14,17 +14,19 @@
"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",
"build-tsc": "tsc --incremental",
"build-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/",
"build-webpack": "webpack --mode production",
"watch": "concurrently --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"watch-tsc": "tsc -watch",
"watch": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack\"",
"watch-viewer": "concurrently -c \"green,gray,gray\" --names \"tsc,ext,wpc\" --kill-others \"npm:watch-tsc\" \"npm:watch-extra\" \"npm:watch-webpack-viewer\"",
"watch-tsc": "tsc --watch --incremental",
"watch-extra": "cpx \"src/**/*.{scss,woff,woff2,ttf,otf,eot,svg,html,ico}\" lib/ --watch",
"watch-webpack": "webpack -w --mode development --display minimal",
"watch-webpack": "webpack -w --mode development --display errors-only --info-verbosity verbose",
"watch-webpack-viewer": "webpack -w --mode development --display errors-only --info-verbosity verbose --config ./webpack.config.viewer.js",
"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-test": "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",
@@ -33,6 +35,14 @@
"files": [
"lib/"
],
"bin": {
"model-server": "lib/servers/model/server.js",
"model-server-query": "lib/servers/model/local.js",
"model-server-preprocess": "lib/servers/model/preprocess.js",
"volume-server": "lib/servers/volume/server.js",
"volume-server-query": "lib/servers/volume/query.js",
"volume-server-pack": "lib/servers/volume/pack.js"
},
"nodemonConfig": {
"ignoreRoot": [
"./node_modules",
@@ -64,17 +74,16 @@
],
"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",
"@graphql-codegen/add": "^1.13.1",
"@graphql-codegen/cli": "^1.13.1",
"@graphql-codegen/time": "^1.13.1",
"@graphql-codegen/typescript": "^1.13.1",
"@graphql-codegen/typescript-graphql-files-modules": "^1.13.1",
"@graphql-codegen/typescript-graphql-request": "^1.13.1",
"@graphql-codegen/typescript-operations": "^1.13.1",
"@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",
"@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0",
"benchmark": "^2.1.4",
"circular-dependency-plugin": "^5.2.0",
"concurrently": "^5.1.0",
@@ -82,7 +91,7 @@
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"extra-watch-webpack-plugin": "^1.0.3",
"file-loader": "^5.0.2",
"file-loader": "^6.0.0",
"fs-extra": "^8.1.0",
"http-server": "^0.12.1",
"jest": "^25.1.0",
@@ -93,22 +102,22 @@
"raw-loader": "^4.0.0",
"resolve-url-loader": "^3.1.1",
"sass-loader": "^8.0.2",
"simple-git": "^1.131.0",
"simple-git": "^1.132.0",
"style-loader": "^1.1.3",
"ts-jest": "^25.2.0",
"typescript": "^3.7.5",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
"ts-jest": "^25.2.1",
"typescript": "^3.8.3",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
},
"dependencies": {
"@types/argparse": "^1.0.38",
"@types/benchmark": "^1.0.31",
"@types/compression": "1.7.0",
"@types/express": "^4.17.2",
"@types/jest": "^25.1.2",
"@types/node": "^13.7.0",
"@types/node-fetch": "^2.5.4",
"@types/react": "^16.9.19",
"@types/express": "^4.17.3",
"@types/jest": "^25.1.4",
"@types/node": "^13.9.2",
"@types/node-fetch": "^2.5.5",
"@types/react": "^16.9.23",
"@types/react-dom": "^16.9.5",
"@types/swagger-ui-dist": "3.0.5",
"argparse": "^1.0.10",
@@ -117,12 +126,14 @@
"cors": "^2.8.5",
"express": "^4.17.1",
"graphql": "^14.6.0",
"immer": "^6.0.2",
"immutable": "^3.8.2",
"node-fetch": "^2.6.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"rxjs": "^6.5.4",
"swagger-ui-dist": "^3.25.0",
"tslib": "^1.11.1",
"util.promisify": "^1.0.1",
"xhr2": "^0.2.0"
}

View File

@@ -6,13 +6,13 @@
import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
import { PluginContext } from '../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { StateBuilder } from '../../mol-state';
import Expression from '../../mol-script/language/expression';
import { BuiltInColorThemeName } from '../../mol-theme/color';
import { ColorTheme } from '../../mol-theme/color';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
type SupportedFormats = 'cif' | 'pdb'
export namespace StateHelper {
@@ -67,27 +67,30 @@ export namespace StateHelper {
}
export function assemble(b: StateBuilder.To<PSO.Molecule.Model>, id?: string) {
return b.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: id || 'deposited' }, { tags: 'asm' })
const props = {
type: {
name: 'assembly' as const,
params: { id: id || 'deposited' }
}
}
return b.apply(StateTransforms.Model.StructureFromModel, props, { tags: 'asm' })
}
export function visual(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'), { tags: 'seq-visual' });
createStructureRepresentationParams(ctx, void 0, { type: 'cartoon' }), { tags: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'), { tags: 'het-visual' });
// visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
// .apply(StateTransforms.Representation.StructureRepresentation3D,
// StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }), { tags: 'water-visual' });
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick' }), { tags: 'het-visual' });
return visualRoot;
}
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, coloring?: BuiltInColorThemeName) {
export function ballsAndSticks(ctx: PluginContext, visualRoot: StateBuilder.To<PSO.Molecule.Structure>, expression: Expression, color?: ColorTheme.BuiltIn) {
visualRoot
.apply(StateTransforms.Model.StructureSelectionFromExpression, { expression })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', void 0, coloring), { tags: 'het-visual' });
createStructureRepresentationParams(ctx, void 0, { type: 'ball-and-stick', color }), { tags: 'het-visual' });
return visualRoot;
}

View File

@@ -7,12 +7,11 @@
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/command';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { StateBuilder, StateTransform } from '../../mol-state';
import { StripedResidues } from './coloring';
import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
@@ -21,6 +20,7 @@ import { CustomToastMessage } from './controls';
import { EmptyLoci } from '../../mol-model/loci';
import { StructureSelection } from '../../mol-model/structure';
import { Script } from '../../mol-script/script';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
@@ -47,8 +47,8 @@ class BasicWrapper {
}
});
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.propertyProvider.descriptor.name, StripedResidues.colorThemeProvider!);
this.plugin.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.representation.structure.themes.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
}
@@ -61,25 +61,31 @@ class BasicWrapper {
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
.apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'cartoon'), { ref: 'seq-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick', { alpha: 0.51 }), { ref: 'water-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill'), { ref: 'ihm-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
return visualRoot;
}
@@ -87,7 +93,7 @@ class BasicWrapper {
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.dataState;
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
@@ -97,22 +103,29 @@ class BasicWrapper {
let tree: StateBuilder.Root;
if (loadType === 'full') {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.Camera.Reset(this.plugin, { });
}
setBackground(color: number) {
const renderer = this.plugin.canvas3d!.props.renderer;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
}
toggleSpin() {
@@ -120,8 +133,8 @@ class BasicWrapper {
const trackball = this.plugin.canvas3d.props.trackball;
const spinning = trackball.spin;
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
}
animate = {
@@ -137,68 +150,68 @@ class BasicWrapper {
coloring = {
applyStripes: async () => {
const state = this.plugin.state.dataState;
const state = this.plugin.state.data;
const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
const tree = state.build();
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
for (const v of visuals) {
tree.to(v).update(old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
await PluginCommands.State.Update(this.plugin, { state, tree });
}
}
interactivity = {
highlightOn: () => {
const seq_id = 7;
const data = (this.plugin.state.dataState.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
const data = (this.plugin.state.data.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
'group-by': Q.struct.atomProperty.macromolecular.residueKey()
}), data);
const loci = StructureSelection.toLociWithSourceUnits(sel);
this.plugin.interactivity.lociHighlights.highlightOnly({ loci });
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
},
clearHighlight: () => {
this.plugin.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
}
}
tests = {
staticSuperposition: async () => {
const state = this.plugin.state.dataState;
const state = this.plugin.state.data;
const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: StateTransform.RootRef });
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
await PluginCommands.State.Update(this.plugin, { state, tree });
},
dynamicSuperposition: async () => {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
await PluginCommands.State.RemoveObject(this.plugin, { state: this.plugin.state.data, ref: StateTransform.RootRef });
await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
},
toggleValidationTooltip: async () => {
const state = this.plugin.state.behaviorState;
const state = this.plugin.state.behaviors;
const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
await PluginCommands.State.Update(this.plugin, { state, tree });
},
showToasts: () => {
PluginCommands.Toast.Show.dispatch(this.plugin, {
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 1',
message: 'This is an example text, timeout 3s',
key: 'toast-1',
timeoutMs: 3000
});
PluginCommands.Toast.Show.dispatch(this.plugin, {
PluginCommands.Toast.Show(this.plugin, {
title: 'Toast 2',
message: CustomToastMessage,
key: 'toast-2'
});
},
hideToasts: () => {
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide.dispatch(this.plugin, { key: 'toast-2' });
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
}
}
}

View File

@@ -9,9 +9,9 @@
import { PluginContext } from '../../mol-plugin/context';
import { Mat4 } from '../../mol-math/linear-algebra';
import { StateHelper } from './helpers';
import { PluginCommands } from '../../mol-plugin/command';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateSelection, StateBuilder } from '../../mol-state';
import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
import { PluginStateObject as PSO } from '../../mol-plugin-state/objects';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { compile } from '../../mol-script/runtime/query/compiler';
import { StructureSelection, QueryContext } from '../../mol-model/structure';
@@ -32,7 +32,7 @@ export type SuperpositionTestInput = {
// }
export function buildStaticSuperposition(ctx: PluginContext, src: SuperpositionTestInput) {
const b = ctx.state.dataState.build().toRoot();
const b = ctx.state.data.build().toRoot();
for (const s of src) {
StateHelper.visual(ctx,
StateHelper.transform(
@@ -63,7 +63,7 @@ export const StaticSuperpositionTestData: SuperpositionTestInput = [
];
export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[], comp_id: string) {
const state = ctx.state.dataState;
const state = ctx.state.data;
const structures = state.build().toRoot();
for (const s of src) {
@@ -71,7 +71,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
StateHelper.getModel(StateHelper.download(structures, `https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`), 'cif'));
}
await PluginCommands.State.Update.dispatch(ctx, { state, tree: structures });
await PluginCommands.State.Update(ctx, { state, tree: structures });
const pivot = MS.struct.filter.first([
MS.struct.generator.atomGroups({
@@ -99,7 +99,7 @@ export async function dynamicSuperpositionTest(ctx: PluginContext, src: string[]
pivot, rest);
}
await PluginCommands.State.Update.dispatch(ctx, { state, tree: visuals });
await PluginCommands.State.Update(ctx, { state, tree: visuals });
}
function siteVisual(ctx: PluginContext, b: StateBuilder.To<PSO.Molecule.Structure>, pivot: Expression, rest: Expression) {

View File

@@ -144,9 +144,9 @@ async function createBonds() {
const comp_id: string[] = []
const atom_id_1: string[] = []
const atom_id_2: string[] = []
const value_order: string[] = []
const pdbx_aromatic_flag: string[] = []
const pdbx_stereo_config: string[] = []
const value_order: typeof mmCIF_chemCompBond_schema['value_order']['T'][] = []
const pdbx_aromatic_flag: typeof mmCIF_chemCompBond_schema['pdbx_aromatic_flag']['T'][] = []
const pdbx_stereo_config: typeof mmCIF_chemCompBond_schema['pdbx_stereo_config']['T'][] = []
const molstar_protonation_variant: string[] = []
function addBonds(compId: string, ccb: CCB, protonationVariant: boolean) {

View File

@@ -7,12 +7,12 @@
import { createPlugin, DefaultPluginSpec } from '../../../mol-plugin';
import './index.html'
import { PluginContext } from '../../../mol-plugin/context';
import { PluginCommands } from '../../../mol-plugin/command';
import { StateTransforms } from '../../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
import { PluginStateObject as PSO } from '../../../mol-plugin/state/objects';
import { PluginCommands } from '../../../mol-plugin/commands';
import { StateTransforms } from '../../../mol-plugin-state/transforms';
import { PluginStateObject as PSO } from '../../../mol-plugin-state/objects';
import { StateBuilder } from '../../../mol-state';
import { Canvas3DProps } from '../../../mol-canvas3d/canvas3d';
import { createStructureRepresentationParams } from '../../../mol-plugin-state/helpers/structure-representation-params';
require('mol-plugin-ui/skin/light.scss')
type SupportedFormats = 'cif' | 'pdb'
@@ -94,7 +94,7 @@ class LightingDemo {
setPreset(preset: Canvas3DPreset) {
const props = getPreset(preset)
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: {
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: {
...props,
multiSample: {
...this.plugin.canvas3d!.props.multiSample,
@@ -120,18 +120,24 @@ class LightingDemo {
? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
: b.apply(StateTransforms.Model.TrajectoryFromPDB);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
return parsed
.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
.apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
.apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
}
private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'spacefill', {}, 'illustrative'), { ref: 'seq-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill', color: 'illustrative' }), { ref: 'seq-visual' });
visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsStatic(this.plugin, 'ball-and-stick'), { ref: 'het-visual' });
createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
return visualRoot;
}
@@ -139,7 +145,7 @@ class LightingDemo {
async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.dataState;
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
@@ -149,17 +155,23 @@ class LightingDemo {
let tree: StateBuilder.Root;
if (loadType === 'full') {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
tree = state.build();
this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
} else {
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
tree = state.build();
tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
this.loadedParams = { url, format, assemblyId };
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.Camera.Reset(this.plugin, { });
}
}

View File

@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<title>Mol* ModelServer Query Builder</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>

View File

@@ -1,134 +0,0 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import * as Rx from 'rxjs'
import { QueryDefinition, QueryList } from '../../servers/model/server/api'
import './index.html'
interface State {
query: Rx.BehaviorSubject<QueryDefinition>,
id: Rx.BehaviorSubject<string>,
params: Rx.BehaviorSubject<any>,
isBinary: Rx.BehaviorSubject<boolean>,
models: Rx.BehaviorSubject<number[]>,
url: Rx.Subject<string>
}
class Root extends React.Component<{ state: State }, { }> {
render() {
return <div>
<div>
Query: <QuerySelect state={this.props.state} />
</div>
<div>
ID: <input type='text' onChange={t => this.props.state.id.next(t.currentTarget.value)} />
</div>
<div>
Params:<br/>
<QueryParams state={this.props.state} />
</div>
<div>
Model numbers (empty for all): <ModelNums state={this.props.state} />
</div>
<div>
<input type='checkbox' onChange={t => this.props.state.isBinary.next(!!t.currentTarget.checked)} /> Binary
</div>
<div>
Query string:
<QueryUrl state={this.props.state} />
</div>
</div>
}
}
class QuerySelect extends React.Component<{ state: State }> {
render() {
return <select onChange={s => this.props.state.query.next(QueryList[+s.currentTarget.value].definition)}>
{ QueryList.map((q, i) => <option value={i} key={i} selected={i === 1}>{q.definition.niceName}</option>) }
</select>
}
}
class QueryParams extends React.Component<{ state: State }, { prms: string }> {
state = { prms: '' };
parseParams(str: string) {
this.setState({ prms: str });
try {
const params = JSON.parse(str);
this.props.state.params.next(params);
} catch {
this.props.state.params.next({});
}
}
componentDidMount() {
this.props.state.query.subscribe(q => this.setState({ prms: formatParams(q) }))
}
render() {
return <textarea style={{height: '300px'}} value={this.state.prms} cols={80} onChange={t => this.parseParams(t.currentTarget.value)} />;
}
}
class QueryUrl extends React.Component<{ state: State }, { queryString: string }> {
state = { queryString: '' };
componentDidMount() {
this.props.state.url.subscribe(url => this.setState({ queryString: url }))
}
render() {
return <input type='text' value={this.state.queryString} style={{ width: '800px' }} />
}
}
class ModelNums extends React.Component<{ state: State }> {
render() {
return <input type='text' defaultValue='1' style={{ width: '300px' }} onChange={t =>
this.props.state.models.next(t.currentTarget.value.split(',')
.map(v => v.trim())
.filter(v => !!v)
.map(v => +v)
)} />
}
}
const state: State = {
query: new Rx.BehaviorSubject(QueryList[1].definition),
id: new Rx.BehaviorSubject('1cbs'),
params: new Rx.BehaviorSubject({ }),
isBinary: new Rx.BehaviorSubject<boolean>(false),
models: new Rx.BehaviorSubject<number[]>([]),
url: new Rx.Subject()
}
function formatParams(def: QueryDefinition) {
const prms = Object.create(null);
for (const p of def.jsonParams) {
prms[p.name] = p.exampleValues ? p.exampleValues[0] : void 0;
}
return JSON.stringify(prms, void 0, 2);
}
function formatUrl() {
const json = JSON.stringify({
name: state.query.value.name,
id: state.id.value,
modelNums: state.models.value.length ? state.models.value : void 0,
binary: state.isBinary.value,
params: state.params.value
});
state.url.next(encodeURIComponent(json));
}
Rx.merge(state.query, state.id, state.params, state.isBinary, state.models).subscribe(s => formatUrl());
ReactDOM.render(<Root state={state} />, document.getElementById('app'));

View File

@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import * as _ from '../../mol-plugin/state/transforms'
import * as _ from '../../mol-plugin-state/transforms'
import { StateTransformer, StateObject } from '../../mol-state';
import { StringBuilder } from '../../mol-util';
import * as fs from 'fs';

View File

@@ -39,7 +39,7 @@ function paramInfo(param: PD.Any, offset: number): string {
}
}
function oToS(options: readonly (readonly [string, string])[]) {
function oToS(options: readonly (readonly [string, string] | readonly [string, string, string])[]) {
return options.map(o => `'${o[0]}'`).join(', ');
}

View File

@@ -123,18 +123,6 @@ export function printSequence(model: Model) {
console.log();
}
export function printModRes(model: Model) {
console.log('\nModified Residues\n=============');
const map = model.properties.modifiedResidues.parentId;
const { label_comp_id, _rowCount } = model.atomicHierarchy.residues;
for (let i = 0; i < _rowCount; i++) {
const comp_id = label_comp_id.value(i);
if (!map.has(comp_id)) continue;
console.log(`[${i}] ${map.get(comp_id)} -> ${comp_id}`);
}
console.log();
}
export function printRings(structure: Structure) {
console.log('\nRings\n=============');
for (const unit of structure.units) {
@@ -221,7 +209,6 @@ async function run(frame: CifFrame, args: Args) {
if (args.rings) printRings(structure);
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]);
}

View File

@@ -6,7 +6,7 @@
import { StateAction } from '../../../../mol-state';
import { PluginContext } from '../../../../mol-plugin/context';
import { PluginStateObject as PSO } from '../../../../mol-plugin/state/objects';
import { PluginStateObject as PSO } from '../../../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Ingredient, CellPacking, Cell } from './data';
import { getFromPdb, getFromCellPackDB } from './util';
@@ -16,8 +16,7 @@ import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
import { SymmetryOperator } from '../../../../mol-math/geometry';
import { Task } from '../../../../mol-task';
import { StructureRepresentation3DHelpers } from '../../../../mol-plugin/state/transforms/representation';
import { StateTransforms } from '../../../../mol-plugin/state/transforms';
import { StateTransforms } from '../../../../mol-plugin-state/transforms';
import { distinctColors } from '../../../../mol-util/color/distinct';
import { ModelIndexColorThemeProvider } from '../../../../mol-theme/color/model-index';
import { Hcl } from '../../../../mol-util/color/spaces/hcl';
@@ -26,12 +25,11 @@ import { MolScriptBuilder as MS } from '../../../../mol-script/language/builder'
import { getMatFromResamplePoints } from './curve';
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 { 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';
import { createStructureRepresentationParams } from '../../../../mol-plugin-state/helpers/structure-representation-params';
function getCellPackModelUrl(fileName: string, baseUrl: string) {
return `${baseUrl}/results/${fileName}`
@@ -398,9 +396,9 @@ export const LoadCellPackModel = StateAction.build({
}
cellpackTree
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
color: getColorParams(hue)
createStructureRepresentationParams(ctx, Structure.Empty, {
...getReprParams(ctx, params.preset),
...getColorParams(hue)
})
)
}
@@ -414,8 +412,8 @@ export const LoadCellPackModel = StateAction.build({
.apply(StateTransforms.Model.StructureFromModel, undefined, { state: { isGhost: true } })
.apply(StateTransforms.Misc.CreateGroup, { label: 'HIV1_envelope_Membrane' })
.apply(StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.createParams(ctx, Structure.Empty, {
repr: getReprParams(ctx, params.preset),
createStructureRepresentationParams(ctx, Structure.Empty, {
...getReprParams(ctx, params.preset),
color: UniformColorThemeProvider
})
)
@@ -431,50 +429,41 @@ function getReprParams(ctx: PluginContext, params: { representation: Representat
switch (representation) {
case 'spacefill':
return traceOnly
? [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ sizeFactor: 2, ignoreHydrogens: true })
] as [any, any]
: [
ctx.structureRepresentation.registry.get('spacefill'),
() => ({ ignoreHydrogens: true })
] as [any, any]
? {
type: ctx.representation.structure.registry.get('spacefill'),
typeParams: { sizeFactor: 2, ignoreHydrogens: true }
} : {
type: ctx.representation.structure.registry.get('spacefill'),
typeParams: { ignoreHydrogens: true }
}
case 'gaussian-surface':
return [
ctx.structureRepresentation.registry.get('gaussian-surface'),
() => ({
return {
type: ctx.representation.structure.registry.get('gaussian-surface'),
typeParams: {
quality: 'custom', resolution: 10, radiusOffset: 2,
alpha: 1.0, flatShaded: false, doubleSided: false,
ignoreHydrogens: true
})
] as [any, any]
}
}
case 'point':
return [
ctx.structureRepresentation.registry.get('point'),
() => ({ ignoreHydrogens: true })
] as [any, any]
return { type: ctx.representation.structure.registry.get('point') }
case 'ellipsoid':
return [
ctx.structureRepresentation.registry.get('orientation'),
() => ({})
] as [any, any]
return { type: ctx.representation.structure.registry.get('orientation') }
}
}
function getColorParams(hue: [number, number]) {
return [
ModelIndexColorThemeProvider,
(c: ColorTheme.Provider<any>, ctx: ThemeRegistryContext) => {
return {
palette: {
name: 'generate',
params: {
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800,
maxCount: 75
}
function getColorParams(hue: [number, number]): any {
return {
color: ModelIndexColorThemeProvider,
colorParams: {
palette: {
name: 'generate',
params: {
hue, chroma: [30, 80], luminance: [15, 85],
clusteringStepCount: 50, minSampleCount: 800,
maxCount: 75
}
}
}
] as [any, any]
}
}

View File

@@ -4,7 +4,7 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin/state/objects';
import { PluginStateObject as PSO, PluginStateTransform } from '../../../../mol-plugin-state/objects';
import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
import { Task } from '../../../../mol-task';
import { CellPack as _CellPack, Cell, CellPacking } from './data';

View File

@@ -18,7 +18,7 @@
// 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 { createStructureRepresentation3dParamss } from '../../../mol-plugin/state/transforms/representation';
// import { createDefaultStructureComplex } from '../../../mol-plugin/util/structure-complex-helper';
// export const CreateJoleculeState = StateAction.build({
@@ -108,13 +108,13 @@
// group
// .apply(StateTransforms.Model.StructureSelectionFromExpression, { expression: MS.struct.modifier.wholeResidues([ expression ]), label: 'Residue' })
// .apply(StateTransforms.Representation.StructureRepresentation3D,
// StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
// createStructureRepresentation3dParamss.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'));
// createStructureRepresentation3dParamss.getDefaultParamsStatic(plugin, 'ball-and-stick'));
// }
// // TODO
// // for (const l of params.e.distances) {

View File

@@ -10,10 +10,11 @@ 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 { PluginCommands } from '../../mol-plugin/commands';
import { PluginSpec } from '../../mol-plugin/spec';
import { LoadCellPackModel } from './extensions/cellpack/model';
import { StructureFromCellpack } from './extensions/cellpack/state';
import { DownloadStructure } from '../../mol-plugin-state/actions/structure';
require('mol-plugin-ui/skin/light.scss')
function getParam(name: string, regex: string): string {
@@ -47,6 +48,7 @@ function init() {
};
const plugin = createPlugin(document.getElementById('app')!, spec);
trySetSnapshot(plugin);
tryLoadFromUrl(plugin);
}
async function trySetSnapshot(ctx: PluginContext) {
@@ -58,11 +60,41 @@ async function trySetSnapshot(ctx: PluginContext) {
const url = snapshotId
? `https://webchem.ncbr.muni.cz/molstar-state/get/${snapshotId}`
: snapshotUrl;
await PluginCommands.State.Snapshots.Fetch.dispatch(ctx, { url })
await PluginCommands.State.Snapshots.Fetch(ctx, { url })
} catch (e) {
ctx.log.error('Failed to load snapshot.');
console.warn('Failed to load snapshot', e);
}
}
async function tryLoadFromUrl(ctx: PluginContext) {
const url = getParam('loadFromURL', '[^&]+').trim();
try {
if (!url) return;
let format = 'cif', isBinary = false;
switch (getParam('loadFromURLFormat', '[a-z]+').toLocaleLowerCase().trim()) {
case 'pdb': format = 'pdb'; break;
case 'mmbcif': isBinary = true; break;
}
const params = DownloadStructure.createDefaultParams(void 0 as any, ctx);
return ctx.runTask(ctx.state.data.applyAction(DownloadStructure, {
source: {
name: 'url',
params: {
url,
format: format as any,
isBinary,
options: params.source.params.options,
}
}
}));
} catch (e) {
ctx.log.error(`Failed to load from URL (${url})`);
console.warn(`Failed to load from URL (${url})`, e);
}
}
init();

View File

@@ -94,13 +94,13 @@ export function createProteopediaCustomTheme(colors: number[]) {
}
}
const ProteopediaCustomColorThemeProvider: ColorTheme.Provider<ProteopediaCustomColorThemeParams> = {
return {
name: 'proteopedia-custom',
label: 'Proteopedia Custom',
category: 'Custom',
factory: ProteopediaCustomColorTheme,
getParams: getChainIdColorThemeParams,
defaultValues: PD.getDefaultValues(ProteopediaCustomColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure
}
return ProteopediaCustomColorThemeProvider;
}

View File

@@ -5,9 +5,9 @@
*/
import { ResidueIndex, Model } from '../../mol-model/structure';
import { BuiltInStructureRepresentationsName } from '../../mol-repr/structure/registry';
import { BuiltInColorThemeName } from '../../mol-theme/color';
import { AminoAcidNames } from '../../mol-model/structure/model/types';
import { StructureRepresentationRegistry } from '../../mol-repr/structure/registry';
import { ColorTheme } from '../../mol-theme/color';
import { PolymerType } from '../../mol-model/structure/model/types';
import { PluginContext } from '../../mol-plugin/context';
import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
@@ -54,15 +54,14 @@ export namespace ModelInfo {
const hetMap = new Map<string, ModelInfo['hetResidues'][0]>();
for (let rI = 0 as ResidueIndex; rI < residueCount; rI++) {
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
if (AminoAcidNames.has(comp_id)) continue;
const mod_parent = model.properties.modifiedResidues.parentId.get(comp_id);
if (mod_parent && AminoAcidNames.has(mod_parent)) continue;
if (model.atomicHierarchy.derived.residue.polymerType[rI] !== PolymerType.NA) continue;
const cI = chainIndex[residueOffsets[rI]];
const eI = model.atomicHierarchy.index.getEntityFromChain(cI);
if (model.entities.data.type.value(eI) === 'water') continue;
const comp_id = model.atomicHierarchy.residues.label_comp_id.value(rI);
let lig = hetMap.get(comp_id);
if (!lig) {
lig = { name: comp_id, indices: [] };
@@ -99,7 +98,7 @@ export interface RepresentationStyle {
}
export namespace RepresentationStyle {
export type Entry = { hide?: boolean, kind?: BuiltInStructureRepresentationsName, coloring?: BuiltInColorThemeName }
export type Entry = { hide?: boolean, kind?: StructureRepresentationRegistry.BuiltIn, coloring?: ColorTheme.BuiltIn }
}
export enum StateElements {

View File

@@ -8,35 +8,32 @@ import * as ReactDOM from 'react-dom';
import { createPlugin, DefaultPluginSpec } from '../../mol-plugin';
import './index.html'
import { PluginContext } from '../../mol-plugin/context';
import { PluginCommands } from '../../mol-plugin/command';
import { StateTransforms } from '../../mol-plugin/state/transforms';
import { StructureRepresentation3DHelpers } from '../../mol-plugin/state/transforms/representation';
import { PluginCommands } from '../../mol-plugin/commands';
import { StateTransforms } from '../../mol-plugin-state/transforms';
import { Color } from '../../mol-util/color';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin/state/objects';
import { AnimateModelIndex } from '../../mol-plugin/state/animation/built-in';
import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
import { StateBuilder, StateObject, StateSelection } from '../../mol-state';
import { EvolutionaryConservation } from './annotation';
import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo, StateElements } from './helpers';
import { RxEventHelper } from '../../mol-util/rx-event-helper';
import { ControlsWrapper, volumeStreamingControls } from './ui/controls';
import { volumeStreamingControls } from './ui/controls';
import { PluginState } from '../../mol-plugin/state';
import { Scheduler } from '../../mol-task';
import { createProteopediaCustomTheme } from './coloring';
import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
import { BuiltInStructureRepresentations } from '../../mol-repr/structure/registry';
import { BuiltInColorThemes } from '../../mol-theme/color';
import { BuiltInSizeThemes } from '../../mol-theme/size';
import { ColorNames } from '../../mol-util/color/names';
import { InitVolumeStreaming, CreateVolumeStreamingInfo } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
import { DefaultCanvas3DParams, Canvas3DProps } from '../../mol-canvas3d/canvas3d';
import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
// 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-ui/skin/light.scss')
class MolStarProteopediaWrapper {
static VERSION_MAJOR = 3;
static VERSION_MINOR = 4;
static VERSION_MAJOR = 4;
static VERSION_MINOR = 0;
private _ev = RxEventHelper.create();
@@ -58,23 +55,23 @@ class MolStarProteopediaWrapper {
initial: {
isExpanded: false,
showControls: false
},
controls: {
right: ControlsWrapper
}
},
components: {
remoteState: 'none'
}
});
const customColoring = createProteopediaCustomTheme((options && options.customColorList) || []);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add('proteopedia-custom', customColoring);
this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.propertyProvider.descriptor.name, EvolutionaryConservation.colorThemeProvider!);
this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
this.plugin.representation.structure.themes.colorThemeRegistry.add(customColoring);
this.plugin.representation.structure.themes.colorThemeRegistry.add(EvolutionaryConservation.colorThemeProvider!);
this.plugin.managers.lociLabels.addProvider(EvolutionaryConservation.labelProvider!);
this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider, true);
}
get state() {
return this.plugin.state.dataState;
return this.plugin.state.data;
}
private download(b: StateBuilder.To<PSO.Root>, url: string) {
@@ -92,10 +89,16 @@ class MolStarProteopediaWrapper {
private structure(assemblyId: string) {
const model = this.state.build().to(StateElements.Model);
const props = {
type: {
name: 'assembly' as const,
params: { id: assemblyId || 'deposited' }
}
}
const s = model
.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 });
.apply(StateTransforms.Model.StructureFromModel, props, { ref: StateElements.Assembly });
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: StateElements.Sequence });
s.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: StateElements.Het });
@@ -118,9 +121,10 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.SequenceVisual);
} else {
root.applyOrUpdate(StateElements.SequenceVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.sequence && style.sequence.kind) || 'cartoon',
(style.sequence && style.sequence.coloring) || 'unit-index', structure));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.sequence && style.sequence.kind) || 'cartoon',
color: (style.sequence && style.sequence.coloring) || 'unit-index'
}));
}
}
@@ -133,9 +137,10 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.HetVisual);
} else {
root.applyOrUpdate(StateElements.HetVisual, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
(style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
(style.hetGroups && style.hetGroups.coloring), structure));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
color: style.hetGroups && style.hetGroups.coloring
}));
}
}
}
@@ -149,7 +154,7 @@ class MolStarProteopediaWrapper {
root.delete(StateElements.Het3DSNFG);
} else {
root.applyOrUpdate(StateElements.Het3DSNFG, StateTransforms.Representation.StructureRepresentation3D,
StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin, 'carbohydrate', void 0, structure));
createStructureRepresentationParams(this.plugin, structure, { type: 'carbohydrate' }));
}
}
}
@@ -160,9 +165,11 @@ 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 }));
createStructureRepresentationParams(this.plugin, structure, {
type: (style.water && style.water.kind) || 'ball-and-stick',
typeParams: { alpha: 0.51 },
color: style.water && style.water.coloring
}));
}
}
@@ -186,14 +193,14 @@ class MolStarProteopediaWrapper {
}
private applyState(tree: StateBuilder) {
return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
return PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
async load({ url, format = 'cif', assemblyId = 'deposited', representationStyle }: LoadParams) {
let loadType: 'full' | 'update' = 'full';
const state = this.plugin.state.dataState;
const state = this.plugin.state.data;
if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
loadType = 'full';
@@ -202,7 +209,7 @@ class MolStarProteopediaWrapper {
}
if (loadType === 'full') {
await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
const modelTree = this.model(this.download(state.build().toRoot(), url), format);
await this.applyState(modelTree);
const info = await this.doInfo(true);
@@ -213,39 +220,45 @@ class MolStarProteopediaWrapper {
const tree = state.build();
const info = await this.doInfo(true);
const asmId = (assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId;
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: asmId }));
const props = {
type: {
name: 'assembly' as const,
params: { id: asmId || 'deposited' }
}
}
tree.to(StateElements.Assembly).update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
await this.applyState(tree);
}
await this.updateStyle(representationStyle);
this.loadedParams = { url, format, assemblyId };
Scheduler.setImmediate(() => PluginCommands.Camera.Reset.dispatch(this.plugin, { }));
Scheduler.setImmediate(() => PluginCommands.Camera.Reset(this.plugin, { }));
}
async updateStyle(style?: RepresentationStyle, partial?: boolean) {
const tree = this.visual(style, partial);
if (!tree) return;
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.data, tree });
}
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) } } });
PluginCommands.Canvas3D.SetSettings(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 } } });
if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
}
viewport = {
setSettings: (settings?: Canvas3DProps) => {
PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
PluginCommands.Canvas3D.SetSettings(this.plugin, {
settings: settings || DefaultCanvas3DParams
});
}
@@ -253,10 +266,10 @@ class MolStarProteopediaWrapper {
camera = {
toggleSpin: () => this.toggleSpin(),
resetPosition: () => PluginCommands.Camera.Reset.dispatch(this.plugin, { }),
resetPosition: () => PluginCommands.Camera.Reset(this.plugin, { }),
// setClip: (options?: { distance?: number, near?: number, far?: number }) => {
// if (!options) {
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: {
// cameraClipDistance: DefaultCanvas3DParams.cameraClipDistance,
// clip: DefaultCanvas3DParams.clip
@@ -269,7 +282,7 @@ class MolStarProteopediaWrapper {
// const props = this.plugin.canvas3d.props;
// const clipNear = typeof options.near === 'undefined' ? props.clip[0] : options.near;
// const clipFar = typeof options.far === 'undefined' ? props.clip[1] : options.far;
// PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, {
// PluginCommands.Canvas3D.SetSettings(this.plugin, {
// settings: { cameraClipDistance: options.distance, clip: [clipNear, clipFar] }
// });
// }
@@ -299,7 +312,7 @@ class MolStarProteopediaWrapper {
// }
const tree = state.build();
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
const colorTheme = { name: EvolutionaryConservation.propertyProvider.descriptor.name, params: this.plugin.representation.structure.themes.colorThemeRegistry.get(EvolutionaryConservation.propertyProvider.descriptor.name).defaultValues };
if (!params || !!params.sequence) {
tree.to(StateElements.SequenceVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
@@ -308,7 +321,7 @@ class MolStarProteopediaWrapper {
tree.to(StateElements.HetVisual).update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
}
await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
await PluginCommands.State.Update(this.plugin, { state, tree });
}
}
@@ -328,7 +341,7 @@ class MolStarProteopediaWrapper {
remove: () => {
const r = this.state.select(StateSelection.Generators.ofTransformer(CreateVolumeStreamingInfo))[0];
if (!r) return;
PluginCommands.State.RemoveObject.dispatch(this.plugin, { state: this.state, ref: r.transform.ref });
PluginCommands.State.RemoveObject(this.plugin, { state: this.state, ref: r.transform.ref });
if (this.experimentalDataElement) {
ReactDOM.unmountComponentAtNode(this.experimentalDataElement);
this.experimentalDataElement = void 0;
@@ -339,12 +352,12 @@ class MolStarProteopediaWrapper {
hetGroups = {
reset: () => {
const update = this.state.build().delete(StateElements.HetGroupFocusGroup);
PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset.dispatch(this.plugin, { });
PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
PluginCommands.Camera.Reset(this.plugin, { });
},
focusFirst: async (compId: string) => {
if (!this.state.transforms.has(StateElements.Assembly)) return;
await PluginCommands.Camera.Reset.dispatch(this.plugin, { });
await PluginCommands.Camera.Reset(this.plugin, { });
// const asm = (this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure).data;
@@ -377,7 +390,7 @@ class MolStarProteopediaWrapper {
// }
// });
await PluginCommands.State.Update.dispatch(this.plugin, { state: this.state, tree: update });
await PluginCommands.State.Update(this.plugin, { state: this.state, tree: update });
const focus = (this.state.select(StateElements.HetGroupFocus)[0].obj as PluginStateObject.Molecule.Structure).data;
const sphere = focus.boundary.sphere;
@@ -386,26 +399,24 @@ class MolStarProteopediaWrapper {
// Vec3.normalize(position, position);
// Vec3.scaleAndAdd(position, sphere.center, position, sphere.radius);
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 });
const snapshot = this.plugin.canvas3d!.camera.getFocus(sphere.center, radius);
PluginCommands.Camera.SetSnapshot(this.plugin, { snapshot, durationMs: 250 });
}
}
private createSurVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
repr: BuiltInStructureRepresentations['ball-and-stick'],
color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
return createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick',
color: 'uniform', colorParams: { value: ColorNames.gray },
size: 'uniform', sizeParams: { value: 0.33 }
});
}
private createCoreVisualParams() {
const asm = this.state.select(StateElements.Assembly)[0].obj as PluginStateObject.Molecule.Structure;
return StructureRepresentation3DHelpers.createParams(this.plugin, asm.data, {
repr: BuiltInStructureRepresentations['ball-and-stick'],
// color: [BuiltInColorThemes.uniform, () => ({ value: ColorNames.gray })],
// size: [BuiltInSizeThemes.uniform, () => ({ value: 0.33 } )]
return createStructureRepresentationParams(this.plugin, asm.data, {
type: 'ball-and-stick'
});
}

View File

@@ -6,24 +6,11 @@
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 { PluginContext } from '../../../mol-plugin/context';
import { PluginContextContainer } from '../../../mol-plugin-ui/plugin';
import { TransformUpdaterControl } from '../../../mol-plugin-ui/state/update-transform';
import { PluginContext } from '../../../mol-plugin/context';
import { StateElements } from '../helpers';
export class ControlsWrapper extends PluginUIComponent {
render() {
return <div className='msp-scrollable-container msp-right-controls'>
<CurrentObject />
<AnimationControls />
<CameraSnapshots />
</div>;
}
}
export function volumeStreamingControls(plugin: PluginContext, parent: Element) {
ReactDOM.render(<PluginContextContainer plugin={plugin}>
<TransformUpdaterControl nodeRef={StateElements.VolumeStreaming} />

View File

@@ -8,6 +8,7 @@
import { Mat4, Vec3, Vec4, EPSILON } from '../mol-math/linear-algebra'
import { Viewport, cameraProject, cameraUnproject } from './camera/util';
import { CameraTransitionManager } from './camera/transition';
import { BehaviorSubject } from 'rxjs';
export { Camera }
@@ -33,6 +34,7 @@ class Camera {
zoom = 1
readonly transition: CameraTransitionManager = new CameraTransitionManager(this);
readonly stateChanged = new BehaviorSubject<Partial<Camera.Snapshot>>(this.state);
get position() { return this.state.position; }
set position(v: Vec3) { Vec3.copy(this.state.position, v); }
@@ -76,18 +78,20 @@ class Camera {
setState(snapshot: Partial<Camera.Snapshot>, durationMs?: number) {
this.transition.apply(snapshot, durationMs);
this.stateChanged.next(snapshot);
}
getSnapshot() {
return Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state);
}
getFocus(target: Vec3, radiusNear: number, radiusFar: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
const fov = this.state.fov
getFocus(target: Vec3, radius: number, up?: Vec3, dir?: Vec3): Partial<Camera.Snapshot> {
const r = Math.max(radius, 0.01)
const { fov } = this.state
const { width, height } = this.viewport
const aspect = width / height
const aspectFactor = (height < width ? 1 : aspect)
const targetDistance = Math.abs((radiusNear / aspectFactor) / Math.sin(fov / 2))
const targetDistance = Math.abs((r / aspectFactor) / Math.sin(fov / 2))
Vec3.sub(this.deltaDirection, this.target, this.position)
if (dir) Vec3.matchDirection(this.deltaDirection, dir, this.deltaDirection)
@@ -96,17 +100,16 @@ class Camera {
const state = Camera.copySnapshot(Camera.createDefaultSnapshot(), this.state)
state.target = Vec3.clone(target)
state.radiusNear = radiusNear
state.radiusFar = radiusFar
state.radius = r
state.position = Vec3.clone(this.newPosition)
if (up) Vec3.matchDirection(state.up, up, state.up)
return state
}
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);
focus(target: Vec3, radius: number, durationMs?: number, up?: Vec3, dir?: Vec3) {
if (radius > 0) {
this.setState(this.getFocus(target, radius, up, dir), durationMs);
}
}
@@ -161,8 +164,8 @@ namespace Camera {
up: Vec3.create(0, 1, 0),
target: Vec3.create(0, 0, 0),
radiusNear: 10,
radiusFar: 10,
radius: 10,
radiusMax: 10,
fog: 50,
clipFar: true
};
@@ -176,8 +179,8 @@ namespace Camera {
up: Vec3
target: Vec3
radiusNear: number
radiusFar: number
radius: number
radiusMax: number
fog: number
clipFar: boolean
}
@@ -192,8 +195,8 @@ 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.radiusNear !== 'undefined') out.radiusNear = source.radiusNear;
if (typeof source.radiusFar !== 'undefined') out.radiusFar = source.radiusFar;
if (typeof source.radius !== 'undefined') out.radius = source.radius;
if (typeof source.radiusMax !== 'undefined') out.radiusMax = source.radiusMax;
if (typeof source.fog !== 'undefined') out.fog = source.fog;
if (typeof source.clipFar !== 'undefined') out.clipFar = source.clipFar;
@@ -262,14 +265,16 @@ function updatePers(camera: Camera) {
}
function updateClip(camera: Camera) {
const { radiusNear, radiusFar, mode, fog, clipFar } = camera.state
let { radius, radiusMax, mode, fog, clipFar } = camera.state
if (radius < 0.01) radius = 0.01
const cDist = Vec3.distance(camera.position, camera.target)
let near = cDist - radiusNear
let far = cDist + (clipFar ? radiusNear : radiusFar)
const normalizedFar = clipFar ? radius : radiusMax
const cameraDistance = Vec3.distance(camera.position, camera.target)
let near = cameraDistance - radius
let far = cameraDistance + normalizedFar
const fogNearFactor = -(50 - fog) / 50
let fogNear = cDist - (radiusNear * fogNearFactor)
let fogNear = cameraDistance - (normalizedFar * fogNearFactor)
let fogFar = far
if (mode === 'perspective') {

View File

@@ -17,19 +17,26 @@ class CameraTransitionManager {
private start = 0;
inTransition = false;
private durationMs = 0;
private source: Camera.Snapshot = Camera.createDefaultSnapshot();
private target: Camera.Snapshot = Camera.createDefaultSnapshot();
private current = Camera.createDefaultSnapshot();
private _source: Camera.Snapshot = Camera.createDefaultSnapshot();
private _target: Camera.Snapshot = Camera.createDefaultSnapshot();
private _current = Camera.createDefaultSnapshot();
get source(): Readonly<Camera.Snapshot> { return this._source }
get target(): Readonly<Camera.Snapshot> { return this._target }
apply(to: Partial<Camera.Snapshot>, durationMs: number = 0, transition?: CameraTransitionManager.TransitionFunc) {
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
this.finish(to);
return;
Camera.copySnapshot(this._source, this.camera.state);
Camera.copySnapshot(this._target, this.camera.state);
Camera.copySnapshot(this._target, to);
if (this._target.radius > this._target.radiusMax) {
this._target.radius = this._target.radiusMax
}
Camera.copySnapshot(this.source, this.camera.state);
Camera.copySnapshot(this.target, this.camera.state);
Camera.copySnapshot(this.target, to);
if (durationMs <= 0 || (typeof to.mode !== 'undefined' && to.mode !== this.camera.state.mode)) {
this.finish(this._target);
return;
}
this.inTransition = true;
this.func = transition || CameraTransitionManager.defaultTransition;
@@ -52,12 +59,12 @@ class CameraTransitionManager {
const normalized = Math.min((this.t - this.start) / this.durationMs, 1);
if (normalized === 1) {
this.finish(this.target!);
this.finish(this._target!);
return;
}
this.func(this.current, normalized, this.source, this.target);
Camera.copySnapshot(this.camera.state, this.current);
this.func(this._current, normalized, this._source, this._target);
Camera.copySnapshot(this.camera.state, this._current);
}
constructor(private camera: Camera) {
@@ -79,9 +86,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.radiusNear = lerp(source.radiusNear, target.radiusNear, t);
out.radius = lerp(source.radius, target.radius, t);
// TODO take change of `clipFar` into account
out.radiusFar = lerp(source.radiusFar, target.radiusFar, t);
out.radiusMax = lerp(source.radiusMax, target.radiusMax, t);
// Lerp fov & fog
out.fov = lerp(source.fov, target.fov, t);

View File

@@ -37,9 +37,18 @@ 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),
cameraMode: PD.Select('perspective', [['perspective', 'Perspective'], ['orthographic', 'Orthographic']] as const),
cameraFog: PD.MappedStatic('on', {
on: PD.Group({
intensity: PD.Numeric(50, { min: 1, max: 100, step: 1 }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Show fog in the distance' }),
cameraClipping: PD.Group({
radius: PD.Numeric(100, { min: 0, max: 99, step: 1 }, { label: 'Clipping', description: 'How much of the scene to show.' }),
far: PD.Boolean(true, { description: 'Hide scene in the distance' }),
}, { pivot: 'radius' }),
cameraResetDurationMs: PD.Numeric(250, { min: 0, max: 1000, step: 1 }, { description: 'The time it takes to reset the camera.' }),
transparentBackground: PD.Boolean(false),
@@ -62,9 +71,10 @@ interface Canvas3D {
/**
* This function must be called if animate() is not set up so that add/remove actions take place.
*/
commit(): void
commit(isSynchronous?: boolean): void
update(repr?: Representation.Any, keepBoundingSphere?: boolean): void
clear(): void
syncVisibility(): void
requestDraw(force?: boolean): void
animate(): void
@@ -77,7 +87,7 @@ interface Canvas3D {
handleResize(): void
/** Focuses camera on scene's bounding sphere, centered and zoomed. */
requestCameraReset(): void
requestCameraReset(options?: { durationMs?: number, snapshot?: Partial<Camera.Snapshot> }): void
readonly camera: Camera
readonly boundingSphere: Readonly<Sphere3D>
downloadScreenshot(): void
@@ -169,8 +179,8 @@ namespace Canvas3D {
const camera = new Camera({
position: Vec3.create(0, 0, 100),
mode: p.cameraMode,
fog: p.cameraFog,
clipFar: p.cameraClipFar
fog: p.cameraFog.name === 'on' ? p.cameraFog.params.intensity : 0,
clipFar: p.cameraClipping.far
})
const controls = TrackballControls.create(input, camera, p.trackball)
@@ -190,6 +200,8 @@ namespace Canvas3D {
let drawPending = false
let cameraResetRequested = false
let nextCameraResetDuration: number | undefined = void 0
let nextCameraResetSnapshot: Partial<Camera.Snapshot> | undefined = void 0
function getLoci(pickingId: PickingId) {
let loci: Loci = EmptyLoci
@@ -266,9 +278,7 @@ namespace Canvas3D {
function animate() {
currentTime = now();
commit();
camera.transition.tick(currentTime);
draw(false);
@@ -282,25 +292,60 @@ namespace Canvas3D {
return webgl.isContextLost ? undefined : pickPass.identify(x, y)
}
function commit() {
commitScene();
resolveCameraReset();
function commit(isSynchronous: boolean = false) {
const allCommited = commitScene(isSynchronous);
// Only reset the camera after the full scene has been commited.
if (allCommited) resolveCameraReset();
}
function resolveCameraReset() {
if (!cameraResetRequested) return;
const { center, radius } = scene.boundingSphere;
camera.focus(center, radius, radius, p.cameraResetDurationMs);
if (radius > 0) {
const duration = nextCameraResetDuration === undefined ? p.cameraResetDurationMs : nextCameraResetDuration
const focus = camera.getFocus(center, radius);
const snapshot = nextCameraResetSnapshot ? { ...focus, ...nextCameraResetSnapshot } : focus;
camera.setState(snapshot, duration);
}
nextCameraResetDuration = void 0;
nextCameraResetSnapshot = void 0;
cameraResetRequested = false;
}
const sceneCommitTimeoutMs = 250;
function commitScene() {
if (!scene.needsCommit) return;
const oldBoundary = Sphere3D.zero();
function shouldResetCamera() {
if (camera.state.radiusMax === 0) return true;
// check if any renderable center has moved outside of the old boundary
for (const r of scene.renderables) {
if (!r.state.visible) continue;
const { center, radius } = r.values.boundingSphere.ref.value;
if (!radius) continue;
// TODO: include renderable radius into this?
if (Vec3.distance(oldBoundary.center, center) > 1.1 * oldBoundary.radius) {
return true;
}
}
return false;
}
const sceneCommitTimeoutMs = 250;
function commitScene(isSynchronous: boolean) {
if (!scene.needsCommit) return true;
// snapshot the current bounding sphere
Sphere3D.copy(oldBoundary, scene.boundingSphere);
if (!scene.commit(isSynchronous ? void 0 : sceneCommitTimeoutMs)) return false;
const allCommited = scene.commit(sceneCommitTimeoutMs);
if (debugHelper.isEnabled) debugHelper.update();
if (allCommited) reprCount.next(reprRenderObjects.size);
if (reprCount.value === 0 || shouldResetCamera()) cameraResetRequested = true;
camera.setState({ radiusMax: scene.boundingSphere.radius })
reprCount.next(reprRenderObjects.size);
return true;
}
function add(repr: Representation.Any) {
@@ -375,6 +420,12 @@ namespace Canvas3D {
requestDraw(true)
reprCount.next(reprRenderObjects.size)
},
syncVisibility: () => {
if (scene.syncVisibility()) {
camera.setState({ radiusMax: scene.boundingSphere.radius })
if (debugHelper.isEnabled) debugHelper.update()
}
},
// draw,
requestDraw,
@@ -384,7 +435,9 @@ namespace Canvas3D {
getLoci,
handleResize,
requestCameraReset: () => {
requestCameraReset: options => {
nextCameraResetDuration = options?.durationMs;
nextCameraResetSnapshot = options?.snapshot;
cameraResetRequested = true;
},
camera,
@@ -404,15 +457,28 @@ namespace Canvas3D {
didDraw,
reprCount,
setProps: (props: Partial<Canvas3DProps>) => {
const cameraState: Partial<Camera.Snapshot> = Object.create(null)
if (props.cameraMode !== undefined && props.cameraMode !== camera.state.mode) {
camera.setState({ mode: props.cameraMode })
cameraState.mode = props.cameraMode
}
if (props.cameraFog !== undefined && props.cameraFog !== camera.state.fog) {
camera.setState({ fog: props.cameraFog })
if (props.cameraFog !== undefined) {
const newFog = props.cameraFog.name === 'on' ? props.cameraFog.params.intensity : 0
if (newFog !== camera.state.fog) cameraState.fog = newFog
}
if (props.cameraClipFar !== undefined && props.cameraClipFar !== camera.state.clipFar) {
camera.setState({ clipFar: props.cameraClipFar })
if (props.cameraClipping !== undefined) {
if (props.cameraClipping.far !== undefined && props.cameraClipping.far !== camera.state.clipFar) {
cameraState.clipFar = props.cameraClipping.far
}
if (props.cameraClipping.radius !== undefined) {
const radius = (scene.boundingSphere.radius / 100) * (100 - props.cameraClipping.radius)
if (radius > 0 && radius !== cameraState.radius) {
// if radius = 0, NaNs happen
cameraState.radius = Math.max(radius, 0.01)
}
}
}
if (Object.keys(cameraState).length > 0) camera.setState(cameraState)
if (props.cameraResetDurationMs !== undefined) p.cameraResetDurationMs = props.cameraResetDurationMs
if (props.transparentBackground !== undefined) p.transparentBackground = props.transparentBackground
@@ -421,6 +487,7 @@ namespace Canvas3D {
if (props.renderer) renderer.setProps(props.renderer)
if (props.trackball) controls.setProps(props.trackball)
if (props.debug) debugHelper.setProps(props.debug)
requestDraw(true)
},
getImagePass: (props: Partial<ImageProps> = {}) => {
@@ -428,10 +495,16 @@ namespace Canvas3D {
},
get props() {
const radius = scene.boundingSphere.radius > 0
? 100 - Math.round((camera.transition.target.radius / scene.boundingSphere.radius) * 100)
: 0
return {
cameraMode: camera.state.mode,
cameraFog: camera.state.fog,
cameraClipFar: camera.state.clipFar,
cameraFog: camera.state.fog > 0
? { name: 'on' as const, params: { intensity: camera.state.fog } }
: { name: 'off' as const, params: {} },
cameraClipping: { far: camera.state.clipFar, radius },
cameraResetDurationMs: p.cameraResetDurationMs,
transparentBackground: p.transparentBackground,

View File

@@ -21,15 +21,15 @@ const M = ModifiersKeys
const Trigger = Binding.Trigger
export const DefaultTrackballBindings = {
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate the 3D scene by dragging using ${triggers}'),
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate the 3D scene around the z-axis by dragging using ${triggers}'),
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan the 3D scene by dragging using ${triggers}'),
dragRotate: Binding([Trigger(B.Flag.Primary, M.create())], 'Rotate', 'Drag using ${triggers}'),
dragRotateZ: Binding([Trigger(B.Flag.Primary, M.create({ shift: true }))], 'Rotate around z-axis', 'Drag using ${triggers}'),
dragPan: Binding([Trigger(B.Flag.Secondary, M.create()), Trigger(B.Flag.Primary, M.create({ control: true }))], 'Pan', 'Drag using ${triggers}'),
dragZoom: Binding.Empty,
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus the 3D scene by dragging using ${triggers}'),
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom the 3D scene by dragging using ${triggers}'),
dragFocus: Binding([Trigger(B.Flag.Forth, M.create())], 'Focus', 'Drag using ${triggers}'),
dragFocusZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Focus and zoom', 'Drag using ${triggers}'),
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom the 3D scene by scrolling using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Focus the 3D scene by scrolling using ${triggers}'),
scrollZoom: Binding([Trigger(B.Flag.Auxilary, M.create())], 'Zoom', 'Scroll using ${triggers}'),
scrollFocus: Binding([Trigger(B.Flag.Auxilary, M.create({ shift: true }))], 'Clip', 'Scroll using ${triggers}'),
scrollFocusZoom: Binding.Empty,
}
@@ -41,7 +41,7 @@ export const TrackballControlsParams = {
panSpeed: PD.Numeric(0.8, { min: 0.1, max: 5, step: 0.1 }),
spin: PD.Boolean(false, { description: 'Spin the 3D scene around the x-axis in view space' }),
spinSpeed: PD.Numeric(1, { min: -100, max: 100, step: 1 }),
spinSpeed: PD.Numeric(1, { min: -20, max: 20, step: 1 }),
staticMoving: PD.Boolean(true, { isHidden: true }),
dynamicDampingFactor: PD.Numeric(0.2, {}, { isHidden: true }),
@@ -208,8 +208,8 @@ namespace TrackballControls {
function focusCamera() {
const factor = (_focusEnd[1] - _focusStart[1]) * p.zoomSpeed
if (factor !== 0.0) {
const radiusNear = Math.max(1, camera.state.radiusNear + 10 * factor)
camera.setState({ radiusNear })
const radius = Math.max(1, camera.state.radius + 10 * factor)
camera.setState({ radius })
}
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({ radiusNear: dist / 5 })
camera.setState({ radius: dist / 5 })
}
if (dragPan) Vec2.copy(_panEnd, mouseOnScreenVec2)
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -45,27 +45,25 @@ export class BoundingSphereHelper {
}
update() {
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.grey)
const newSceneData = updateBoundingSphereData(this.scene, this.parent.boundingSphere, this.sceneData, ColorNames.grey, sceneMaterialId)
if (newSceneData) this.sceneData = newSceneData
this.parent.forEach((r, ro) => {
const objectData = this.objectsData.get(ro)
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato)
const newObjectData = updateBoundingSphereData(this.scene, r.values.boundingSphere.ref.value, objectData, ColorNames.tomato, objectMaterialId)
if (newObjectData) this.objectsData.set(ro, newObjectData)
if (ro.type === 'mesh' || ro.type === 'lines' || ro.type === 'points') {
const instanceData = this.instancesData.get(ro)
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, {
aTransform: ro.values.aTransform,
matrix: ro.values.matrix,
transform: ro.values.transform,
extraTransform: ro.values.extraTransform,
uInstanceCount: ro.values.uInstanceCount,
instanceCount: ro.values.instanceCount,
aInstance: ro.values.aInstance,
})
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
}
const instanceData = this.instancesData.get(ro)
const newInstanceData = updateBoundingSphereData(this.scene, r.values.invariantBoundingSphere.ref.value, instanceData, ColorNames.skyblue, instanceMaterialId, {
aTransform: ro.values.aTransform,
matrix: ro.values.matrix,
transform: ro.values.transform,
extraTransform: ro.values.extraTransform,
uInstanceCount: ro.values.uInstanceCount,
instanceCount: ro.values.instanceCount,
aInstance: ro.values.aInstance,
})
if (newInstanceData) this.instancesData.set(ro, newInstanceData)
})
this.objectsData.forEach((objectData, ro) => {
@@ -116,10 +114,10 @@ export class BoundingSphereHelper {
}
}
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, transform?: TransformData) {
function updateBoundingSphereData(scene: Scene, boundingSphere: Sphere3D, data: BoundingSphereData | undefined, color: Color, materialId: number, transform?: TransformData) {
if (!data || !Sphere3D.equals(data.boundingSphere, boundingSphere)) {
const mesh = createBoundingSphereMesh(boundingSphere, data && data.mesh)
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, transform)
const renderObject = data ? data.renderObject : createBoundingSphereRenderObject(mesh, color, materialId, transform)
if (data) {
ValueCell.update(renderObject.values.drawCount, Geometry.getDrawCount(mesh))
} else {
@@ -133,12 +131,20 @@ function createBoundingSphereMesh(boundingSphere: Sphere3D, mesh?: Mesh) {
const detail = 2
const vertexCount = sphereVertexCount(detail)
const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh)
if (boundingSphere.radius) addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
if (boundingSphere.radius) {
addSphere(builderState, boundingSphere.center, boundingSphere.radius, detail)
if (Sphere3D.hasExtrema(boundingSphere)) {
for (const e of boundingSphere.extrema) addSphere(builderState, e, 1.0, 0)
}
}
return MeshBuilder.getMesh(builderState)
}
const boundingSphereHelberMaterialId = getNextMaterialId()
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, transform?: TransformData) {
const sceneMaterialId = getNextMaterialId()
const objectMaterialId = getNextMaterialId()
const instanceMaterialId = getNextMaterialId()
function createBoundingSphereRenderObject(mesh: Mesh, color: Color, materialId: number, transform?: TransformData) {
const values = Mesh.Utils.createValuesSimple(mesh, { alpha: 0.1, doubleSided: false }, color, 1, transform)
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, boundingSphereHelberMaterialId)
return createRenderObject('mesh', values, { visible: true, alphaFactor: 1, pickable: false, opaque: false }, materialId)
}

View File

@@ -40,7 +40,7 @@ function getComposeRenderable(ctx: WebGLContext, colorTexture: Texture): Compose
}
const schema = { ...ComposeSchema }
const shaderCode = ShaderCode(quad_vert, compose_frag)
const shaderCode = ShaderCode('compose', quad_vert, compose_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
return createComputeRenderable(renderItem, values)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -17,6 +17,7 @@ import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { RenderTarget } from '../../mol-gl/webgl/render-target';
import { DrawPass } from './draw';
import { Camera } from '../../mol-canvas3d/camera';
import { produce } from 'immer';
import quad_vert from '../../mol-gl/shader/quad.vert'
import postprocessing_frag from '../../mol-gl/shader/postprocessing.frag'
@@ -47,14 +48,21 @@ const PostprocessingSchema = {
}
export const PostprocessingParams = {
occlusionEnable: PD.Boolean(false),
occlusionKernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
occlusionBias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
occlusionRadius: PD.Numeric(32, { min: 0, max: 256, step: 1 }),
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 }),
occlusion: PD.MappedStatic('off', {
on: PD.Group({
kernelSize: PD.Numeric(4, { min: 1, max: 32, step: 1 }),
bias: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }),
radius: PD.Numeric(64, { min: 0, max: 256, step: 1 }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Darken occluded crevices with the ambient occlusion effect' }),
outline: PD.MappedStatic('off', {
on: PD.Group({
scale: PD.Numeric(1, { min: 0, max: 10, step: 1 }),
threshold: PD.Numeric(0.8, { min: 0, max: 1, step: 0.01 }),
}),
off: PD.Group({})
}, { cycle: true, description: 'Draw outline around 3D objects' })
}
export type PostprocessingProps = PD.Values<typeof PostprocessingParams>
@@ -75,20 +83,20 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
uFogFar: ValueCell.create(10000),
uFogColor: ValueCell.create(Vec3.create(1, 1, 1)),
dOcclusionEnable: ValueCell.create(p.occlusionEnable),
dOcclusionKernelSize: ValueCell.create(p.occlusionKernelSize),
uOcclusionBias: ValueCell.create(p.occlusionBias),
uOcclusionRadius: ValueCell.create(p.occlusionRadius),
dOcclusionEnable: ValueCell.create(p.occlusion.name === 'on'),
dOcclusionKernelSize: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.kernelSize : 4),
uOcclusionBias: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.bias : 0.5),
uOcclusionRadius: ValueCell.create(p.occlusion.name === 'on' ? p.occlusion.params.radius : 64),
dOutlineEnable: ValueCell.create(p.outlineEnable),
uOutlineScale: ValueCell.create(p.outlineScale * ctx.pixelRatio),
uOutlineThreshold: ValueCell.create(p.outlineThreshold),
dOutlineEnable: ValueCell.create(p.outline.name === 'on'),
uOutlineScale: ValueCell.create((p.outline.name === 'on' ? p.outline.params.scale : 1) * ctx.pixelRatio),
uOutlineThreshold: ValueCell.create(p.outline.name === 'on' ? p.outline.params.threshold : 0.8),
dPackedDepth: ValueCell.create(packedDepth),
}
const schema = { ...PostprocessingSchema }
const shaderCode = ShaderCode(quad_vert, postprocessing_frag)
const shaderCode = ShaderCode('postprocessing', quad_vert, postprocessing_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
return createComputeRenderable(renderItem, values)
@@ -108,7 +116,7 @@ export class PostprocessingPass {
}
get enabled() {
return this.props.occlusionEnable || this.props.outlineEnable
return this.props.occlusion.name === 'on' || this.props.outline.name === 'on'
}
setSize(width: number, height: number) {
@@ -117,35 +125,28 @@ export class PostprocessingPass {
}
setProps(props: Partial<PostprocessingProps>) {
if (props.occlusionEnable !== undefined) {
this.props.occlusionEnable = props.occlusionEnable
ValueCell.update(this.renderable.values.dOcclusionEnable, props.occlusionEnable)
}
if (props.occlusionKernelSize !== undefined) {
this.props.occlusionKernelSize = props.occlusionKernelSize
ValueCell.update(this.renderable.values.dOcclusionKernelSize, props.occlusionKernelSize)
}
if (props.occlusionBias !== undefined) {
this.props.occlusionBias = props.occlusionBias
ValueCell.update(this.renderable.values.uOcclusionBias, props.occlusionBias)
}
if (props.occlusionRadius !== undefined) {
this.props.occlusionRadius = props.occlusionRadius
ValueCell.update(this.renderable.values.uOcclusionRadius, props.occlusionRadius)
}
this.props = produce(this.props, p => {
if (props.occlusion !== undefined) {
p.occlusion.name = props.occlusion.name
ValueCell.updateIfChanged(this.renderable.values.dOcclusionEnable, props.occlusion.name === 'on')
if (props.occlusion.name === 'on') {
p.occlusion.params = { ...props.occlusion.params }
ValueCell.updateIfChanged(this.renderable.values.dOcclusionKernelSize, props.occlusion.params.kernelSize)
ValueCell.updateIfChanged(this.renderable.values.uOcclusionBias, props.occlusion.params.bias)
ValueCell.updateIfChanged(this.renderable.values.uOcclusionRadius, props.occlusion.params.radius)
}
}
if (props.outlineEnable !== undefined) {
this.props.outlineEnable = props.outlineEnable
ValueCell.update(this.renderable.values.dOutlineEnable, props.outlineEnable)
}
if (props.outlineScale !== undefined) {
this.props.outlineScale = props.outlineScale
ValueCell.update(this.renderable.values.uOutlineScale, props.outlineScale * this.webgl.pixelRatio)
}
if (props.outlineThreshold !== undefined) {
this.props.outlineThreshold = props.outlineThreshold
ValueCell.update(this.renderable.values.uOutlineThreshold, props.outlineThreshold)
}
if (props.outline !== undefined) {
p.outline.name = props.outline.name
ValueCell.updateIfChanged(this.renderable.values.dOutlineEnable, props.outline.name === 'on')
if (props.outline.name === 'on') {
p.outline.params = { ...props.outline.params }
ValueCell.updateIfChanged(this.renderable.values.uOutlineScale, props.outline.params.scale)
ValueCell.updateIfChanged(this.renderable.values.uOutlineThreshold, props.outline.params.threshold)
}
}
})
this.renderable.update()
}

View File

@@ -9,7 +9,7 @@ import { sortArray } from '../util/sort'
import { StringBuilder } from '../../mol-util';
/** A collection of columns */
type Table<Schema extends Table.Schema> = {
type Table<Schema extends Table.Schema = any> = {
readonly _rowCount: number,
readonly _columns: ReadonlyArray<string>,
readonly _schema: Schema
@@ -73,7 +73,7 @@ namespace Table {
return ret;
}
export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Partial<Row<S>>>): R {
export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: S, rows: ArrayLike<Partial<Row<S>>>): R {
const ret = Object.create(null);
const rowCount = rows.length;
const columns = Object.keys(schema);
@@ -91,14 +91,19 @@ namespace Table {
return ret as R;
}
export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, arrays: Arrays<S>): R {
export function ofArrays<S extends Schema, R extends Table<S> = Table<S>>(schema: S, arrays: Partial<Arrays<S>>): R {
const ret = Object.create(null);
const columns = Object.keys(schema);
ret._rowCount = arrays[columns[0]].length;
ret._rowCount = 0;
ret._columns = columns;
ret._schema = schema;
for (const k of columns) {
(ret as any)[k] = typeof arrays[k] !== 'undefined' ? Column.ofArray({ array: arrays[k], schema: schema[k] }) : Column.Undefined(ret._rowCount, schema[k]);
if (typeof arrays[k] !== 'undefined') {
(ret as any)[k] = Column.ofArray({ array: arrays[k]!, schema: schema[k] });
ret._rowCount = arrays[k]?.length;
} else {
(ret as any)[k] = Column.Undefined(ret._rowCount, schema[k]);
}
}
return ret as R;
}
@@ -167,7 +172,7 @@ namespace Table {
}
/** Sort and return a new table */
export function sort<T extends Table<S>, S extends Schema>(table: T, cmp: (i: number, j: number) => number) {
export function sort<T extends Table>(table: T, cmp: (i: number, j: number) => number) {
const indices = new Int32Array(table._rowCount);
for (let i = 0, _i = indices.length; i < _i; i++) indices[i] = i;
sortArray(indices, (_, i, j) => cmp(i, j));
@@ -191,7 +196,7 @@ namespace Table {
return ret;
}
export function areEqual<T extends Table<Schema>>(a: T, b: T) {
export function areEqual<T extends Table<any>>(a: T, b: T) {
if (a._rowCount !== b._rowCount) return false;
if (a._columns.length !== b._columns.length) return false;
for (const c of a._columns) {

View File

@@ -28,18 +28,24 @@ export const VisualQualityInfo = {
'lowest': {},
}
export type VisualQuality = keyof typeof VisualQualityInfo
export const VisualQualityNames = Object.keys(VisualQualityInfo)
export const VisualQualityOptions = VisualQualityNames.map(n => [n, n] as [VisualQuality, string])
export const VisualQualityNames = Object.keys(VisualQualityInfo) as VisualQuality[]
export const VisualQualityOptions = PD.arrayToOptions(VisualQualityNames)
//
export namespace BaseGeometry {
export const Params = {
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions),
alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity', isEssential: true, description: 'How opaque/transparent the representation is rendered.' }),
quality: PD.Select<VisualQuality>('auto', VisualQualityOptions, { isEssential: true, description: 'Visual/rendering quality of the representation.' }),
}
export type Params = typeof Params
export const ShadingCategory: PD.Info = { category: 'Shading' };
export const CustomQualityParamInfo: PD.Info = {
category: 'Custom Quality',
hideIf: (params: PD.Values<Params>) => typeof params.quality !== 'undefined' && params.quality !== 'custom'
};
export type Counts = { drawCount: number, groupCount: number, instanceCount: number }
export function createSimple(colorValue = ColorNames.grey, sizeValue = 1, transform?: TransformData) {

View File

@@ -6,9 +6,7 @@
import { ValueCell } from '../../../mol-util'
import { Mat4 } from '../../../mol-math/linear-algebra'
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
GroupMapping,
createGroupMapping} from '../../util';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -50,6 +48,8 @@ export interface Lines {
readonly boundingSphere: Sphere3D
/** Maps group ids to line indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Lines {
@@ -129,6 +129,10 @@ export namespace Lines {
currentGroup = lines.groupBuffer.ref.version
}
return groupMapping
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere)
currentHash = hashCode(lines)
}
}
return lines

View File

@@ -45,6 +45,8 @@ export interface Mesh {
readonly boundingSphere: Sphere3D
/** Maps group ids to vertex indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Mesh {
@@ -101,6 +103,10 @@ export namespace Mesh {
currentGroup = mesh.groupBuffer.ref.version
}
return groupMapping
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere)
currentHash = hashCode(mesh)
}
}
return mesh
@@ -321,10 +327,10 @@ export namespace Mesh {
export const Params = {
...BaseGeometry.Params,
doubleSided: PD.Boolean(false),
flipSided: PD.Boolean(false),
flatShaded: PD.Boolean(false),
ignoreLight: PD.Boolean(false),
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
}
export type Params = typeof Params

View File

@@ -6,9 +6,7 @@
import { ValueCell } from '../../../mol-util'
import { Mat4 } from '../../../mol-math/linear-algebra'
import { transformPositionArray,/* , transformDirectionArray, getNormalMatrix */
GroupMapping,
createGroupMapping} from '../../util';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
@@ -43,6 +41,8 @@ export interface Points {
readonly boundingSphere: Sphere3D
/** Maps group ids to point indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Points {
@@ -92,6 +92,10 @@ export namespace Points {
currentGroup = points.groupBuffer.ref.version
}
return groupMapping
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere)
currentHash = hashCode(points)
}
}
return points

View File

@@ -42,6 +42,8 @@ export interface Spheres {
readonly boundingSphere: Sphere3D
/** Maps group ids to sphere indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Spheres {
@@ -97,6 +99,10 @@ export namespace Spheres {
currentGroup = spheres.groupBuffer.ref.version
}
return groupMapping
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere)
currentHash = hashCode(spheres)
}
}
return spheres
@@ -114,8 +120,8 @@ export namespace 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),
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
}
export type Params = typeof Params

View File

@@ -62,6 +62,8 @@ export interface Text {
readonly boundingSphere: Sphere3D
/** Maps group ids to text indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Text {
@@ -124,6 +126,10 @@ export namespace Text {
currentGroup = text.groupBuffer.ref.version
}
return groupMapping
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere)
currentHash = hashCode(text)
}
}
return text

View File

@@ -70,9 +70,9 @@ export namespace TextureMesh {
export const Params = {
...BaseGeometry.Params,
doubleSided: PD.Boolean(false),
flipSided: PD.Boolean(false),
flatShaded: PD.Boolean(false),
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
}
export type Params = typeof Params

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -166,6 +166,19 @@ class MarchingCubesState {
const v1 = sfg(sf, hi, hj, hk);
const t = (this.isoLevel - v0) / (v0 - v1);
if (this.idField) {
const u = this.idFieldGet!(this.idField, li, lj, lk);
const v = this.idFieldGet!(this.idField, hi, hj, hk)
let a = t < 0.5 ? u : v;
// -1 means 'no id', check if the other cell has an id
if (a === -1) a = t < 0.5 ? v : u;
// -2 means 'ignore this cell'
if (a === -2) return -1
this.builder.addGroup(a);
} else {
this.builder.addGroup(0);
}
const id = this.builder.addVertex(
li + t * (li - hi),
lj + t * (lj - hj),
@@ -189,16 +202,6 @@ class MarchingCubesState {
n0z + t * (n0z - n1z)
)
if (this.idField) {
const u = this.idFieldGet!(this.idField, li, lj, lk);
const v = this.idFieldGet!(this.idField, hi, hj, hk)
let a = t < 0.5 ? u : v;
if (a < 0) a = t < 0.5 ? v : u;
this.builder.addGroup(a);
} else {
this.builder.addGroup(0);
}
return id;
}

View File

@@ -44,8 +44,13 @@ export function MarchinCubesMeshBuilder(vertexChunkSize: number, mesh?: Mesh): M
ChunkedArray.add(groups, group);
},
addTriangle: (vertList: number[], a: number, b: number, c: number) => {
++triangleCount
ChunkedArray.add3(indices, vertList[a], vertList[b], vertList[c]);
const i = vertList[a], j = vertList[b], k = vertList[c]
// vertex indices <0 mean that the vertex was ignored and is not available
// and hence we don't add a triangle when this occurs
if (i >= 0 && j >= 0 && k >= 0) {
++triangleCount
ChunkedArray.add3(indices, i, j, k)
}
},
get: () => {
const vb = ChunkedArray.compact(vertices, true) as Float32Array;
@@ -73,17 +78,22 @@ export function MarchinCubesLinesBuilder(vertexChunkSize: number, lines?: Lines)
ChunkedArray.add(groups, group);
},
addTriangle: (vertList: number[], a: number, b: number, c: number, edgeFilter: number) => {
if (AllowedContours[a][b] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[a], vertList[b])
}
if (AllowedContours[b][c] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[b], vertList[c])
}
if (AllowedContours[a][c] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[a], vertList[c])
const i = vertList[a], j = vertList[b], k = vertList[c]
// vertex indices <0 mean that the vertex was ignored and is not available
// and hence we don't add a triangle when this occurs
if (i >= 0 && j >= 0 && k >= 0) {
if (AllowedContours[a][b] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[a], vertList[b])
}
if (AllowedContours[b][c] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[b], vertList[c])
}
if (AllowedContours[a][c] & edgeFilter) {
++linesCount
ChunkedArray.add2(indices, vertList[a], vertList[c])
}
}
},
get: () => {

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -10,7 +10,8 @@ describe('renderable', () => {
it('calculateBoundingSphere', () => {
const position = new Float32Array([
0, 0, 0,
1, 0, 0
1, 0, 0,
-1, 0, 0,
])
const transform = new Float32Array([
1, 0, 0, 0,
@@ -26,15 +27,18 @@ describe('renderable', () => {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
2, 0, 0, 0
-1, 0, 0, 0
])
const { boundingSphere } = calculateBoundingSphere(
const { boundingSphere, invariantBoundingSphere } = calculateBoundingSphere(
position, position.length / 3,
transform, transform.length / 16
)
expect(boundingSphere.radius).toBe(1.5)
expect(boundingSphere.center).toEqual([1.5, 0.0, 0.0])
expect(invariantBoundingSphere.extrema).toEqual([[0, 0, 0], [1, 0, 0], [-1, 0, 0]])
expect(invariantBoundingSphere.radius).toBe(1)
expect(invariantBoundingSphere.center).toEqual([0, 0, 0])
expect(boundingSphere.radius).toBe(2)
expect(boundingSphere.center).toEqual([0, 0, 0])
})
})

View File

@@ -41,7 +41,7 @@ function getHistopyramidReductionRenderable(ctx: WebGLContext, initialTexture: T
}
const schema = { ...HistopyramidReductionSchema }
const shaderCode = ShaderCode(quad_vert, reduction_frag)
const shaderCode = ShaderCode('reduction', quad_vert, reduction_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
HistopyramidReductionRenderable = createComputeRenderable(renderItem, values);

View File

@@ -34,7 +34,7 @@ function getHistopyramidSumRenderable(ctx: WebGLContext, texture: Texture) {
}
const schema = { ...HistopyramidSumSchema }
const shaderCode = ShaderCode(quad_vert, sum_frag)
const shaderCode = ShaderCode('sum', quad_vert, sum_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
HistopyramidSumRenderable = createComputeRenderable(renderItem, values)

View File

@@ -46,7 +46,7 @@ function getActiveVoxelsRenderable(ctx: WebGLContext, volumeData: Texture, gridD
}
const schema = { ...ActiveVoxelsSchema }
const shaderCode = ShaderCode(quad_vert, active_voxels_frag)
const shaderCode = ShaderCode('active-voxels', quad_vert, active_voxels_frag)
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
return createComputeRenderable(renderItem, values);

View File

@@ -62,7 +62,7 @@ function getIsosurfaceRenderable(ctx: WebGLContext, activeVoxelsPyramid: Texture
}
const schema = { ...IsosurfaceSchema }
const shaderCode = ShaderCode(quad_vert, isosurface_frag, { drawBuffers: true })
const shaderCode = ShaderCode('isosurface', quad_vert, isosurface_frag, { drawBuffers: true })
const renderItem = createComputeRenderItem(ctx, 'triangles', shaderCode, schema, values)
return createComputeRenderable(renderItem, values);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -66,7 +66,10 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
img.src = objectURL
img.style.width = imageData.width * scale + 'px'
img.style.height = imageData.height * scale + 'px';
if (pixelated) (img.style as any).imageRendering = 'pixelated' // supported only in Chrome
if (pixelated) {
// not supported in Firefox and IE
img.style.imageRendering = 'pixelated'
}
img.style.position = 'absolute'
img.style.top = '0px'
img.style.left = '0px'
@@ -78,35 +81,73 @@ export function printImageData(imageData: ImageData, scale = 1, pixelated = fals
//
const v = Vec3.zero()
const boundaryHelper = new BoundaryHelper()
const boundaryHelperCoarse = new BoundaryHelper('14')
const boundaryHelperFine = new BoundaryHelper('98')
function getHelper(count: number) {
return count > 500_000 ? boundaryHelperCoarse : boundaryHelperFine
}
export function calculateInvariantBoundingSphere(position: Float32Array, positionCount: number, stepFactor: number): Sphere3D {
const step = stepFactor * 3
boundaryHelper.reset(0)
const boundaryHelper = getHelper(positionCount)
boundaryHelper.reset()
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
Vec3.fromArray(v, position, i)
boundaryHelper.boundaryStep(v, 0)
boundaryHelper.includeStep(v)
}
boundaryHelper.finishBoundaryStep()
boundaryHelper.finishedIncludeStep()
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
Vec3.fromArray(v, position, i)
boundaryHelper.extendStep(v, 0)
boundaryHelper.radiusStep(v)
}
return boundaryHelper.getSphere()
const sphere = boundaryHelper.getSphere()
if (positionCount <= 98) {
const extrema: Vec3[] = []
for (let i = 0, _i = positionCount * 3; i < _i; i += step) {
extrema.push(Vec3.fromArray(Vec3(), position, i));
}
Sphere3D.setExtrema(sphere, extrema)
}
return sphere
}
export function calculateTransformBoundingSphere(invariantBoundingSphere: Sphere3D, transform: Float32Array, transformCount: number): Sphere3D {
const { center, radius } = invariantBoundingSphere
boundaryHelper.reset(0)
for (let i = 0, _i = transformCount; i < _i; ++i) {
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
boundaryHelper.boundaryStep(v, radius)
}
boundaryHelper.finishBoundaryStep()
for (let i = 0, _i = transformCount; i < _i; ++i) {
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
boundaryHelper.extendStep(v, radius)
const boundaryHelper = getHelper(transformCount)
boundaryHelper.reset()
const { center, radius, extrema } = invariantBoundingSphere
if (extrema) {
for (let i = 0, _i = transformCount; i < _i; ++i) {
for (const e of extrema) {
Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16)
boundaryHelper.includeStep(v)
}
}
boundaryHelper.finishedIncludeStep()
for (let i = 0, _i = transformCount; i < _i; ++i) {
for (const e of extrema) {
Vec3.transformMat4Offset(v, e, transform, 0, 0, i * 16)
boundaryHelper.radiusStep(v)
}
}
} else {
for (let i = 0, _i = transformCount; i < _i; ++i) {
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
boundaryHelper.includeSphereStep(v, radius)
}
boundaryHelper.finishedIncludeStep()
for (let i = 0, _i = transformCount; i < _i; ++i) {
Vec3.transformMat4Offset(v, center, transform, 0, 0, i * 16)
boundaryHelper.radiusSphereStep(v, radius)
}
}
return boundaryHelper.getSphere()
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -16,7 +16,6 @@ import { ValueCell } from '../mol-util';
import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
import { GraphicsRenderVariant } from './webgl/render-item';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { deepClone } from '../mol-util/object';
export interface RendererStats {
programCount: number
@@ -55,22 +54,63 @@ export const RendererParams = {
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 }),
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)),
style: PD.MappedStatic('matte', {
custom: PD.Group({
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 }),
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 }),
}, { isExpanded: true }),
flat: PD.Group({}),
matte: PD.Group({}),
glossy: PD.Group({}),
metallic: PD.Group({}),
plastic: PD.Group({}),
}, { label: 'Lighting', description: 'Style in which the 3D scene is rendered/lighted' }),
}
export type RendererProps = PD.Values<typeof RendererParams>
function getStyle(props: RendererProps['style']) {
switch (props.name) {
case 'custom':
return props.params
case 'flat':
return {
lightIntensity: 0, ambientIntensity: 1,
metalness: 0, roughness: 0.4, reflectivity: 0.5
}
case 'matte':
return {
lightIntensity: 0.6, ambientIntensity: 0.4,
metalness: 0, roughness: 1, reflectivity: 0.5
}
case 'glossy':
return {
lightIntensity: 0.6, ambientIntensity: 0.4,
metalness: 0, roughness: 0.4, reflectivity: 0.5
}
case 'metallic':
return {
lightIntensity: 0.6, ambientIntensity: 0.4,
metalness: 0.4, roughness: 0.6, reflectivity: 0.5
}
case 'plastic':
return {
lightIntensity: 0.6, ambientIntensity: 0.4,
metalness: 0, roughness: 0.2, reflectivity: 0.5
}
}
}
namespace Renderer {
export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
const { gl, state, stats } = ctx
const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
const p = PD.merge(RendererParams, PD.getDefaultValues(RendererParams), props)
const style = getStyle(p.style)
const viewport = Viewport()
const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
@@ -112,12 +152,12 @@ namespace Renderer {
uTransparentBackground: ValueCell.create(0),
// the following are general 'material' uniforms
uLightIntensity: ValueCell.create(p.lightIntensity),
uAmbientIntensity: ValueCell.create(p.ambientIntensity),
uLightIntensity: ValueCell.create(style.lightIntensity),
uAmbientIntensity: ValueCell.create(style.ambientIntensity),
uMetalness: ValueCell.create(p.metalness),
uRoughness: ValueCell.create(p.roughness),
uReflectivity: ValueCell.create(p.reflectivity),
uMetalness: ValueCell.create(style.metalness),
uRoughness: ValueCell.create(style.roughness),
uReflectivity: ValueCell.create(style.reflectivity),
uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
@@ -276,28 +316,6 @@ namespace Renderer {
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)
}
if (props.ambientIntensity !== undefined && props.ambientIntensity !== p.ambientIntensity) {
p.ambientIntensity = props.ambientIntensity
ValueCell.update(globalUniforms.uAmbientIntensity, p.ambientIntensity)
}
if (props.metalness !== undefined && props.metalness !== p.metalness) {
p.metalness = props.metalness
ValueCell.update(globalUniforms.uMetalness, p.metalness)
}
if (props.roughness !== undefined && props.roughness !== p.roughness) {
p.roughness = props.roughness
ValueCell.update(globalUniforms.uRoughness, p.roughness)
}
if (props.reflectivity !== undefined && props.reflectivity !== p.reflectivity) {
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))
@@ -306,6 +324,16 @@ namespace Renderer {
p.selectColor = props.selectColor
ValueCell.update(globalUniforms.uSelectColor, Color.toVec3Normalized(globalUniforms.uSelectColor.ref.value, p.selectColor))
}
if (props.style !== undefined) {
p.style = props.style
Object.assign(style, getStyle(props.style))
ValueCell.updateIfChanged(globalUniforms.uLightIntensity, style.lightIntensity)
ValueCell.updateIfChanged(globalUniforms.uAmbientIntensity, style.ambientIntensity)
ValueCell.updateIfChanged(globalUniforms.uMetalness, style.metalness)
ValueCell.updateIfChanged(globalUniforms.uRoughness, style.roughness)
ValueCell.updateIfChanged(globalUniforms.uReflectivity, style.reflectivity)
}
},
setViewport: (x: number, y: number, width: number, height: number) => {
gl.viewport(x, y, width, height)

View File

@@ -11,32 +11,48 @@ import { RenderableValues, BaseValues } from './renderable/schema';
import { GraphicsRenderObject, createRenderable } from './render-object';
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 { CommitQueue } from './commit-queue';
import { now } from '../mol-util/now';
import { arraySetRemove } from '../mol-util/array';
import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
import { hash1 } from '../mol-data/util';
const boundaryHelper = new BoundaryHelper('98')
const boundaryHelper = new BoundaryHelper();
function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D {
boundaryHelper.reset(0.1);
boundaryHelper.reset();
for (let i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.visible) continue;
const boundingSphere = renderables[i].values.boundingSphere.ref.value
if (!boundingSphere.radius) continue;
boundaryHelper.boundaryStep(boundingSphere.center, boundingSphere.radius);
if (Sphere3D.hasExtrema(boundingSphere)) {
for (const e of boundingSphere.extrema) {
boundaryHelper.includeStep(e)
}
} else {
boundaryHelper.includeSphereStep(boundingSphere.center, boundingSphere.radius);
}
}
boundaryHelper.finishBoundaryStep();
boundaryHelper.finishedIncludeStep();
for (let i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.visible) continue;
const boundingSphere = renderables[i].values.boundingSphere.ref.value
if (!boundingSphere.radius) continue;
boundaryHelper.extendStep(boundingSphere.center, boundingSphere.radius);
if (Sphere3D.hasExtrema(boundingSphere)) {
for (const e of boundingSphere.extrema) {
boundaryHelper.radiusStep(e)
}
} else {
boundaryHelper.radiusSphereStep(boundingSphere.center, boundingSphere.radius);
}
}
Vec3.copy(boundingSphere.center, boundaryHelper.center);
boundingSphere.radius = boundaryHelper.radius;
return boundingSphere;
return boundaryHelper.getSphere(boundingSphere);
}
function renderableSort(a: Renderable<RenderableValues & BaseValues>, b: Renderable<RenderableValues & BaseValues>) {
@@ -59,6 +75,8 @@ interface Scene extends Object3D {
readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>>
readonly boundingSphere: Sphere3D
/** Returns `true` if some visibility has changed, `false` otherwise. */
syncVisibility: () => boolean
update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean, isRemoving?: boolean) => void
add: (o: GraphicsRenderObject) => void // Renderable<any>
remove: (o: GraphicsRenderObject) => void
@@ -73,7 +91,7 @@ namespace Scene {
export function create(ctx: WebGLContext): Scene {
const renderableMap = new Map<GraphicsRenderObject, Renderable<RenderableValues & BaseValues>>()
const renderables: Renderable<RenderableValues & BaseValues>[] = []
const boundingSphere = Sphere3D.zero()
const boundingSphere = Sphere3D()
let boundingSphereDirty = true
@@ -126,17 +144,37 @@ namespace Scene {
return true;
}
// const toAdd: GraphicsRenderObject[] = []
// const toRemove: GraphicsRenderObject[] = []
const commitQueue = new CommitQueue();
let visibleHash = -1
function computeVisibleHash() {
let hash = 23
for (let i = 0, il = renderables.length; i < il; ++i) {
if (!renderables[i].state.visible) continue;
hash = (31 * hash + renderables[i].id) | 0;
}
hash = hash1(hash);
if (hash === -1) hash = 0;
return hash
}
function syncVisibility() {
const newVisibleHash = computeVisibleHash()
if (newVisibleHash !== visibleHash) {
boundingSphereDirty = true
return true
} else {
return false
}
}
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 },
syncVisibility,
update(objects, keepBoundingSphere, isRemoving) {
Object3D.update(object3d)
if (objects) {
@@ -150,7 +188,11 @@ namespace Scene {
renderables[i].update()
}
}
if (!keepBoundingSphere) boundingSphereDirty = true
if (!keepBoundingSphere) {
boundingSphereDirty = true
} else {
syncVisibility()
}
},
add: (o: GraphicsRenderObject) => commitQueue.add(o),
remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
@@ -175,8 +217,11 @@ namespace Scene {
},
renderables,
get boundingSphere() {
if (boundingSphereDirty) calculateBoundingSphere(renderables, boundingSphere)
boundingSphereDirty = false
if (boundingSphereDirty) {
calculateBoundingSphere(renderables, boundingSphere)
boundingSphereDirty = false
visibleHash = computeVisibleHash()
}
return boundingSphere
}
}

View File

@@ -24,6 +24,7 @@ export interface ShaderExtensions {
export interface ShaderCode {
readonly id: number
readonly name: string
readonly vert: string
readonly frag: string
readonly extensions: ShaderExtensions
@@ -37,7 +38,6 @@ import assign_color_varying from './shader/chunks/assign-color-varying.glsl'
import assign_group from './shader/chunks/assign-group.glsl'
import assign_marker_varying from './shader/chunks/assign-marker-varying.glsl'
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'
@@ -63,7 +63,6 @@ const ShaderChunks: { [k: string]: string } = {
assign_group,
assign_marker_varying,
assign_material_color,
assign_normal,
assign_position,
assign_size,
check_picking_alpha,
@@ -99,33 +98,33 @@ function addIncludes(text: string) {
.replace(reMultipleLinebreaks, '\n')
}
export function ShaderCode(vert: string, frag: string, extensions: ShaderExtensions = {}): ShaderCode {
return { id: shaderCodeId(), vert: addIncludes(vert), frag: addIncludes(frag), extensions }
export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}): ShaderCode {
return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions }
}
import points_vert from './shader/points.vert'
import points_frag from './shader/points.frag'
export const PointsShaderCode = ShaderCode(points_vert, points_frag)
export const PointsShaderCode = ShaderCode('points', points_vert, points_frag)
import spheres_vert from './shader/spheres.vert'
import spheres_frag from './shader/spheres.frag'
export const SpheresShaderCode = ShaderCode(spheres_vert, spheres_frag, { fragDepth: true })
export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: true })
import text_vert from './shader/text.vert'
import text_frag from './shader/text.frag'
export const TextShaderCode = ShaderCode(text_vert, text_frag, { standardDerivatives: true })
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: true })
import lines_vert from './shader/lines.vert'
import lines_frag from './shader/lines.frag'
export const LinesShaderCode = ShaderCode(lines_vert, lines_frag)
export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag)
import mesh_vert from './shader/mesh.vert'
import mesh_frag from './shader/mesh.frag'
export const MeshShaderCode = ShaderCode(mesh_vert, mesh_frag, { standardDerivatives: true })
export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: true })
import direct_volume_vert from './shader/direct-volume.vert'
import direct_volume_frag from './shader/direct-volume.frag'
export const DirectVolumeShaderCode = ShaderCode(direct_volume_vert, direct_volume_frag, { fragDepth: true })
export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: true })
//
@@ -235,6 +234,7 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
const frag = isWebGL2(gl) ? transformGlsl300Frag(shaders.frag) : shaders.frag
return {
id: shaderCodeId(),
name: shaders.name,
vert: `${vertPrefix}${header}${shaders.vert}`,
frag: `${fragPrefix}${header}${frag}`,
extensions: shaders.extensions

View File

@@ -1,20 +0,0 @@
export default `
// inputs
// - vViewPosition (if dFlatShaded)
// - vNormal (if NOT dFlatShaded)
// outputs
// - normal
// surface normal
#if defined(dFlatShaded) && defined(enabledStandardDerivatives)
vec3 fdx = dFdx(vViewPosition);
vec3 fdy = dFdy(vViewPosition);
vec3 normal = -normalize(cross(fdx, fdy));
#else
vec3 normal = -normalize(vNormal);
#ifdef dDoubleSided
normal = normal * (float(gl_FrontFacing) * 2.0 - 1.0);
#endif
#endif
`

View File

@@ -1,5 +1,3 @@
export default `
#if !defined(dFlatShaded) || !defined(enabledStandardDerivatives)
varying vec3 vNormal;
#endif
varying vec3 vNormal;
`

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -15,7 +15,22 @@ precision highp int;
#include normal_frag_params
void main() {
interior = !gl_FrontFacing; // TODO take dFlipSided into account
// Workaround for buggy gl_FrontFacing (e.g. on some integrated Intel GPUs)
#if defined(enabledStandardDerivatives)
vec3 fdx = dFdx(vViewPosition);
vec3 fdy = dFdy(vViewPosition);
vec3 faceNormal = normalize(cross(fdx,fdy));
bool frontFacing = dot(vNormal, faceNormal) > 0.0;
#else
bool frontFacing = dot(vNormal, vViewPosition) < 0.0;
#endif
#if defined(dFlipSided)
interior = frontFacing;
#else
interior = !frontFacing;
#endif
#include assign_material_color
#if defined(dColorType_objectPicking) || defined(dColorType_instancePicking) || defined(dColorType_groupPicking)
@@ -27,7 +42,14 @@ void main() {
#ifdef dIgnoreLight
gl_FragColor = material;
#else
#include assign_normal
#if defined(dFlatShaded) && defined(enabledStandardDerivatives)
vec3 normal = -faceNormal;
#else
vec3 normal = -normalize(vNormal);
#ifdef dDoubleSided
normal = normal * (float(frontFacing) * 2.0 - 1.0);
#endif
#endif
#include apply_light_color
#endif

View File

@@ -23,14 +23,12 @@ attribute mat4 aTransform;
attribute float aInstance;
attribute float aGroup;
#if !defined(dFlatShaded) || !defined(enabledStandardDerivatives) || defined(dIgnoreLight)
#ifdef dGeoTexture
uniform sampler2D tNormal;
#else
attribute vec3 aNormal;
#endif
varying vec3 vNormal;
#ifdef dGeoTexture
uniform sampler2D tNormal;
#else
attribute vec3 aNormal;
#endif
varying vec3 vNormal;
void main(){
#include assign_group
@@ -38,18 +36,16 @@ void main(){
#include assign_marker_varying
#include assign_position
#if !defined(dFlatShaded) || !defined(enabledStandardDerivatives) || defined(dIgnoreLight)
#ifdef dGeoTexture
vec3 normal = readFromTexture(tNormal, aGroup, uGeoTexDim).xyz;
#else
vec3 normal = aNormal;
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(modelView)));
vec3 transformedNormal = normalize(normalMatrix * normalize(normal));
#if defined(dFlipSided) && !defined(dDoubleSided) // TODO checking dDoubleSided should not be required, ASR
transformedNormal = -transformedNormal;
#endif
vNormal = transformedNormal;
#ifdef dGeoTexture
vec3 normal = readFromTexture(tNormal, aGroup, uGeoTexDim).xyz;
#else
vec3 normal = aNormal;
#endif
mat3 normalMatrix = transpose3(inverse3(mat3(modelView)));
vec3 transformedNormal = normalize(normalMatrix * normalize(normal));
#if defined(dFlipSided) && !defined(dDoubleSided) // TODO checking dDoubleSided should not be required, ASR
transformedNormal = -transformedNormal;
#endif
vNormal = transformedNormal;
}
`

View File

@@ -38,10 +38,12 @@ function getLocations(gl: GLRenderingContext, program: WebGLProgram, schema: Ren
const spec = schema[k]
if (spec.type === 'attribute') {
const loc = gl.getAttribLocation(program, k)
// unused attributes will result in a `-1` location which is usually fine
// if (loc === -1) console.info(`Could not get attribute location for '${k}'`)
locations[k] = loc
} else if (spec.type === 'uniform' || spec.type === 'texture') {
const loc = gl.getUniformLocation(program, k)
// unused uniforms will result in a `null` location which is usually fine
// if (loc === null) console.info(`Could not get uniform location for '${k}'`)
locations[k] = loc as number
}
@@ -146,8 +148,8 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi
const vertShader = getShader('vert', shaderCode.vert)
const fragShader = getShader('frag', shaderCode.frag)
let locations: Locations // = getLocations(gl, program, schema)
let uniformSetters: UniformSetters // = getUniformSetters(schema)
let locations: Locations
let uniformSetters: UniformSetters
function init() {
vertShader.attach(program)
@@ -186,9 +188,9 @@ export function createProgram(gl: GLRenderingContext, state: WebGLState, extensi
}
}
},
bindAttributes: (attribueBuffers: AttributeBuffers) => {
for (let i = 0, il = attribueBuffers.length; i < il; ++i) {
const [k, buffer] = attribueBuffers[i]
bindAttributes: (attributeBuffers: AttributeBuffers) => {
for (let i = 0, il = attributeBuffers.length; i < il; ++i) {
const [k, buffer] = attributeBuffers[i]
const l = locations[k]
if (l !== -1) buffer.bind(l)
}

View File

@@ -198,9 +198,6 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
try {
checkError(ctx.gl)
} catch (e) {
// console.log('shaderCode', shaderCode)
// console.log('schema', schema)
// console.log('attributeBuffers', attributeBuffers)
throw new Error(`Error rendering item id ${id}: '${e}'`)
}
}
@@ -246,10 +243,10 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
const value = attributeValues[k]
if (value.ref.version !== versions[k]) {
if (buffer.length >= value.ref.value.length) {
// console.log('attribute array large enough to update', k, value.ref.id, value.ref.version)
// console.log('attribute array large enough to update', buffer.id, k, value.ref.id, value.ref.version)
buffer.updateData(value.ref.value)
} else {
// console.log('attribute array to small, need to create new attribute', k, value.ref.id, value.ref.version)
// console.log('attribute array too small, need to create new attribute', buffer.id, k, value.ref.id, value.ref.version)
buffer.destroy()
const { itemSize, divisor } = schema[k] as AttributeSpec<AttributeKind>
attributeBuffers[i][1] = resources.attribute(value.ref.value, itemSize, divisor)
@@ -276,7 +273,8 @@ export function createRenderItem<T extends RenderVariantDefines, S extends keyof
// console.log('program/defines or buffers changed, update vaos')
Object.keys(renderVariantDefines).forEach(k => {
const vertexArray = vertexArrays[k]
if (vertexArray) vertexArray.update()
if (vertexArray) vertexArray.destroy()
vertexArrays[k] = vertexArrayObject ? resources.vertexArray(programs[k], attributeBuffers, elementsBuffer) : null
})
}

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.323, IHM 1.07, CARB draft.
* Code-generated 'BIRD' schema file. Dictionary versions: mmCIF 5.323, IHM 1.08, CARB draft.
*
* @author molstar/ciftools package
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.323, IHM 1.07, CARB draft.
* Code-generated 'CCD' schema file. Dictionary versions: mmCIF 5.323, IHM 1.08, CARB draft.
*
* @author molstar/ciftools package
*/

View File

@@ -1,7 +1,7 @@
/**
* Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.323, IHM 1.07, CARB draft.
* Code-generated 'mmCIF' schema file. Dictionary versions: mmCIF 5.323, IHM 1.08, CARB draft.
*
* @author molstar/ciftools package
*/

View File

@@ -199,7 +199,7 @@ export namespace Category {
getFormat(cat, field) { return void 0; }
}
export function ofTable(table: Table<Table.Schema>, indices?: ArrayLike<number>): Category.Instance {
export function ofTable(table: Table, indices?: ArrayLike<number>): Category.Instance {
if (indices) {
return {
fields: cifFieldsFromTableSchema(table._schema),

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
@@ -7,6 +7,7 @@
import { GridLookup3D } from '../../geometry';
import { sortArray } from '../../../mol-data/util';
import { OrderedSet } from '../../../mol-data/int';
import { getBoundary } from '../boundary';
const xs = [0, 0, 1];
const ys = [0, 1, 0];
@@ -15,7 +16,9 @@ const rs = [0, 0.5, 1/3];
describe('GridLookup3d', () => {
it('basic', () => {
const grid = GridLookup3D({ x: xs, y: ys, z: zs, indices: OrderedSet.ofBounds(0, 3) });
const position = { x: xs, y: ys, z: zs, indices: OrderedSet.ofBounds(0, 3) }
const boundary = getBoundary(position)
const grid = GridLookup3D(position, boundary);
let r = grid.find(0, 0, 0, 0);
expect(r.count).toBe(1);
@@ -27,7 +30,9 @@ describe('GridLookup3d', () => {
});
it('radius', () => {
const grid = GridLookup3D({ x: xs, y: ys, z: zs, radius: [0, 0.5, 1 / 3], indices: OrderedSet.ofBounds(0, 3) });
const position = { x: xs, y: ys, z: zs, radius: [0, 0.5, 1 / 3], indices: OrderedSet.ofBounds(0, 3) }
const boundary = getBoundary(position)
const grid = GridLookup3D(position, boundary);
let r = grid.find(0, 0, 0, 0);
expect(r.count).toBe(1);
@@ -39,7 +44,9 @@ describe('GridLookup3d', () => {
});
it('indexed', () => {
const grid = GridLookup3D({ x: xs, y: ys, z: zs, indices: OrderedSet.ofSingleton(1), radius: rs });
const position = { x: xs, y: ys, z: zs, indices: OrderedSet.ofSingleton(1), radius: rs }
const boundary = getBoundary(position)
const grid = GridLookup3D(position, boundary);
let r = grid.find(0, 0, 0, 0);
expect(r.count).toBe(0);

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Spacegroup, SpacegroupCell } from '../spacegroup/construction';
import { Vec3 } from '../../linear-algebra';
function getSpacegroup(name: string) {
const size = Vec3.create(1, 1, 1)
const anglesInRadians = Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2)
const cell = SpacegroupCell.create(name, size, anglesInRadians)
return Spacegroup.create(cell)
}
function checkOperatorsXyz(name: string, expected: string[]) {
const spacegroup = getSpacegroup(name)
for (let i = 0, il = spacegroup.operators.length; i < il; ++i) {
const op = spacegroup.operators[i]
const actual = Spacegroup.getOperatorXyz(op)
expect(actual).toBe(expected[i])
}
}
describe('Spacegroup', () => {
it('operators xyz', () => {
checkOperatorsXyz('P 1', ['X,Y,Z'])
checkOperatorsXyz('P -1', ['X,Y,Z', '-X,-Y,-Z'])
checkOperatorsXyz('P 1 21 1', ['X,Y,Z', '-X,1/2+Y,-Z'])
checkOperatorsXyz('P 1 21/m 1', ['X,Y,Z', '-X,1/2+Y,-Z', '-X,-Y,-Z', 'X,1/2-Y,Z'])
checkOperatorsXyz('P 41', ['X,Y,Z', '-X,-Y,1/2+Z', '-Y,X,1/4+Z', 'Y,-X,3/4+Z'])
checkOperatorsXyz('P 41 21 2', ['X,Y,Z', '-X,-Y,1/2+Z', '1/2-Y,1/2+X,1/4+Z', '1/2+Y,1/2-X,3/4+Z', '1/2-X,1/2+Y,1/4-Z', '1/2+X,1/2-Y,3/4-Z', 'Y,X,-Z', '-Y,-X,1/2-Z'])
checkOperatorsXyz('P 3', ['X,Y,Z', '-Y,X-Y,Z', 'Y-X,-X,Z'])
});
})

View File

@@ -1,124 +1,145 @@
/**
* Copyright (c) 2018 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 David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Vec3 } from '../../mol-math/linear-algebra/3d';
import { Vec3 } from '../linear-algebra/3d';
import { CentroidHelper } from './centroid-helper';
import { Sphere3D } from '../geometry';
import { Box3D } from './primitives/box3d';
import { Sphere3D } from './primitives/sphere3d';
/**
* Usage:
*
* 1. .reset(tolerance); tolerance plays part in the "extend" step
* 2. for each point/sphere call boundaryStep()
* 3. .finishBoundaryStep
* 4. for each point/sphere call extendStep
* 5. use .center/.radius or call getSphere/getBox
*/
// implementing http://www.ep.liu.se/ecp/034/009/ecp083409.pdf
export class BoundaryHelper {
private count = 0;
private extremes = [Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero()];
private u = Vec3.zero();
private v = Vec3.zero();
private dir: Vec3[]
tolerance = 0;
center: Vec3 = Vec3.zero();
radius = 0;
private minDist: number[] = []
private maxDist: number[] = []
private extrema: Vec3[] = []
centroidHelper = new CentroidHelper()
reset(tolerance: number) {
Vec3.set(this.center, 0, 0, 0);
for (let i = 0; i < 6; i++) {
const e = i % 2 === 0 ? Number.MAX_VALUE : -Number.MAX_VALUE;
Vec3.set(this.extremes[i], e, e, e);
private computeExtrema(i: number, p: Vec3) {
const d = Vec3.dot(this.dir[i], p)
if (d < this.minDist[i]) {
this.minDist[i] = d
Vec3.copy(this.extrema[i * 2], p)
}
this.radius = 0;
this.count = 0;
this.tolerance = tolerance;
}
boundaryStep(p: Vec3, r: number) {
updateExtremeMin(0, this.extremes[0], p, r);
updateExtremeMax(0, this.extremes[1], p, r);
updateExtremeMin(1, this.extremes[2], p, r);
updateExtremeMax(1, this.extremes[3], p, r);
updateExtremeMin(2, this.extremes[4], p, r);
updateExtremeMax(2, this.extremes[5], p, r);
this.count++;
}
finishBoundaryStep() {
if (this.count === 0) return;
let maxSpan = 0, mI = 0, mJ = 0;
for (let i = 0; i < 5; i++) {
for (let j = i + 1; j < 6; j++) {
const d = Vec3.squaredDistance(this.extremes[i], this.extremes[j]);
if (d > maxSpan) {
maxSpan = d;
mI = i;
mJ = j;
}
}
if (d > this.maxDist[i]) {
this.maxDist[i] = d
Vec3.copy(this.extrema[i * 2 + 1], p)
}
Vec3.add(this.center, this.extremes[mI], this.extremes[mJ]);
Vec3.scale(this.center, this.center, 0.5);
this.radius = Vec3.distance(this.center, this.extremes[mI]);
}
extendStep(p: Vec3, r: number) {
const d = Vec3.distance(p, this.center);
if ((1 + this.tolerance) * this.radius >= r + d) return;
private computeSphereExtrema(i: number, center: Vec3, radius: number) {
const d = Vec3.dot(this.dir[i], center)
Vec3.sub(this.u, p, this.center);
Vec3.normalize(this.u, this.u);
Vec3.scale(this.v, this.u, -this.radius);
Vec3.add(this.v, this.v, this.center);
Vec3.scale(this.u, this.u, r + d);
Vec3.add(this.u, this.u, this.center);
Vec3.add(this.center, this.u, this.v);
Vec3.scale(this.center, this.center, 0.5);
this.radius = 0.5 * (r + d + this.radius);
}
getBox(): Box3D {
Vec3.copy(this.u, this.extremes[0]);
Vec3.copy(this.v, this.extremes[0]);
for (let i = 1; i < 6; i++) {
Vec3.min(this.u, this.u, this.extremes[i]);
Vec3.max(this.v, this.v, this.extremes[i]);
if (d - radius < this.minDist[i]) {
this.minDist[i] = d - radius
Vec3.scaleAndSub(this.extrema[i * 2], center, this.dir[i], radius)
}
if (d + radius > this.maxDist[i]) {
this.maxDist[i] = d + radius
Vec3.scaleAndAdd(this.extrema[i * 2 + 1], center, this.dir[i], radius)
}
return { min: Vec3.clone(this.u), max: Vec3.clone(this.v) };
}
getSphere(): Sphere3D {
return { center: Vec3.clone(this.center), radius: this.radius };
includeStep(p: Vec3) {
for (let i = 0, il = this.dir.length; i < il; ++i) {
this.computeExtrema(i, p)
}
}
constructor() {
this.reset(0);
includeSphereStep(center: Vec3, radius: number) {
for (let i = 0, il = this.dir.length; i < il; ++i) {
this.computeSphereExtrema(i, center, radius)
}
}
finishedIncludeStep() {
for (let i = 0; i < this.extrema.length; i++) {
this.centroidHelper.includeStep(this.extrema[i]);
}
this.centroidHelper.finishedIncludeStep();
}
radiusStep(p: Vec3) {
this.centroidHelper.radiusStep(p);
}
radiusSphereStep(center: Vec3, radius: number) {
this.centroidHelper.radiusSphereStep(center, radius);
}
getSphere(sphere?: Sphere3D) {
return Sphere3D.setExtrema(this.centroidHelper.getSphere(sphere), this.extrema)
}
getBox(box?: Box3D) {
if (!box) box = Box3D()
Box3D.setEmpty(box)
for (let i = 0; i < this.extrema.length; i++) {
Box3D.add(box, this.extrema[i])
}
return box
}
reset() {
for (let i = 0, il = this.dir.length; i < il; ++i) {
this.minDist[i] = Infinity
this.maxDist[i] = -Infinity
this.extrema[i * 2] = Vec3()
this.extrema[i * 2 + 1] = Vec3()
}
this.centroidHelper.reset()
}
constructor(quality: EposQuality) {
this.dir = getEposDir(quality)
this.reset()
}
}
function updateExtremeMin(d: number, e: Vec3, center: Vec3, r: number) {
if (center[d] - r < e[d]) {
Vec3.copy(e, center);
e[d] -= r;
type EposQuality = '6' | '14' | '26' | '98'
function getEposDir(quality: EposQuality) {
let dir: number[][]
switch (quality) {
case '6': dir = [ ...Type001 ]; break
case '14': dir = [ ...Type001, ...Type111 ]; break
case '26': dir = [ ...Type001, ...Type111, ...Type011 ]; break
case '98': dir = [ ...Type001, ...Type111, ...Type011, ...Type012, ...Type112, ...Type122 ]; break
}
return dir.map(a => {
const v = Vec3.create(a[0], a[1], a[2])
return Vec3.normalize(v, v)
})
}
function updateExtremeMax(d: number, e: Vec3, center: Vec3, r: number) {
if (center[d] + r > e[d]) {
Vec3.copy(e, center);
e[d] += r;
}
}
const Type001 = [
[1, 0, 0], [0, 1, 0], [0, 0, 1]
]
const Type111 = [
[1, 1, 1], [-1, 1, 1], [-1, -1, 1], [1, -1, 1]
]
const Type011 = [
[1, 1, 0], [1, -1, 0], [1, 0, 1], [1, 0, -1], [0, 1, 1], [0, 1, -1]
]
const Type012 = [
[0, 1, 2], [0, 2, 1], [1, 0, 2], [2, 0, 1], [1, 2, 0], [2, 1, 0],
[0, 1, -2], [0, 2, -1], [1, 0, -2], [2, 0, -1], [1, -2, 0], [2, -1, 0]
]
const Type112 = [
[1, 1, 2], [2, 1, 1], [1, 2, 1], [1, -1, 2], [1, 1, -2], [1, -1, -2],
[2, -1, 1], [2, 1, -1], [2, -1, -1], [1, -2, 1], [1, 2, -1], [1, -2, -1]
]
const Type122 = [
[2, 2, 1], [1, 2, 2], [2, 1, 2], [2, -2, 1], [2, 2, -1], [2, -2, -1],
[1, -2, 2], [1, 2, -2], [1, -2, -2], [2, -1, 2], [2, 1, -2], [2, -1, -2]
]

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2018-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 { PositionData } from './common';
import { Vec3 } from '../linear-algebra';
import { OrderedSet } from '../../mol-data/int';
import { BoundaryHelper } from './boundary-helper';
import { Box3D, Sphere3D } from '../geometry';
const boundaryHelperCoarse = new BoundaryHelper('14');
const boundaryHelperFine = new BoundaryHelper('98');
function getBoundaryHelper(count: number) {
return count > 100_000 ? boundaryHelperCoarse : boundaryHelperFine
}
export type Boundary = { readonly box: Box3D, readonly sphere: Sphere3D }
export function getBoundary(data: PositionData): Boundary {
const { x, y, z, radius, indices } = data;
const p = Vec3();
const boundaryHelper = getBoundaryHelper(OrderedSet.size(indices));
boundaryHelper.reset();
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
Vec3.set(p, x[i], y[i], z[i]);
boundaryHelper.includeSphereStep(p, (radius && radius[i]) || 0);
}
boundaryHelper.finishedIncludeStep();
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
Vec3.set(p, x[i], y[i], z[i]);
boundaryHelper.radiusSphereStep(p, (radius && radius[i]) || 0);
}
const sphere = boundaryHelper.getSphere()
if (!radius && OrderedSet.size(indices) <= 98) {
const extrema: Vec3[] = []
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
extrema.push(Vec3.create(x[i], y[i], z[i]));
}
Sphere3D.setExtrema(sphere, extrema)
}
return { box: boundaryHelper.getBox(), sphere };
}

View File

@@ -37,13 +37,24 @@ class CentroidHelper {
if (d > this.radiusSq) this.radiusSq = d;
}
getSphere(): Sphere3D {
return { center: Vec3.clone(this.center), radius: Math.sqrt(this.radiusSq) };
radiusSphereStep(center: Vec3, radius: number) {
const _d = Vec3.distance(center, this.center) + radius;
const d = _d * _d;
if (d > this.radiusSq) this.radiusSq = d;
}
constructor() {
getSphere(sphere?: Sphere3D): Sphere3D {
if (!sphere) sphere = Sphere3D()
Vec3.copy(sphere.center, this.center)
sphere.radius = Math.sqrt(this.radiusSq)
return sphere
}
getCount() {
return this.count;
}
constructor() { }
}
namespace CentroidHelper {

View File

@@ -46,7 +46,7 @@ export const GaussianDensitySchema = {
}
export const GaussianDensityShaderCode = ShaderCode(
gaussian_density_vert, gaussian_density_frag,
'gaussian-density', gaussian_density_vert, gaussian_density_frag,
{ standardDerivatives: false, fragDepth: false }
)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -11,14 +11,14 @@ import { Sphere3D } from '../primitives/sphere3d';
import { PositionData } from '../common';
import { Vec3 } from '../../linear-algebra';
import { OrderedSet } from '../../../mol-data/int';
import { BoundaryHelper } from '../boundary-helper';
import { Boundary } from '../../../mol-model/structure/structure/util/boundary';
interface GridLookup3D<T = number> extends Lookup3D<T> {
readonly buckets: { readonly offset: ArrayLike<number>, readonly count: ArrayLike<number>, readonly array: ArrayLike<number> }
}
function GridLookup3D<T extends number = number>(data: PositionData, cellSizeOrCount?: Vec3 | number): GridLookup3D<T> {
return new GridLookup3DImpl<T>(data, cellSizeOrCount);
function GridLookup3D<T extends number = number>(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | number): GridLookup3D<T> {
return new GridLookup3DImpl<T>(data, boundary, cellSizeOrCount);
}
export { GridLookup3D }
@@ -48,8 +48,8 @@ class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> {
return query(this.ctx);
}
constructor(data: PositionData, cellSizeOrCount?: Vec3 | number) {
const structure = build(data, cellSizeOrCount);
constructor(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | number) {
const structure = build(data, boundary, cellSizeOrCount);
this.ctx = createContext<T>(structure);
this.boundary = { box: structure.boundingBox, sphere: structure.boundingSphere };
this.buckets = { offset: structure.bucketOffset, count: structure.bucketCounts, array: structure.bucketArray };
@@ -166,30 +166,9 @@ function _build(state: BuildState): Grid3D {
}
}
const boundaryHelper = new BoundaryHelper();
function getBoundary(data: PositionData) {
const { x, y, z, radius, indices } = data;
const p = Vec3();
boundaryHelper.reset(0);
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
Vec3.set(p, x[i], y[i], z[i]);
boundaryHelper.boundaryStep(p, (radius && radius[i]) || 0);
}
boundaryHelper.finishBoundaryStep();
for (let t = 0, _t = OrderedSet.size(indices); t < _t; t++) {
const i = OrderedSet.getAt(indices, t);
Vec3.set(p, x[i], y[i], z[i]);
boundaryHelper.extendStep(p, (radius && radius[i]) || 0);
}
return { boundingBox: boundaryHelper.getBox(), boundingSphere: boundaryHelper.getSphere() };
}
function build(data: PositionData, cellSizeOrCount?: Vec3 | number) {
const { boundingBox, boundingSphere } = getBoundary(data);
function build(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | number) {
// need to expand the grid bounds to avoid rounding errors
const expandedBox = Box3D.expand(Box3D.empty(), boundingBox, Vec3.create(0.5, 0.5, 0.5));
const expandedBox = Box3D.expand(Box3D.empty(), boundary.box, Vec3.create(0.5, 0.5, 0.5));
const { indices } = data;
const S = Box3D.size(Vec3.zero(), expandedBox);
@@ -227,8 +206,8 @@ function build(data: PositionData, cellSizeOrCount?: Vec3 | number) {
size,
data: inputData,
expandedBox,
boundingBox,
boundingSphere,
boundingBox: boundary.box,
boundingSphere: boundary.sphere,
elementCount,
delta
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Fred Ludlow <fred.ludlow@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -14,6 +14,8 @@ import { OrderedSet } from '../../mol-data/int';
import { PositionData } from './common';
import { Mat4 } from '../../mol-math/linear-algebra/3d';
import { Box3D, GridLookup3D, fillGridDim } from '../../mol-math/geometry';
import { BaseGeometry } from '../../mol-geo/geometry/base';
import { Boundary } from '../../mol-model/structure/structure/util/boundary';
function normalToLine (out: Vec3, p: Vec3) {
out[0] = out[1] = out[2] = 1.0
@@ -45,15 +47,15 @@ function getAngleTables (probePositions: number): AnglesTables {
//
export const MolecularSurfaceCalculationParams = {
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }),
probeRadius: PD.Numeric(1.4, { min: 0, max: 10, step: 0.1 }),
probePositions: PD.Numeric(30, { min: 12, max: 90, step: 1 }),
probeRadius: PD.Numeric(1.4, { min: 0, max: 10, step: 0.1 }, { description: 'Radius of the probe tracing the molecular surface.' }),
resolution: PD.Numeric(0.5, { min: 0.01, max: 20, step: 0.01 }, { description: 'Grid resolution/cell spacing.', ...BaseGeometry.CustomQualityParamInfo }),
probePositions: PD.Numeric(30, { min: 12, max: 90, step: 1 }, { description: 'Number of positions tested for probe target intersection.', ...BaseGeometry.CustomQualityParamInfo }),
}
export const DefaultMolecularSurfaceCalculationProps = PD.getDefaultValues(MolecularSurfaceCalculationParams)
export type MolecularSurfaceCalculationProps = typeof DefaultMolecularSurfaceCalculationProps
export async function calcMolecularSurface(ctx: RuntimeContext, position: Required<PositionData>, maxRadius: number, props: MolecularSurfaceCalculationProps) {
export async function calcMolecularSurface(ctx: RuntimeContext, position: Required<PositionData>, boundary: Boundary, maxRadius: number, box: Box3D | null, props: MolecularSurfaceCalculationProps) {
// Field generation method adapted from AstexViewer (Mike Hartshorn) by Fred Ludlow.
// Other parts based heavily on NGL (Alexander Rose) EDT Surface class
@@ -78,7 +80,7 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
}
for (let j = 0, jl = neighbours.count; j < jl; ++j) {
const ai = OrderedSet.getAt(position.indices, neighbours.indices[j])
const ai = OrderedSet.getAt(indices, neighbours.indices[j])
if (ai !== a && ai !== b && singleAtomObscures(ai, x, y, z)) {
lastClip = ai
return ai
@@ -281,7 +283,7 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
// Is this grid point closer to a or b?
// Take dot product of atob and gridpoint->p (dx, dy, dz)
const dp = dx * atob[0] + dy * atob[1] + dz * atob[2]
idData[idx] = id[OrderedSet.indexOf(position.indices, dp < 0.0 ? b : a)]
idData[idx] = id[OrderedSet.indexOf(indices, dp < 0.0 ? b : a)]
}
}
}
@@ -317,15 +319,16 @@ export async function calcMolecularSurface(ctx: RuntimeContext, position: Requir
const scaleFactor = 1 / resolution
const ngTorus = Math.max(5, 2 + Math.floor(probeRadius * scaleFactor))
const cellSize = Vec3.create(maxRadius, maxRadius, maxRadius)
const cellSize = Vec3.create(maxRadius, maxRadius, maxRadius)
Vec3.scale(cellSize, cellSize, 2)
const lookup3d = GridLookup3D(position, cellSize)
const lookup3d = GridLookup3D(position, boundary, cellSize)
const neighbours = lookup3d.result
const box = lookup3d.boundary.box
if (box === null) box = lookup3d.boundary.box
const { indices, x: px, y: py, z: pz, id, radius } = position
const n = OrderedSet.size(indices)
const pad = maxRadius * 2 + resolution
const pad = maxRadius + resolution
const expandedBox = Box3D.expand(Box3D(), box, Vec3.create(pad, pad, pad));
const [ minX, minY, minZ ] = expandedBox.min
const scaledBox = Box3D.scale(Box3D(), expandedBox, scaleFactor)

View File

@@ -8,6 +8,7 @@
import { Vec3, Mat4 } from '../../linear-algebra'
import { PositionData } from '../common'
import { OrderedSet } from '../../../mol-data/int';
import { Sphere3D } from './sphere3d';
interface Box3D { min: Vec3, max: Vec3 }
@@ -29,6 +30,13 @@ namespace Box3D {
return copy(empty(), a);
}
export function fromSphere3D(out: Box3D, sphere: Sphere3D): Box3D {
const r = Vec3.create(sphere.radius, sphere.radius, sphere.radius)
Vec3.sub(out.min, sphere.center, r)
Vec3.add(out.max, sphere.center, r)
return out
}
export function computeBounding(data: PositionData): Box3D {
const min = Vec3.create(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
const max = Vec3.create(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -8,33 +8,51 @@
import { Vec3, Mat4, EPSILON } from '../../linear-algebra'
import { PositionData } from '../common'
import { OrderedSet } from '../../../mol-data/int';
import { NumberArray } from '../../../mol-util/type-helpers';
import { NumberArray, PickRequired } from '../../../mol-util/type-helpers';
import { Box3D } from './box3d';
import { Axes3D } from './axes3d';
interface Sphere3D { center: Vec3, radius: number }
interface Sphere3D {
center: Vec3,
radius: number,
extrema?: Vec3[]
}
function Sphere3D() {
return Sphere3D.zero();
}
namespace Sphere3D {
export function hasExtrema(sphere: Sphere3D): sphere is PickRequired<Sphere3D, 'extrema'> {
return sphere.extrema !== undefined
}
export function create(center: Vec3, radius: number): Sphere3D { return { center, radius }; }
export function zero(): Sphere3D { return { center: Vec3.zero(), radius: 0 }; }
export function clone(a: Sphere3D): Sphere3D {
const out = zero();
Vec3.copy(out.center, a.center);
out.radius = a.radius
const out = create(Vec3.clone(a.center), a.radius)
if (hasExtrema(a)) out.extrema = a.extrema
return out;
}
export function copy(out: Sphere3D, a: Sphere3D) {
Vec3.copy(out.center, a.center)
out.radius = a.radius
if (hasExtrema(a)) setExtrema(out, a.extrema)
return out;
}
export function setExtrema(out: Sphere3D, extrema: Vec3[]): Sphere3D {
if (out.extrema !== undefined) {
out.extrema.length = 0
out.extrema.push(...extrema)
} else {
out.extrema = [...extrema]
}
return out
}
export function computeBounding(data: PositionData): Sphere3D {
const { x, y, z, indices } = data;
let cx = 0, cy = 0, cz = 0;
@@ -113,10 +131,19 @@ namespace Sphere3D {
return out
}
const tmpDir = Vec3()
/** Expand sphere radius by delta */
export function expand(out: Sphere3D, sphere: Sphere3D, delta: number): Sphere3D {
Vec3.copy(out.center, sphere.center)
out.radius = sphere.radius + delta
if (hasExtrema(sphere)) {
setExtrema(out, sphere.extrema.map(e => {
Vec3.sub(tmpDir, e, sphere.center)
const dist = Vec3.distance(sphere.center, e)
Vec3.normalize(tmpDir, tmpDir)
return Vec3.scaleAndAdd(Vec3(), sphere.center, tmpDir, dist + delta)
}))
}
return out
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -35,7 +35,8 @@ namespace SpacegroupCell {
export const Zero: SpacegroupCell = create('P 1', Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2));
/** True if 'P 1' with cellsize [1, 1, 1] */
export function isZero(cell: SpacegroupCell) {
export function isZero(cell?: SpacegroupCell) {
if (!cell) return true;
return cell.index === 0 && cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1;
}
@@ -145,6 +146,54 @@ namespace Spacegroup {
const r3 = TransformData[ids[2]];
return Mat4.ofRows([r1, r2, r3, [0, 0, 0, 1]]);
}
export function getOperatorXyz(op: Mat4) {
return [
formatElement(getRotation(op[0], op[4], op[8]), getShift(op[12])),
formatElement(getRotation(op[1], op[5], op[9]), getShift(op[13])),
formatElement(getRotation(op[2], op[6], op[10]), getShift(op[14]))
].join(',')
}
function getRotation(x: number, y: number, z: number) {
let r: string[] = []
if (x > 0) r.push('+X')
else if (x < 0) r.push('-X')
if (y > 0) r.push('+Y')
else if (y < 0) r.push('-Y')
if (z > 0) r.push('+Z')
else if (z < 0) r.push('-Z')
if (r.length === 1) {
return r[0].charAt(0) === '+' ? r[0].substr(1) : r[0]
}
if (r.length === 2) {
const s0 = r[0].charAt(0)
const s1 = r[1].charAt(0)
if (s0 === '+') return `${r[0].substr(1)}${r[1]}`
if (s1 === '+') return `${r[1].substr(1)}${r[0]}`
}
throw new Error(`unknown rotation '${r}', ${x} ${y} ${z}`)
}
function getShift(s: number) {
switch (s) {
case 1/2: return '1/2'
case 1/4: return '1/4'
case 3/4: return '3/4'
case 1/3: return '1/3'
case 2/3: return '2/3'
case 1/6: return '1/6'
case 5/6: return '5/6'
}
return ''
}
function formatElement(rotation: string, shift: string) {
if (shift === '') return rotation
if (rotation.length > 2) return `${rotation}+${shift}`
return rotation.charAt(0) === '-' ? `${shift}${rotation}` : `${shift}+${rotation}`
}
}
export { Spacegroup, SpacegroupCell }

View File

@@ -436,7 +436,7 @@ namespace Vec3 {
return out;
}
/** Computes the angle between 2 vectors, reports in rad. */
/** Computes the angle between 2 vectors, reports in radians. */
export function angle(a: Vec3, b: Vec3) {
const theta = dot(a, b) / Math.sqrt(squaredMagnitude(a) * squaredMagnitude(b));
return Math.acos(clamp(theta, -1, 1)); // clamp to avoid numerical problems
@@ -450,7 +450,7 @@ namespace Vec3 {
const tmp_dh_bcd = zero();
const tmp_dh_cross = zero();
/**
* Computes the dihedral angles of 4 points.
* Computes the dihedral angles of 4 points, reports in radians.
*/
export function dihedralAngle(a: Vec3, b: Vec3, c: Vec3, d: Vec3): number {
sub(tmp_dh_ab, a, b);
@@ -461,7 +461,7 @@ namespace Vec3 {
cross(tmp_dh_abc, tmp_dh_ab, tmp_dh_cb);
cross(tmp_dh_bcd, tmp_dh_bc, tmp_dh_dc);
const _angle = angle(tmp_dh_abc, tmp_dh_bcd) * 360.0 / (2 * Math.PI);
const _angle = angle(tmp_dh_abc, tmp_dh_bcd);
cross(tmp_dh_cross, tmp_dh_abc, tmp_dh_bcd);
return dot(tmp_dh_cb, tmp_dh_cross) > 0 ? _angle : -_angle;
}

View File

@@ -90,9 +90,9 @@ function getConformation(atom_site: AtomSite): AtomicConformation {
function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
// TODO need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
return Table.areEqual(a.chains as Table<ChainsSchema>, b.chains as Table<ChainsSchema>)
&& Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>)
&& Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>)
return Table.areEqual(a.chains, b.chains)
&& Table.areEqual(a.residues, b.residues)
&& Table.areEqual(a.atoms, b.atoms)
}
function getAtomicHierarchy(atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, chemicalComponentMap: Model['properties']['chemicalComponentMap'], previous?: Model) {

View File

@@ -24,9 +24,16 @@ import { getModelGroupName } from './util';
export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
const properties = getProperties(data)
return data.ihm_model_list._rowCount > 0
const models = data.ihm_model_list._rowCount > 0
? await readIntegrative(ctx, data, properties, format)
: await readStandard(ctx, data, properties, format);
for (let i = 0; i < models.length; i++) {
models[i].trajectoryInfo.index = i;
models[i].trajectoryInfo.size = models.length;
}
return models;
}
/** Standard atomic model */
@@ -45,7 +52,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
}
const coarse = EmptyCoarse;
const sequence = getSequence(data, entities, atomic.hierarchy, coarse.hierarchy, properties.modifiedResidues.parentId)
const sequence = getSequence(data, entities, atomic.hierarchy, coarse.hierarchy)
const atomicRanges = getAtomicRanges(atomic.hierarchy, entities, atomic.conformation, sequence)
const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
@@ -62,6 +69,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
entry,
sourceData: format,
modelNum,
trajectoryInfo: { index: 0, size: 1 },
entities,
sequence,
atomicHierarchy: atomic.hierarchy,
@@ -80,7 +88,7 @@ function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex:
function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Model['properties'], format: ModelFormat): Model {
const atomic = getAtomicHierarchyAndConformation(ihm.atom_site, ihm.atom_site_sourceIndex, ihm.entities, properties.chemicalComponentMap);
const coarse = getCoarse(ihm, properties);
const sequence = getSequence(data, ihm.entities, atomic.hierarchy, coarse.hierarchy, properties.modifiedResidues.parentId)
const sequence = getSequence(data, ihm.entities, atomic.hierarchy, coarse.hierarchy)
const atomicRanges = getAtomicRanges(atomic.hierarchy, ihm.entities, atomic.conformation, sequence)
const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
@@ -99,6 +107,7 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
entry,
sourceData: format,
modelNum: ihm.model_id,
trajectoryInfo: { index: 0, size: 1 },
entities: ihm.entities,
sequence,
atomicHierarchy: atomic.hierarchy,

View File

@@ -6,30 +6,13 @@
*/
import { Model } from '../../../mol-model/structure/model/model';
import { ChemicalComponent, MissingResidue } from '../../../mol-model/structure/model/properties/common';
import { ChemicalComponent, MissingResidue, StructAsym } from '../../../mol-model/structure/model/properties/common';
import { getMoleculeType, MoleculeType, getDefaultChemicalComponent } from '../../../mol-model/structure/model/types';
import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
import { memoize1 } from '../../../mol-util/memoize';
import { BasicData } from './schema';
import { Table } from '../../../mol-data/db';
function getModifiedResidueNameMap(data: BasicData): Model['properties']['modifiedResidues'] {
const parentId = new Map<string, string>();
const details = new Map<string, string>();
const c = data.pdbx_struct_mod_residue;
const comp_id = c.label_comp_id.isDefined ? c.label_comp_id : c.auth_comp_id;
const parent_id = c.parent_comp_id, details_data = c.details;
for (let i = 0; i < c._rowCount; i++) {
const id = comp_id.value(i);
parentId.set(id, parent_id.value(i));
details.set(id, details_data.value(i));
}
return { parentId, details };
}
function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
const map = new Map<string, MissingResidue>();
const getKey = (model_num: number, asym_id: string, seq_id: number) => {
@@ -124,11 +107,43 @@ const getUniqueComponentNames = memoize1((data: BasicData) => {
return uniqueNames
})
function getStructAsymMap(data: BasicData): Model['properties']['structAsymMap'] {
const map = new Map<string, StructAsym>();
const { label_asym_id, auth_asym_id, label_entity_id } = data.atom_site
for (let i = 0, il = label_asym_id.rowCount; i < il; ++i) {
const id = label_asym_id.value(i)
if (!map.has(id)) {
map.set(id, {
id,
auth_id: auth_asym_id.value(i),
entity_id: label_entity_id.value(i)
})
}
}
if (data.struct_asym._rowCount > 0) {
const { id, entity_id } = data.struct_asym
for (let i = 0, il = id.rowCount; i < il; ++i) {
const _id = id.value(i)
if (!map.has(_id)) {
map.set(_id, {
id: _id,
auth_id: '',
entity_id: entity_id.value(i)
})
}
}
}
return map
}
export function getProperties(data: BasicData): Model['properties'] {
return {
modifiedResidues: getModifiedResidueNameMap(data),
missingResidues: getMissingResidues(data),
chemicalComponentMap: getChemicalComponentMap(data),
saccharideComponentMap: getSaccharideComponentMap(data)
saccharideComponentMap: getSaccharideComponentMap(data),
structAsymMap: getStructAsymMap(data)
}
}

View File

@@ -8,7 +8,6 @@ import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { Table } from '../../../mol-data/db';
// TODO split into conformation and hierarchy parts
// TODO extract `pdbx_struct_mod_residue` as property?
export type Entry = Table<mmCIF_Schema['entry']>
export type Struct = Table<mmCIF_Schema['struct']>
@@ -22,7 +21,6 @@ export type EntityPolySeq = Table<mmCIF_Schema['entity_poly_seq']>
export type EntityBranch = Table<mmCIF_Schema['pdbx_entity_branch']>
export type ChemComp = Table<mmCIF_Schema['chem_comp']>
export type ChemCompIdentifier = Table<mmCIF_Schema['pdbx_chem_comp_identifier']>
export type StructModResidue = Table<mmCIF_Schema['pdbx_struct_mod_residue']>
export type AtomSite = Table<mmCIF_Schema['atom_site']>
export type IhmSphereObjSite = Table<mmCIF_Schema['ihm_sphere_obj_site']>
export type IhmGaussianObjSite =Table<mmCIF_Schema['ihm_gaussian_obj_site']>
@@ -41,7 +39,6 @@ export const BasicSchema = {
pdbx_entity_branch: mmCIF_Schema.pdbx_entity_branch,
chem_comp: mmCIF_Schema.chem_comp,
pdbx_chem_comp_identifier: mmCIF_Schema.pdbx_chem_comp_identifier,
pdbx_struct_mod_residue: mmCIF_Schema.pdbx_struct_mod_residue,
atom_site: mmCIF_Schema.atom_site,
ihm_sphere_obj_site: mmCIF_Schema.ihm_sphere_obj_site,
ihm_gaussian_obj_site: mmCIF_Schema.ihm_gaussian_obj_site,
@@ -61,7 +58,6 @@ export interface BasicData {
pdbx_entity_branch: EntityBranch
chem_comp: ChemComp
pdbx_chem_comp_identifier: ChemCompIdentifier
pdbx_struct_mod_residue: StructModResidue
atom_site: AtomSite
ihm_sphere_obj_site: IhmSphereObjSite
ihm_gaussian_obj_site: IhmGaussianObjSite

View File

@@ -13,9 +13,9 @@ import { Sequence } from '../../../mol-model/sequence';
import { CoarseHierarchy } from '../../../mol-model/structure/model/properties/coarse';
import { BasicData } from './schema';
export function getSequence(data: BasicData, entities: Entities, atomicHierarchy: AtomicHierarchy, coarseHierarchy: CoarseHierarchy, modResMap: ReadonlyMap<string, string>): StructureSequence {
export function getSequence(data: BasicData, entities: Entities, atomicHierarchy: AtomicHierarchy, coarseHierarchy: CoarseHierarchy): StructureSequence {
if (!data.entity_poly_seq || !data.entity_poly_seq._rowCount) {
return StructureSequence.fromHierarchy(entities, atomicHierarchy, coarseHierarchy, modResMap);
return StructureSequence.fromHierarchy(entities, atomicHierarchy, coarseHierarchy);
}
const { entity_id, num, mon_id } = data.entity_poly_seq;
@@ -37,7 +37,7 @@ export function getSequence(data: BasicData, entities: Entities, atomicHierarchy
byEntityKey[entityKey] = {
entityId: id,
sequence: Sequence.ofResidueNames(compId, seqId, modResMap)
sequence: Sequence.ofResidueNames(compId, seqId)
};
sequences.push(byEntityKey[entityKey]);

View File

@@ -6,7 +6,7 @@
import { Table, Column } from '../../../mol-data/db';
import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
import { WaterNames } from '../../../mol-model/structure/model/types';
import { WaterNames, PolymerNames } from '../../../mol-model/structure/model/types';
import { SetUtils } from '../../../mol-util/set';
import { BasicSchema } from '../basic/schema';
@@ -77,12 +77,14 @@ export class ComponentBuilder {
private ids: string[] = []
private names: string[] = []
private types: mmCIF_Schema['chem_comp']['type']['T'][] = []
private mon_nstd_flags: mmCIF_Schema['chem_comp']['mon_nstd_flag']['T'][] = []
private set(c: Component) {
this.comps.set(c.id, c)
this.ids.push(c.id)
this.names.push(c.name)
this.types.push(c.type)
this.mon_nstd_flags.push(PolymerNames.has(c.id) ? 'y' : 'n')
}
private getAtomIds(index: number) {
@@ -141,6 +143,7 @@ export class ComponentBuilder {
id: Column.ofStringArray(this.ids),
name: Column.ofStringArray(this.names),
type: Column.ofStringAliasArray(this.types),
mon_nstd_flag: Column.ofStringAliasArray(this.mon_nstd_flags),
}, this.ids.length)
}

View File

@@ -14,6 +14,10 @@ class FormatRegistry<T> {
this.map.set(kind, obtain)
}
remove(kind: ModelFormat['kind']) {
this.map.delete(kind)
}
get(kind: ModelFormat['kind']) {
return this.map.get(kind)
}

View File

@@ -17,7 +17,6 @@ import { Table } from '../../mol-data/db';
import { AtomSiteAnisotrop } from './property/anisotropic';
import { ComponentBond } from './property/bonds/comp';
import { StructConn } from './property/bonds/struct_conn';
import { ModelCrossLinkRestraint } from './property/pair-restraints/cross-links';
function modelSymmetryFromMmcif(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return;
@@ -65,14 +64,6 @@ function structConnFromMmcif(model: Model) {
}
StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif)
function crossLinkRestraintFromMmcif(model: Model) {
if (!MmcifFormat.is(model.sourceData)) return;
const { ihm_cross_link_restraint } = model.sourceData.data.db;
if (ihm_cross_link_restraint._rowCount === 0) return;
return ModelCrossLinkRestraint.fromTable(ihm_cross_link_restraint, model)
}
ModelCrossLinkRestraint.Provider.formatRegistry.add('mmCIF', crossLinkRestraintFromMmcif)
//
export { MmcifFormat }

View File

@@ -162,8 +162,8 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
}
const categories = {
entity: entityBuilder.getEntityTable(),
chem_comp: componentBuilder.getChemCompTable(),
entity: CifCategory.ofTable('entity', entityBuilder.getEntityTable()),
chem_comp: CifCategory.ofTable('chem_comp', componentBuilder.getChemCompTable()),
atom_site: CifCategory.ofFields('atom_site', getAtomSite(atomSite)),
atom_site_anisotrop: CifCategory.ofFields('atom_site_anisotrop', getAnisotropic(anisotropic))
} as any;

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
// TODO
// ihm_predicted_contact_restraint: {
// id: int,
// entity_id_1: str,
// entity_id_2: str,
// asym_id_1: str,
// asym_id_2: str,
// comp_id_1: str,
// comp_id_2: str,
// seq_id_1: int,
// seq_id_2: int,
// atom_id_1: str,
// atom_id_2: str,
// distance_upper_limit: float,
// probability: float,
// restraint_type: Aliased<'lower bound' | 'upper bound' | 'lower and upper bound'>(str),
// model_granularity: Aliased<'by-residue' | 'by-feature' | 'by-atom'>(str),
// dataset_list_id: int,
// software_id: int,
// },

View File

@@ -15,7 +15,7 @@ import { Loci } from '../../mol-model/loci';
import { OrderedSet } from '../../mol-data/int';
import { CustomModelProperty } from './custom-model-property';
import { CustomProperty } from './custom-property';
import { LociLabelProvider } from '../../mol-plugin/util/loci-label-manager';
import { LociLabelProvider } from '../../mol-plugin-state/manager/loci-label';
export { CustomElementProperty };
@@ -66,7 +66,7 @@ namespace CustomElementProperty {
})
}
function createColorThemeProvider<T>(modelProperty: CustomModelProperty.Provider<{}, Value<T>>, getColor: (p: T) => Color, defaultColor: Color) {
function createColorThemeProvider<T>(modelProperty: CustomModelProperty.Provider<{}, Value<T>>, getColor: (p: T) => Color, defaultColor: Color): ColorTheme.Provider<{}> {
function Coloring(ctx: ThemeDataContext, props: {}): ColorTheme<{}> {
let color: LocationColor;
@@ -98,11 +98,17 @@ namespace CustomElementProperty {
}
return {
name: modelProperty.descriptor.name,
label: modelProperty.label,
category: 'Custom',
factory: Coloring,
getParams: () => ({}),
defaultValues: {},
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && !!modelProperty.get(ctx.structure.models[0]).value
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && !!modelProperty.get(ctx.structure.models[0]).value,
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? modelProperty.attach(ctx, data.structure.models[0], void 0, true) : Promise.resolve(),
detach: (_, data: ThemeDataContext) => data.structure && data.structure.models[0].customProperties.reference(modelProperty.descriptor, false)
}
}
}

View File

@@ -49,19 +49,22 @@ namespace CustomModelProperty {
label: builder.label,
descriptor: builder.descriptor,
getParams: builder.getParams,
defaultParams: builder.defaultParams,
isApplicable: builder.isApplicable,
attach: async (ctx: CustomProperty.Context, data: Model, props: Partial<PD.Values<Params>> = {}) => {
attach: async (ctx: CustomProperty.Context, data: Model, props: Partial<PD.Values<Params>> = {}, addRef) => {
if (addRef) data.customProperties.reference(builder.descriptor, true);
const property = get(data)
const p = { ...property.props, ...props }
const p = PD.merge(builder.defaultParams, property.props, props)
if (property.data.value && PD.areEqual(builder.defaultParams, property.props, p)) return
const value = await builder.obtain(ctx, data, p)
data.customProperties.add(builder.descriptor);
set(data, p, value);
},
ref: (data: Model, add: boolean) => data.customProperties.reference(builder.descriptor, add),
get: (data: Model) => get(data)?.data,
set: (data: Model, props: Partial<PD.Values<Params>> = {}) => {
const property = get(data)
const p = { ...property.props, ...props }
const p = PD.merge(builder.defaultParams, property.props, props)
if (!PD.areEqual(builder.defaultParams, property.props, p)) {
// this invalidates property.value
set(data, p, undefined)

View File

@@ -1,68 +0,0 @@
/**
* Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { CustomPropertyDescriptor, Model, Structure } from '../../mol-model/structure';
import { OrderedMap } from 'immutable';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
import { Task } from '../../mol-task';
export { CustomPropertyRegistry }
class CustomPropertyRegistry<T = never> {
private providers = OrderedMap<string, CustomPropertyRegistry.Provider<T>>().asMutable();
getSelect(object: T) {
const values = this.providers.values();
const options: [string, string][] = [], selected: string[] = [];
while (true) {
const v = values.next();
if (v.done) break;
if (!v.value.attachableTo(object)) continue;
options.push(v.value.option);
if (v.value.defaultSelected) selected.push(v.value.option[0]);
}
return PD.MultiSelect(selected, options);
}
getDefault(object: T) {
const values = this.providers.values();
const selected: string[] = [];
while (true) {
const v = values.next();
if (v.done) break;
if (!v.value.attachableTo(object)) continue;
if (v.value.defaultSelected) selected.push(v.value.option[0]);
}
return selected;
}
get(name: string) {
const prop = this.providers.get(name);
if (!prop) throw new Error(`Custom prop '${name}' is not registered.`);
return this.providers.get(name);
}
register(provider: CustomPropertyRegistry.Provider<T>) {
this.providers.set(provider.descriptor.name, provider);
}
unregister(name: string) {
this.providers.delete(name);
}
}
namespace CustomPropertyRegistry {
export interface Provider<T> {
option: [string, string],
defaultSelected: boolean,
descriptor: CustomPropertyDescriptor<any, any>,
attachableTo: (object: T) => boolean,
attach: (object: T) => Task<boolean>
}
export type ModelProvider = Provider<Model>
export type StructureProvider = Provider<Structure>
}

View File

@@ -29,8 +29,10 @@ namespace CustomProperty {
readonly label: string
readonly descriptor: CustomPropertyDescriptor
readonly getParams: (data: Data) => Params
readonly defaultParams: Params
readonly isApplicable: (data: Data) => boolean
readonly attach: (ctx: Context, data: Data, props?: Partial<PD.Values<Params>>) => Promise<void>
readonly attach: (ctx: Context, data: Data, props?: Partial<PD.Values<Params>>, addRef?: boolean) => Promise<void>
readonly ref: (data: Data, add: boolean) => void
readonly get: (data: Data) => ValueBox<Value | undefined>
readonly set: (data: Data, props: PD.Values<Params>, value?: Value) => void
}
@@ -75,7 +77,9 @@ namespace CustomProperty {
get(name: string) {
const prop = this.providers.get(name);
if (!prop) throw new Error(`Custom property '${name}' is not registered.`)
if (!prop) {
throw new Error(`Custom property '${name}' is not registered.`)
}
return this.providers.get(name)
}

View File

@@ -49,21 +49,25 @@ namespace CustomStructureProperty {
label: builder.label,
descriptor: builder.descriptor,
getParams: builder.getParams,
defaultParams: builder.defaultParams,
isApplicable: builder.isApplicable,
attach: async (ctx: CustomProperty.Context, data: Structure, props: Partial<PD.Values<Params>> = {}) => {
attach: async (ctx: CustomProperty.Context, data: Structure, props: Partial<PD.Values<Params>> = {}, addRef) => {
if (addRef) data.customPropertyDescriptors.reference(builder.descriptor, true);
if (builder.type === 'root') data = data.root
const rootProps = get(data.root).props
const property = get(data)
const p = { ...property.props, ...props }
const p = PD.merge(builder.defaultParams, rootProps, props)
if (property.data.value && PD.areEqual(builder.defaultParams, property.props, p)) return
const value = await builder.obtain(ctx, data, p)
data.customPropertyDescriptors.add(builder.descriptor);
set(data, p, value);
},
ref: (data: Structure, add: boolean) => data.customPropertyDescriptors.reference(builder.descriptor, add),
get: (data: Structure) => get(data).data,
set: (data: Structure, props: Partial<PD.Values<Params>> = {}, value?: Value) => {
if (builder.type === 'root') data = data.root
const property = get(data)
const p = { ...property.props, ...props }
const p = PD.merge(builder.defaultParams, property.props, props)
if (!PD.areEqual(builder.defaultParams, property.props, p)) {
// this invalidates property.value
set(data, p, value)

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -7,9 +7,12 @@
import { ParamDefinition as PD } from '../../mol-util/param-definition'
import { ShrakeRupleyComputationParams, AccessibleSurfaceArea } from './accessible-surface-area/shrake-rupley';
import { Structure, CustomPropertyDescriptor } from '../../mol-model/structure';
import { Structure, CustomPropertyDescriptor, Unit } from '../../mol-model/structure';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
import { CustomPropSymbol } from '../../mol-script/language/symbol';
import Type from '../../mol-script/language/type';
export const AccessibleSurfaceAreaParams = {
...ShrakeRupleyComputationParams
@@ -17,13 +20,33 @@ export const AccessibleSurfaceAreaParams = {
export type AccessibleSurfaceAreaParams = typeof AccessibleSurfaceAreaParams
export type AccessibleSurfaceAreaProps = PD.Values<AccessibleSurfaceAreaParams>
export const AccessibleSurfaceAreaSymbols = {
isBuried: QuerySymbolRuntime.Dynamic(CustomPropSymbol('computed', 'accessible-surface-area.is-buried', Type.Bool),
ctx => {
if (!Unit.isAtomic(ctx.element.unit)) return false
const accessibleSurfaceArea = AccessibleSurfaceAreaProvider.get(ctx.element.structure).value
if (!accessibleSurfaceArea) return false
return AccessibleSurfaceArea.getFlag(ctx.element, accessibleSurfaceArea) === AccessibleSurfaceArea.Flag.Buried
}
),
isAccessible: QuerySymbolRuntime.Dynamic(CustomPropSymbol('computed', 'accessible-surface-area.is-accessible', Type.Bool),
ctx => {
if (!Unit.isAtomic(ctx.element.unit)) return false
const accessibleSurfaceArea = AccessibleSurfaceAreaProvider.get(ctx.element.structure).value
if (!accessibleSurfaceArea) return false
return AccessibleSurfaceArea.getFlag(ctx.element, accessibleSurfaceArea) === AccessibleSurfaceArea.Flag.Accessible
}
),
}
export type AccessibleSurfaceAreaValue = AccessibleSurfaceArea
export const AccessibleSurfaceAreaProvider: CustomStructureProperty.Provider<AccessibleSurfaceAreaParams, AccessibleSurfaceAreaValue> = CustomStructureProperty.createProvider({
label: 'Accessible Surface Area',
descriptor: CustomPropertyDescriptor({
name: 'molstar_accessible_surface_area',
// TODO `cifExport` and `symbol`
symbols: AccessibleSurfaceAreaSymbols,
// TODO `cifExport`
}),
type: 'root',
defaultParams: AccessibleSurfaceAreaParams,

View File

@@ -9,7 +9,7 @@ import { Task, RuntimeContext } from '../../../mol-task';
// import { BitFlags } from '../../../mol-util';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { Vec3 } from '../../../mol-math/linear-algebra';
import { Structure } from '../../../mol-model/structure';
import { Structure, StructureElement, StructureProperties } from '../../../mol-model/structure';
import { assignRadiusForHeavyAtoms } from './shrake-rupley/radii';
import { ShrakeRupleyContext, VdWLookup, MaxAsa, DefaultMaxAsa } from './shrake-rupley/common';
import { computeArea } from './shrake-rupley/area';
@@ -86,19 +86,35 @@ namespace AccessibleSurfaceArea {
return points;
}
// export namespace SolventAccessibility {
// export const is: (t: number, f: Flag) => boolean = BitFlags.has
// export const create: (f: Flag) => number = BitFlags.create
// export const enum Flag {
// _ = 0x0,
// BURIED = 0x1,
// ACCESSIBLE = 0x2
// }
// }
export const enum Flag {
NA = 0x0,
Buried = 0x1,
Accessible = 0x2
}
/** Get relative area for a given component id */
export function normalize(compId: string, asa: number) {
const maxAsa = MaxAsa[compId] || DefaultMaxAsa;
return asa / maxAsa
}
export function getValue(location: StructureElement.Location, accessibleSurfaceArea: AccessibleSurfaceArea) {
const { getSerialIndex } = location.structure.root.serialMapping
const { area, serialResidueIndex } = accessibleSurfaceArea
const rSI = serialResidueIndex[getSerialIndex(location.unit, location.element)]
if (rSI === -1) return -1
return area[rSI]
}
export function getNormalizedValue(location: StructureElement.Location, accessibleSurfaceArea: AccessibleSurfaceArea) {
const value = getValue(location, accessibleSurfaceArea)
return value === -1 ? -1 : normalize(StructureProperties.residue.label_comp_id(location), value)
}
export function getFlag(location: StructureElement.Location, accessibleSurfaceArea: AccessibleSurfaceArea) {
const value = getNormalizedValue(location, accessibleSurfaceArea)
return value === -1 ? Flag.NA :
value < 0.16 ? Flag.Buried :
Flag.Accessible
}
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -11,6 +11,7 @@ import { OrderedSet, SortedArray } from '../../../mol-data/int';
import { FeatureGroup, FeatureType } from './common';
import { ValenceModelProvider } from '../valence-model';
import { Vec3 } from '../../../mol-math/linear-algebra';
import { getBoundary } from '../../../mol-math/geometry/boundary';
export { Features }
@@ -105,7 +106,11 @@ namespace Features {
return {
...data,
get lookup3d() {
return lookup3d || (lookup3d = GridLookup3D({ x: data.x, y: data.y, z: data.z, indices: OrderedSet.ofBounds(0 as FeatureIndex, data.count as FeatureIndex) }))
if (!lookup3d) {
const position = { x: data.x, y: data.y, z: data.z, indices: OrderedSet.ofBounds(0 as FeatureIndex, data.count as FeatureIndex) }
lookup3d = GridLookup3D(position, getBoundary(position))
}
return lookup3d
},
get elementsIndex() {
return elementsIndex || (elementsIndex = createElementsIndex(data, elementsCount))
@@ -128,7 +133,11 @@ namespace Features {
return {
indices,
get lookup3d() {
return lookup3d || (lookup3d = GridLookup3D({ x: data.x, y: data.y, z: data.z, indices }))
if (!lookup3d) {
const position = { x: data.x, y: data.y, z: data.z, indices }
lookup3d = GridLookup3D(position, getBoundary(position))
}
return lookup3d
}
}
}

View File

@@ -11,7 +11,7 @@ import { ValenceModelProvider } from '../valence-model';
import { InteractionsIntraContacts, InteractionsInterContacts, FeatureType, interactionTypeLabel } from './common';
import { IntraContactsBuilder, InterContactsBuilder } from './contacts-builder';
import { IntMap } from '../../../mol-data/int';
import { addUnitContacts, ContactTester, addStructureContacts, ContactProvider, ContactsParams, ContactsProps } from './contacts';
import { addUnitContacts, ContactTester, addStructureContacts, ContactsParams, ContactsProps } from './contacts';
import { HalogenDonorProvider, HalogenAcceptorProvider, HalogenBondsProvider } from './halogen-bonds';
import { HydrogenDonorProvider, WeakHydrogenDonorProvider, HydrogenAcceptorProvider, HydrogenBondsProvider, WeakHydrogenBondsProvider } from './hydrogen-bonds';
import { NegativChargeProvider, PositiveChargeProvider, AromaticRingProvider, IonicProvider, PiStackingProvider, CationPiProvider } from './charged';
@@ -25,6 +25,7 @@ import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
import { Sphere3D } from '../../../mol-math/geometry';
import { DataLoci } from '../../../mol-model/loci';
import { bondLabel, LabelGranularity } from '../../../mol-theme/label';
import { ObjectKeys } from '../../../mol-util/type-helpers';
export { Interactions }
@@ -38,8 +39,9 @@ interface Interactions {
}
namespace Interactions {
type StructureInteractions = { readonly structure: Structure, readonly interactions: Interactions }
export interface Element {
structure: Structure,
unitA: Unit
/** Index into features of unitA */
indexA: Features.FeatureIndex
@@ -47,11 +49,12 @@ namespace Interactions {
/** Index into features of unitB */
indexB: Features.FeatureIndex
}
export interface Location extends DataLocation<Interactions, Element> {}
export interface Location extends DataLocation<StructureInteractions, Element> {}
export function Location(interactions: Interactions, structure: Structure, unitA?: Unit, indexA?: Features.FeatureIndex, unitB?: Unit, indexB?: Features.FeatureIndex): Location {
return DataLocation('interactions', interactions,
{ structure: structure as any, unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any });
return DataLocation('interactions', { structure, interactions },
{ unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any });
}
export function isLocation(x: any): x is Location {
@@ -60,7 +63,8 @@ namespace Interactions {
export function areLocationsEqual(locA: Location, locB: Location) {
return (
locA.data === locB.data &&
locA.data.structure === locB.data.structure &&
locA.data.interactions === locB.data.interactions &&
locA.element.indexA === locB.element.indexA &&
locA.element.indexB === locB.element.indexB &&
locA.element.unitA === locB.element.unitA &&
@@ -82,10 +86,9 @@ namespace Interactions {
}
export function locationLabel(location: Location): string {
return _label(location.data, location.element)
return _label(location.data.interactions, location.element)
}
type StructureInteractions = { readonly structure: Structure, readonly interactions: Interactions }
export interface Loci extends DataLoci<StructureInteractions, Element> { }
export function Loci(structure: Structure, interactions: Interactions, elements: ReadonlyArray<Element>): Loci {
@@ -145,26 +148,34 @@ const ContactProviders = {
}
type ContactProviders = typeof ContactProviders
function getProvidersParams() {
const params: { [k in keyof ContactProviders]: PD.Group<ContactProviders[k]['params']> } = Object.create(null)
function getProvidersParams(defaultOn: string[] = []) {
const params: { [k in keyof ContactProviders]: PD.Mapped<PD.NamedParamUnion<{
on: PD.Group<ContactProviders[k]['params']>
off: PD.Group<{}>
}>> } = Object.create(null)
Object.keys(ContactProviders).forEach(k => {
(params as any)[k] = PD.Group(ContactProviders[k as keyof ContactProviders].params)
(params as any)[k] = PD.MappedStatic(defaultOn.includes(k) ? 'on' : 'off', {
on: PD.Group(ContactProviders[k as keyof ContactProviders].params),
off: PD.Group({})
}, { cycle: true })
})
return params
}
export const ContactProviderParams = getProvidersParams([
// 'ionic',
'cation-pi',
'pi-stacking',
'hydrogen-bonds',
'halogen-bonds',
// 'hydrophobic',
'metal-coordination',
// 'weak-hydrogen-bonds',
])
export const InteractionsParams = {
types: PD.MultiSelect([
// 'ionic',
'cation-pi',
'pi-stacking',
'hydrogen-bonds',
'halogen-bonds',
// 'hydrophobic',
'metal-coordination',
// 'weak-hydrogen-bonds',
], PD.objectToOptions(ContactProviders)),
contacts: PD.Group(ContactsParams, { isFlat: true }),
...getProvidersParams()
providers: PD.Group(ContactProviderParams, { isFlat: true }),
contacts: PD.Group(ContactsParams, { label: 'Advanced Options' }),
}
export type InteractionsParams = typeof InteractionsParams
export type InteractionsProps = PD.Values<InteractionsParams>
@@ -173,11 +184,13 @@ export async function computeInteractions(ctx: CustomProperty.Context, structure
const p = { ...PD.getDefaultValues(InteractionsParams), ...props }
await ValenceModelProvider.attach(ctx, structure)
const contactProviders: ContactProvider<any>[] = []
Object.keys(ContactProviders).forEach(k => {
if (p.types.includes(k)) contactProviders.push(ContactProviders[k as keyof typeof ContactProviders])
const contactTesters: ContactTester[] = [];
ObjectKeys(ContactProviders).forEach(k => {
const { name, params } = p.providers[k]
if (name === 'on') {
contactTesters.push(ContactProviders[k].createTester(params as any))
}
})
const contactTesters = contactProviders.map(l => l.createTester(p[l.name as keyof InteractionsProps]))
const requiredFeatures = new Set<FeatureType>()
contactTesters.forEach(l => SetUtils.add(requiredFeatures, l.requiredFeatures))

View File

@@ -101,8 +101,8 @@ function getInteractionLoci(pickingId: PickingId, structure: Structure, id: numb
const interactions = InteractionsProvider.get(structure).value!
const c = interactions.contacts.edges[groupId]
return Interactions.Loci(structure, interactions, [
{ structure, unitA: c.unitA, indexA: c.indexA, unitB: c.unitB, indexB: c.indexB },
{ structure, unitA: c.unitB, indexA: c.indexB, unitB: c.unitA, indexB: c.indexA },
{ unitA: c.unitA, indexA: c.indexA, unitB: c.unitB, indexB: c.indexB },
{ unitA: c.unitB, indexA: c.indexB, unitB: c.unitA, indexB: c.indexA },
])
}
return EmptyLoci

View File

@@ -97,8 +97,8 @@ function getInteractionLoci(pickingId: PickingId, structureGroup: StructureGroup
const interactions = InteractionsProvider.get(structure).value!
const { a, b } = interactions.unitsContacts.get(unit.id)
return Interactions.Loci(structure, interactions, [
{ structure, unitA: unit, indexA: a[groupId], unitB: unit, indexB: b[groupId] },
{ structure, unitA: unit, indexA: b[groupId], unitB: unit, indexB: a[groupId] },
{ unitA: unit, indexA: a[groupId], unitB: unit, indexB: b[groupId] },
{ unitA: unit, indexA: b[groupId], unitB: unit, indexB: a[groupId] },
])
}
return EmptyLoci

View File

@@ -8,9 +8,8 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Representation, RepresentationParamsGetter, RepresentationContext } from '../../../mol-repr/representation';
import { ThemeRegistryContext } from '../../../mol-theme/theme';
import { Structure } from '../../../mol-model/structure';
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation } from '../../../mol-repr/structure/representation';
import { UnitsRepresentation, StructureRepresentation, StructureRepresentationStateBuilder, StructureRepresentationProvider, ComplexRepresentation, getUnitKindsParam } from '../../../mol-repr/structure/representation';
import { InteractionsIntraUnitParams, InteractionsIntraUnitVisual } from './interactions-intra-unit-cylinder';
import { UnitKindOptions, UnitKind } from '../../../mol-repr/structure/visual/util/common';
import { InteractionsProvider } from '../interactions';
import { InteractionsInterUnitParams, InteractionsInterUnitVisual } from './interactions-inter-unit-cylinder';
import { CustomProperty } from '../../common/custom-property';
@@ -23,8 +22,8 @@ const InteractionsVisuals = {
export const InteractionsParams = {
...InteractionsIntraUnitParams,
...InteractionsInterUnitParams,
unitKinds: PD.MultiSelect<UnitKind>(['atomic'], UnitKindOptions),
sizeFactor: PD.Numeric(0.3, { min: 0.01, max: 10, step: 0.01 }),
unitKinds: getUnitKindsParam(['atomic']),
sizeFactor: PD.Numeric(0.15, { min: 0.01, max: 1, step: 0.01 }),
visuals: PD.MultiSelect(['intra-unit', 'inter-unit'], PD.objectToOptions(InteractionsVisuals)),
}
export type InteractionsParams = typeof InteractionsParams
@@ -37,7 +36,8 @@ export function InteractionRepresentation(ctx: RepresentationContext, getParams:
return Representation.createMulti('Interactions', ctx, getParams, StructureRepresentationStateBuilder, InteractionsVisuals as unknown as Representation.Def<Structure, InteractionsParams>)
}
export const InteractionsRepresentationProvider: StructureRepresentationProvider<InteractionsParams> = {
export const InteractionsRepresentationProvider = StructureRepresentationProvider({
name: 'interactions',
label: 'Non-covalent Interactions',
description: 'Displays non-covalent interactions as dashed cylinders.',
factory: InteractionRepresentation,
@@ -46,7 +46,8 @@ export const InteractionsRepresentationProvider: StructureRepresentationProvider
defaultColorTheme: { name: 'interaction-type' },
defaultSizeTheme: { name: 'uniform' },
isApplicable: (structure: Structure) => structure.elementCount > 0,
ensureCustomProperties: (ctx: CustomProperty.Context, structure: Structure) => {
return InteractionsProvider.attach(ctx, structure)
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
detach: (_, data) => InteractionsProvider.ref(data, false)
}
}
})

View File

@@ -1,10 +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 { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
import { Structure } from '../../mol-model/structure';
import { DSSPComputationParams, DSSPComputationProps, computeUnitDSSP } from './secondary-structure/dssp';
import { SecondaryStructure } from '../../mol-model/structure/model/properties/seconday-structure';
import { ParamDefinition as PD } from '../../mol-util/param-definition';
@@ -12,32 +12,28 @@ import { Unit } from '../../mol-model/structure/structure';
import { CustomStructureProperty } from '../common/custom-structure-property';
import { CustomProperty } from '../common/custom-property';
import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
import { CustomPropertyDescriptor } from '../../mol-model/structure/common/custom-property';
import { Model } from '../../mol-model/structure/model';
function getSecondaryStructureParams(data?: Structure) {
let defaultType = 'mmcif' as 'mmcif' | 'dssp'
let defaultType = 'model' as 'model' | 'dssp'
if (data) {
defaultType = 'dssp'
for (let i = 0, il = data.models.length; i < il; ++i) {
const m = data.models[i]
if (MmcifFormat.is(m.sourceData)) {
if (m.sourceData.data.db.struct_conf.id.isDefined ||
m.sourceData.data.db.struct_sheet_range.id.isDefined ||
m.sourceData.data.db.database_2.database_id.isDefined
) {
// if there is any secondary structure definition given or if there is
// an archival model, don't calculate dssp by default
defaultType = 'mmcif'
break
}
if (Model.isFromPdbArchive(m) || Model.hasSecondaryStructure(m)) {
// if there is any secondary structure definition given or if there is
// an archival model, don't calculate dssp by default
defaultType = 'model'
break
}
}
}
return {
type: PD.MappedStatic(defaultType, {
'mmcif': PD.EmptyGroup({ label: 'mmCIF' }),
'model': PD.EmptyGroup({ label: 'Model' }),
'dssp': PD.Group(DSSPComputationParams, { label: 'DSSP', isFlat: true })
}, { options: [['mmcif', 'mmCIF'], ['dssp', 'DSSP']] })
}, { options: [['model', 'Model'], ['dssp', 'DSSP']] })
}
}
@@ -45,6 +41,7 @@ export const SecondaryStructureParams = getSecondaryStructureParams()
export type SecondaryStructureParams = typeof SecondaryStructureParams
export type SecondaryStructureProps = PD.Values<SecondaryStructureParams>
/** Maps `unit.id` to `SecondaryStructure` */
export type SecondaryStructureValue = Map<number, SecondaryStructure>
export const SecondaryStructureProvider: CustomStructureProperty.Provider<SecondaryStructureParams, SecondaryStructureValue> = CustomStructureProperty.createProvider({
@@ -61,7 +58,7 @@ export const SecondaryStructureProvider: CustomStructureProperty.Provider<Second
const p = { ...PD.getDefaultValues(SecondaryStructureParams), ...props }
switch (p.type.name) {
case 'dssp': return await computeDssp(data, p.type.params)
case 'mmcif': return await computeMmcif(data)
case 'model': return await computeModel(data)
}
}
})
@@ -80,7 +77,7 @@ async function computeDssp(structure: Structure, props: DSSPComputationProps): P
return map
}
async function computeMmcif(structure: Structure): Promise<SecondaryStructureValue> {
async function computeModel(structure: Structure): Promise<SecondaryStructureValue> {
const map = new Map<number, SecondaryStructure>()
for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) {
const u = structure.unitSymmetryGroups[i].units[0]

View File

@@ -9,6 +9,7 @@ import { Unit } from '../../../../mol-model/structure';
import { Vec3 } from '../../../../mol-math/linear-algebra';
import { ProteinInfo } from './protein-info';
import { ElementIndex } from '../../../../mol-model/structure/model';
import { radToDeg } from '../../../../mol-math/misc';
export interface DihedralAngles {
phi: Float32Array
@@ -50,8 +51,8 @@ export function calculateUnitDihedralAngles(unit: Unit.Atomic, proteinInfo: Prot
if (index.findAtomOnResidue(residueIndices[i], 'OXT') !== -1) continue
// returns NaN for missing atoms
phi[i] = Vec3.dihedralAngle(cPosPrev, nPos, caPos, cPos)
psi[i] = Vec3.dihedralAngle(nPos, caPos, cPos, nPosNext)
phi[i] = radToDeg(Vec3.dihedralAngle(cPosPrev, nPos, caPos, cPos))
psi[i] = radToDeg(Vec3.dihedralAngle(nPos, caPos, cPos, nPosNext))
cPosPrev = cPos, caPosPrev = caPos, nPosPrev = nPos
cPos = cPosNext, caPos = caPosNext, nPos = nPosNext

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
@@ -8,6 +8,7 @@ import { GridLookup3D } from '../../../../mol-math/geometry';
import { SortedArray } from '../../../../mol-data/int';
import { Unit } from '../../../../mol-model/structure/structure';
import { ResidueIndex } from '../../../../mol-model/structure';
import { getBoundary } from '../../../../mol-math/geometry/boundary';
export function calcUnitProteinTraceLookup3D(unit: Unit.Atomic, unitProteinResidues: SortedArray<ResidueIndex>): GridLookup3D {
const { x, y, z } = unit.model.atomicConformation;
@@ -16,5 +17,6 @@ export function calcUnitProteinTraceLookup3D(unit: Unit.Atomic, unitProteinResid
for (let i = 0, il = unitProteinResidues.length; i < il; ++i) {
indices[i] = traceElementIndex[unitProteinResidues[i]]
}
return GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(indices) });
const position = { x, y, z, indices: SortedArray.ofSortedArray(indices) }
return GridLookup3D(position, getBoundary(position));
}

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -10,7 +10,7 @@ import { ParamDefinition as PD } from '../../../mol-util/param-definition'
import { Color, ColorScale } from '../../../mol-util/color'
import { ThemeDataContext } from '../../../mol-theme/theme'
import { ColorTheme, LocationColor } from '../../../mol-theme/color'
import { StructureProperties, StructureElement, Unit } from '../../../mol-model/structure'
import { StructureElement, Unit } from '../../../mol-model/structure'
import { AccessibleSurfaceAreaProvider } from '../accessible-surface-area'
import { AccessibleSurfaceArea } from '../accessible-surface-area/shrake-rupley'
import { CustomProperty } from '../../common/custom-property'
@@ -36,18 +36,16 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
domain: [0.0, 1.0]
})
const { label_comp_id } = StructureProperties.residue
const accessibleSurfaceArea = ctx.structure && AccessibleSurfaceAreaProvider.get(ctx.structure)
const contextHash = accessibleSurfaceArea?.version
if (accessibleSurfaceArea?.value && ctx.structure) {
const { getSerialIndex } = ctx.structure.root.serialMapping
const { area, serialResidueIndex } = accessibleSurfaceArea.value
const asa = accessibleSurfaceArea.value
color = (location: Location): Color => {
if (StructureElement.Location.is(location) && Unit.isAtomic(location.unit)) {
const rSI = serialResidueIndex[getSerialIndex(location.unit, location.element)]
return rSI === -1 ? DefaultColor : scale.color(AccessibleSurfaceArea.normalize(label_comp_id(location), area[rSI]))
const value = AccessibleSurfaceArea.getNormalizedValue(location, asa)
return value === -1 ? DefaultColor : scale.color(value)
}
return DefaultColor
}
@@ -66,13 +64,16 @@ export function AccessibleSurfaceAreaColorTheme(ctx: ThemeDataContext, props: PD
}
}
export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams> = {
export const AccessibleSurfaceAreaColorThemeProvider: ColorTheme.Provider<AccessibleSurfaceAreaColorThemeParams, 'accessible-surface-area'> = {
name: 'accessible-surface-area',
label: 'Accessible Surface Area',
category: ColorTheme.Category.Residue,
factory: AccessibleSurfaceAreaColorTheme,
getParams: getAccessibleSurfaceAreaColorThemeParams,
defaultValues: PD.getDefaultValues(AccessibleSurfaceAreaColorThemeParams),
isApplicable: (ctx: ThemeDataContext) => !!ctx.structure,
ensureCustomProperties: (ctx: CustomProperty.Context, data: ThemeDataContext) => {
return data.structure ? AccessibleSurfaceAreaProvider.attach(ctx, data.structure) : Promise.resolve()
ensureCustomProperties: {
attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? AccessibleSurfaceAreaProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
detach: (_, data) => data.structure && data.structure.customPropertyDescriptors.reference(AccessibleSurfaceAreaProvider.descriptor, false)
}
}

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