representation.ts 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168
  1. /**
  2. * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { Structure, StructureElement } from '../../mol-model/structure';
  8. import { Volume } from '../../mol-model/volume';
  9. import { PluginContext } from '../../mol-plugin/context';
  10. import { VolumeRepresentationRegistry } from '../../mol-repr/volume/registry';
  11. import { VolumeParams } from '../../mol-repr/volume/representation';
  12. import { StateTransformer, StateObject } from '../../mol-state';
  13. import { Task } from '../../mol-task';
  14. import { ColorTheme } from '../../mol-theme/color';
  15. import { SizeTheme } from '../../mol-theme/size';
  16. import { Theme } from '../../mol-theme/theme';
  17. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  18. import { PluginStateObject as SO, PluginStateTransform } from '../objects';
  19. import { ColorNames } from '../../mol-util/color/names';
  20. import { ShapeRepresentation } from '../../mol-repr/shape/representation';
  21. import { StructureUnitTransforms } from '../../mol-model/structure/structure/util/unit-transforms';
  22. import { unwindStructureAssembly, explodeStructure, spinStructure, SpinStructureParams, getSpinStructureAxisAndOrigin } from '../animation/helpers';
  23. import { Color } from '../../mol-util/color';
  24. import { Overpaint } from '../../mol-theme/overpaint';
  25. import { Transparency } from '../../mol-theme/transparency';
  26. import { BaseGeometry, hasColorSmoothingProp } from '../../mol-geo/geometry/base';
  27. import { Script } from '../../mol-script/script';
  28. import { UnitcellParams, UnitcellRepresentation, getUnitcellData } from '../../mol-repr/shape/model/unitcell';
  29. import { DistanceParams, DistanceRepresentation } from '../../mol-repr/shape/loci/distance';
  30. import { getDistanceDataFromStructureSelections, getLabelDataFromStructureSelections, getOrientationDataFromStructureSelections, getAngleDataFromStructureSelections, getDihedralDataFromStructureSelections, getPlaneDataFromStructureSelections } from './helpers';
  31. import { LabelParams, LabelRepresentation } from '../../mol-repr/shape/loci/label';
  32. import { OrientationRepresentation, OrientationParams } from '../../mol-repr/shape/loci/orientation';
  33. import { AngleParams, AngleRepresentation } from '../../mol-repr/shape/loci/angle';
  34. import { DihedralParams, DihedralRepresentation } from '../../mol-repr/shape/loci/dihedral';
  35. import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
  36. import { Clipping } from '../../mol-theme/clipping';
  37. import { ObjectKeys } from '../../mol-util/type-helpers';
  38. import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
  39. import { getBoxMesh } from './shape';
  40. import { Shape } from '../../mol-model/shape';
  41. import { Box3D } from '../../mol-math/geometry';
  42. import { PlaneParams, PlaneRepresentation } from '../../mol-repr/shape/loci/plane';
  43. import { Substance } from '../../mol-theme/substance';
  44. import { Material } from '../../mol-util/material';
  45. export { StructureRepresentation3D };
  46. export { ExplodeStructureRepresentation3D };
  47. export { SpinStructureRepresentation3D };
  48. export { UnwindStructureAssemblyRepresentation3D };
  49. export { OverpaintStructureRepresentation3DFromScript };
  50. export { OverpaintStructureRepresentation3DFromBundle };
  51. export { TransparencyStructureRepresentation3DFromScript };
  52. export { TransparencyStructureRepresentation3DFromBundle };
  53. export { SubstanceStructureRepresentation3DFromScript };
  54. export { SubstanceStructureRepresentation3DFromBundle };
  55. export { ClippingStructureRepresentation3DFromScript };
  56. export { ClippingStructureRepresentation3DFromBundle };
  57. export { VolumeRepresentation3D };
  58. type StructureRepresentation3D = typeof StructureRepresentation3D
  59. const StructureRepresentation3D = PluginStateTransform.BuiltIn({
  60. name: 'structure-representation-3d',
  61. display: '3D Representation',
  62. from: SO.Molecule.Structure,
  63. to: SO.Molecule.Structure.Representation3D,
  64. params: (a, ctx: PluginContext) => {
  65. const { registry, themes: themeCtx } = ctx.representation.structure;
  66. const type = registry.get(registry.default.name);
  67. if (!a) {
  68. const colorThemeInfo = {
  69. help: (value: { name: string, params: {} }) => {
  70. const { name, params } = value;
  71. const p = themeCtx.colorThemeRegistry.get(name);
  72. const ct = p.factory({}, params);
  73. return { description: ct.description, legend: ct.legend };
  74. }
  75. };
  76. return {
  77. type: PD.Mapped<any>(
  78. registry.default.name,
  79. registry.types,
  80. name => PD.Group<any>(registry.get(name).getParams(themeCtx, Structure.Empty))),
  81. colorTheme: PD.Mapped<any>(
  82. type.defaultColorTheme.name,
  83. themeCtx.colorThemeRegistry.types,
  84. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ structure: Structure.Empty })),
  85. colorThemeInfo
  86. ),
  87. sizeTheme: PD.Mapped<any>(
  88. type.defaultSizeTheme.name,
  89. themeCtx.sizeThemeRegistry.types,
  90. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ structure: Structure.Empty }))
  91. )
  92. };
  93. }
  94. const dataCtx = { structure: a.data };
  95. const colorThemeInfo = {
  96. help: (value: { name: string, params: {} }) => {
  97. const { name, params } = value;
  98. const p = themeCtx.colorThemeRegistry.get(name);
  99. const ct = p.factory(dataCtx, params);
  100. return { description: ct.description, legend: ct.legend };
  101. }
  102. };
  103. return ({
  104. type: PD.Mapped<any>(
  105. registry.default.name,
  106. registry.getApplicableTypes(a.data),
  107. name => PD.Group<any>(registry.get(name).getParams(themeCtx, a.data))),
  108. colorTheme: PD.Mapped<any>(
  109. type.defaultColorTheme.name,
  110. themeCtx.colorThemeRegistry.getApplicableTypes(dataCtx),
  111. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams(dataCtx)),
  112. colorThemeInfo
  113. ),
  114. sizeTheme: PD.Mapped<any>(
  115. type.defaultSizeTheme.name,
  116. themeCtx.sizeThemeRegistry.types,
  117. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams(dataCtx))
  118. )
  119. });
  120. }
  121. })({
  122. canAutoUpdate({ a, oldParams, newParams }) {
  123. // TODO: other criteria as well?
  124. return a.data.elementCount < 10000 || (
  125. oldParams.type.name === newParams.type.name && newParams.type.params.quality !== 'custom'
  126. );
  127. },
  128. apply({ a, params, cache }, plugin: PluginContext) {
  129. return Task.create('Structure Representation', async ctx => {
  130. const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
  131. const provider = plugin.representation.structure.registry.get(params.type.name);
  132. const data = provider.getData?.(a.data, params.type.params) || a.data;
  133. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
  134. const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, provider.getParams);
  135. await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, params);
  136. repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, params));
  137. const props = params.type.params || {};
  138. await repr.createOrUpdate(props, data).runInContext(ctx);
  139. return new SO.Molecule.Structure.Representation3D({ repr, sourceData: a.data }, { label: provider.label });
  140. });
  141. },
  142. update({ a, b, oldParams, newParams, cache }, plugin: PluginContext) {
  143. return Task.create('Structure Representation', async ctx => {
  144. if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
  145. const provider = plugin.representation.structure.registry.get(newParams.type.name);
  146. if (provider.mustRecreate?.(oldParams.type.params, newParams.type.params)) return StateTransformer.UpdateResult.Recreate;
  147. const data = provider.getData?.(a.data, newParams.type.params) || a.data;
  148. const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
  149. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, data);
  150. // TODO: if themes had a .needsUpdate method the following block could
  151. // be optimized and only executed conditionally
  152. Theme.releaseDependencies(plugin.representation.structure.themes, { structure: b.data.sourceData }, oldParams);
  153. await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: data }, newParams);
  154. b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: data }, newParams));
  155. const props = { ...b.data.repr.props, ...newParams.type.params };
  156. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  157. b.data.sourceData = a.data;
  158. return StateTransformer.UpdateResult.Updated;
  159. });
  160. },
  161. dispose({ b, params }, plugin: PluginContext) {
  162. if (!b || !params) return;
  163. const structure = b.data.sourceData;
  164. const provider = plugin.representation.structure.registry.get(params.type.name);
  165. if (provider.ensureCustomProperties) provider.ensureCustomProperties.detach(structure);
  166. Theme.releaseDependencies(plugin.representation.structure.themes, { structure }, params);
  167. },
  168. interpolate(src, tar, t) {
  169. if (src.colorTheme.name !== 'uniform' || tar.colorTheme.name !== 'uniform') {
  170. return t <= 0.5 ? src : tar;
  171. }
  172. const from = src.colorTheme.params.value as Color, to = tar.colorTheme.params.value as Color;
  173. const value = Color.interpolate(from, to, t);
  174. return {
  175. type: t <= 0.5 ? src.type : tar.type,
  176. colorTheme: { name: 'uniform', params: { value } },
  177. sizeTheme: t <= 0.5 ? src.sizeTheme : tar.sizeTheme,
  178. };
  179. }
  180. });
  181. type UnwindStructureAssemblyRepresentation3D = typeof UnwindStructureAssemblyRepresentation3D
  182. const UnwindStructureAssemblyRepresentation3D = PluginStateTransform.BuiltIn({
  183. name: 'unwind-structure-assembly-representation-3d',
  184. display: 'Unwind Assembly 3D Representation',
  185. from: SO.Molecule.Structure.Representation3D,
  186. to: SO.Molecule.Structure.Representation3DState,
  187. params: { t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }) }
  188. })({
  189. canAutoUpdate() {
  190. return true;
  191. },
  192. apply({ a, params }) {
  193. const structure = a.data.sourceData;
  194. const unitTransforms = new StructureUnitTransforms(structure);
  195. unwindStructureAssembly(structure, unitTransforms, params.t);
  196. return new SO.Molecule.Structure.Representation3DState({
  197. state: { unitTransforms },
  198. initialState: { unitTransforms: new StructureUnitTransforms(structure) },
  199. info: structure,
  200. repr: a.data.repr
  201. }, { label: `Unwind T = ${params.t.toFixed(2)}` });
  202. },
  203. update({ a, b, newParams, oldParams }) {
  204. const structure = b.data.info as Structure;
  205. if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
  206. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  207. if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
  208. const unitTransforms = b.data.state.unitTransforms!;
  209. unwindStructureAssembly(structure, unitTransforms, newParams.t);
  210. b.label = `Unwind T = ${newParams.t.toFixed(2)}`;
  211. b.data.repr = a.data.repr;
  212. return StateTransformer.UpdateResult.Updated;
  213. }
  214. });
  215. type ExplodeStructureRepresentation3D = typeof ExplodeStructureRepresentation3D
  216. const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
  217. name: 'explode-structure-representation-3d',
  218. display: 'Explode 3D Representation',
  219. from: SO.Molecule.Structure.Representation3D,
  220. to: SO.Molecule.Structure.Representation3DState,
  221. params: { t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }) }
  222. })({
  223. canAutoUpdate() {
  224. return true;
  225. },
  226. apply({ a, params }) {
  227. const structure = a.data.sourceData;
  228. const unitTransforms = new StructureUnitTransforms(structure);
  229. explodeStructure(structure, unitTransforms, params.t, structure.root.boundary.sphere);
  230. return new SO.Molecule.Structure.Representation3DState({
  231. state: { unitTransforms },
  232. initialState: { unitTransforms: new StructureUnitTransforms(structure) },
  233. info: structure,
  234. repr: a.data.repr
  235. }, { label: `Explode T = ${params.t.toFixed(2)}` });
  236. },
  237. update({ a, b, newParams, oldParams }) {
  238. const structure = a.data.sourceData;
  239. if (b.data.info !== structure) return StateTransformer.UpdateResult.Recreate;
  240. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  241. if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
  242. const unitTransforms = b.data.state.unitTransforms!;
  243. explodeStructure(structure, unitTransforms, newParams.t, structure.root.boundary.sphere);
  244. b.label = `Explode T = ${newParams.t.toFixed(2)}`;
  245. b.data.repr = a.data.repr;
  246. return StateTransformer.UpdateResult.Updated;
  247. }
  248. });
  249. type SpinStructureRepresentation3D = typeof SpinStructureRepresentation3D
  250. const SpinStructureRepresentation3D = PluginStateTransform.BuiltIn({
  251. name: 'spin-structure-representation-3d',
  252. display: 'Spin 3D Representation',
  253. from: SO.Molecule.Structure.Representation3D,
  254. to: SO.Molecule.Structure.Representation3DState,
  255. params: {
  256. t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }),
  257. ...SpinStructureParams
  258. }
  259. })({
  260. canAutoUpdate() {
  261. return true;
  262. },
  263. apply({ a, params }) {
  264. const structure = a.data.sourceData;
  265. const unitTransforms = new StructureUnitTransforms(structure);
  266. const { axis, origin } = getSpinStructureAxisAndOrigin(structure.root, params);
  267. spinStructure(structure, unitTransforms, params.t, axis, origin);
  268. return new SO.Molecule.Structure.Representation3DState({
  269. state: { unitTransforms },
  270. initialState: { unitTransforms: new StructureUnitTransforms(structure) },
  271. info: structure,
  272. repr: a.data.repr
  273. }, { label: `Spin T = ${params.t.toFixed(2)}` });
  274. },
  275. update({ a, b, newParams, oldParams }) {
  276. const structure = a.data.sourceData;
  277. if (b.data.info !== structure) return StateTransformer.UpdateResult.Recreate;
  278. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  279. if (oldParams.t === newParams.t && oldParams.axis === newParams.axis && oldParams.origin === newParams.origin) return StateTransformer.UpdateResult.Unchanged;
  280. const unitTransforms = b.data.state.unitTransforms!;
  281. const { axis, origin } = getSpinStructureAxisAndOrigin(structure.root, newParams);
  282. spinStructure(structure, unitTransforms, newParams.t, axis, origin);
  283. b.label = `Spin T = ${newParams.t.toFixed(2)}`;
  284. b.data.repr = a.data.repr;
  285. return StateTransformer.UpdateResult.Updated;
  286. }
  287. });
  288. type OverpaintStructureRepresentation3DFromScript = typeof OverpaintStructureRepresentation3DFromScript
  289. const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  290. name: 'overpaint-structure-representation-3d-from-script',
  291. display: 'Overpaint 3D Representation',
  292. from: SO.Molecule.Structure.Representation3D,
  293. to: SO.Molecule.Structure.Representation3DState,
  294. params: () => ({
  295. layers: PD.ObjectList({
  296. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  297. color: PD.Color(ColorNames.blueviolet),
  298. clear: PD.Boolean(false)
  299. }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, {
  300. defaultValue: [{
  301. script: Script('(sel.atom.all)', 'mol-script'),
  302. color: ColorNames.blueviolet,
  303. clear: false
  304. }]
  305. }),
  306. })
  307. })({
  308. canAutoUpdate() {
  309. return true;
  310. },
  311. apply({ a, params }) {
  312. const structure = a.data.sourceData;
  313. const geometryVersion = a.data.repr.geometryVersion;
  314. const overpaint = Overpaint.ofScript(params.layers, structure);
  315. return new SO.Molecule.Structure.Representation3DState({
  316. state: { overpaint },
  317. initialState: { overpaint: Overpaint.Empty },
  318. info: { structure, geometryVersion },
  319. repr: a.data.repr
  320. }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
  321. },
  322. update({ a, b, newParams, oldParams }) {
  323. const info = b.data.info as { structure: Structure, geometryVersion: number };
  324. const newStructure = a.data.sourceData;
  325. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  326. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  327. const newGeometryVersion = a.data.repr.geometryVersion;
  328. // smoothing needs to be re-calculated when geometry changes
  329. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  330. const oldOverpaint = b.data.state.overpaint!;
  331. const newOverpaint = Overpaint.ofScript(newParams.layers, newStructure);
  332. if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
  333. info.geometryVersion = newGeometryVersion;
  334. b.data.state.overpaint = newOverpaint;
  335. b.data.repr = a.data.repr;
  336. b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
  337. return StateTransformer.UpdateResult.Updated;
  338. }
  339. });
  340. type OverpaintStructureRepresentation3DFromBundle = typeof OverpaintStructureRepresentation3DFromBundle
  341. const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  342. name: 'overpaint-structure-representation-3d-from-bundle',
  343. display: 'Overpaint 3D Representation',
  344. from: SO.Molecule.Structure.Representation3D,
  345. to: SO.Molecule.Structure.Representation3DState,
  346. params: () => ({
  347. layers: PD.ObjectList({
  348. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  349. color: PD.Color(ColorNames.blueviolet),
  350. clear: PD.Boolean(false)
  351. }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, {
  352. defaultValue: [{
  353. bundle: StructureElement.Bundle.Empty,
  354. color: ColorNames.blueviolet,
  355. clear: false
  356. }],
  357. isHidden: true
  358. }),
  359. })
  360. })({
  361. canAutoUpdate() {
  362. return true;
  363. },
  364. apply({ a, params }) {
  365. const structure = a.data.sourceData;
  366. const geometryVersion = a.data.repr.geometryVersion;
  367. const overpaint = Overpaint.ofBundle(params.layers, structure);
  368. return new SO.Molecule.Structure.Representation3DState({
  369. state: { overpaint },
  370. initialState: { overpaint: Overpaint.Empty },
  371. info: { structure, geometryVersion },
  372. repr: a.data.repr
  373. }, { label: `Overpaint (${overpaint.layers.length} Layers)` });
  374. },
  375. update({ a, b, newParams, oldParams }) {
  376. const info = b.data.info as { structure: Structure, geometryVersion: number };
  377. const newStructure = a.data.sourceData;
  378. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  379. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  380. const newGeometryVersion = a.data.repr.geometryVersion;
  381. // smoothing needs to be re-calculated when geometry changes
  382. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  383. const oldOverpaint = b.data.state.overpaint!;
  384. const newOverpaint = Overpaint.ofBundle(newParams.layers, newStructure);
  385. if (Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged;
  386. info.geometryVersion = newGeometryVersion;
  387. b.data.state.overpaint = newOverpaint;
  388. b.data.repr = a.data.repr;
  389. b.label = `Overpaint (${newOverpaint.layers.length} Layers)`;
  390. return StateTransformer.UpdateResult.Updated;
  391. }
  392. });
  393. type TransparencyStructureRepresentation3DFromScript = typeof TransparencyStructureRepresentation3DFromScript
  394. const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  395. name: 'transparency-structure-representation-3d-from-script',
  396. display: 'Transparency 3D Representation',
  397. from: SO.Molecule.Structure.Representation3D,
  398. to: SO.Molecule.Structure.Representation3DState,
  399. params: () => ({
  400. layers: PD.ObjectList({
  401. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  402. value: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
  403. }, e => `Transparency (${e.value})`, {
  404. defaultValue: [{
  405. script: Script('(sel.atom.all)', 'mol-script'),
  406. value: 0.5,
  407. }]
  408. })
  409. })
  410. })({
  411. canAutoUpdate() {
  412. return true;
  413. },
  414. apply({ a, params }) {
  415. const structure = a.data.sourceData;
  416. const geometryVersion = a.data.repr.geometryVersion;
  417. const transparency = Transparency.ofScript(params.layers, structure);
  418. return new SO.Molecule.Structure.Representation3DState({
  419. state: { transparency },
  420. initialState: { transparency: Transparency.Empty },
  421. info: { structure, geometryVersion },
  422. repr: a.data.repr
  423. }, { label: `Transparency (${transparency.layers.length} Layers)` });
  424. },
  425. update({ a, b, newParams, oldParams }) {
  426. const info = b.data.info as { structure: Structure, geometryVersion: number };
  427. const newStructure = a.data.sourceData;
  428. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  429. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  430. const newGeometryVersion = a.data.repr.geometryVersion;
  431. // smoothing needs to be re-calculated when geometry changes
  432. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  433. const oldTransparency = b.data.state.transparency!;
  434. const newTransparency = Transparency.ofScript(newParams.layers, newStructure);
  435. if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
  436. info.geometryVersion = newGeometryVersion;
  437. b.data.state.transparency = newTransparency;
  438. b.data.repr = a.data.repr;
  439. b.label = `Transparency (${newTransparency.layers.length} Layers)`;
  440. return StateTransformer.UpdateResult.Updated;
  441. }
  442. });
  443. type TransparencyStructureRepresentation3DFromBundle = typeof TransparencyStructureRepresentation3DFromBundle
  444. const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  445. name: 'transparency-structure-representation-3d-from-bundle',
  446. display: 'Transparency 3D Representation',
  447. from: SO.Molecule.Structure.Representation3D,
  448. to: SO.Molecule.Structure.Representation3DState,
  449. params: () => ({
  450. layers: PD.ObjectList({
  451. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  452. value: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
  453. }, e => `Transparency (${e.value})`, {
  454. defaultValue: [{
  455. bundle: StructureElement.Bundle.Empty,
  456. value: 0.5,
  457. }],
  458. isHidden: true
  459. })
  460. })
  461. })({
  462. canAutoUpdate() {
  463. return true;
  464. },
  465. apply({ a, params }) {
  466. const structure = a.data.sourceData;
  467. const geometryVersion = a.data.repr.geometryVersion;
  468. const transparency = Transparency.ofBundle(params.layers, structure);
  469. return new SO.Molecule.Structure.Representation3DState({
  470. state: { transparency },
  471. initialState: { transparency: Transparency.Empty },
  472. info: { structure, geometryVersion },
  473. repr: a.data.repr
  474. }, { label: `Transparency (${transparency.layers.length} Layers)` });
  475. },
  476. update({ a, b, newParams, oldParams }) {
  477. const info = b.data.info as { structure: Structure, geometryVersion: number };
  478. const newStructure = a.data.sourceData;
  479. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  480. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  481. const newGeometryVersion = a.data.repr.geometryVersion;
  482. // smoothing needs to be re-calculated when geometry changes
  483. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  484. const oldTransparency = b.data.state.transparency!;
  485. const newTransparency = Transparency.ofBundle(newParams.layers, newStructure);
  486. if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged;
  487. info.geometryVersion = newGeometryVersion;
  488. b.data.state.transparency = newTransparency;
  489. b.data.repr = a.data.repr;
  490. b.label = `Transparency (${newTransparency.layers.length} Layers)`;
  491. return StateTransformer.UpdateResult.Updated;
  492. }
  493. });
  494. type SubstanceStructureRepresentation3DFromScript = typeof SubstanceStructureRepresentation3DFromScript
  495. const SubstanceStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  496. name: 'substance-structure-representation-3d-from-script',
  497. display: 'Substance 3D Representation',
  498. from: SO.Molecule.Structure.Representation3D,
  499. to: SO.Molecule.Structure.Representation3DState,
  500. params: () => ({
  501. layers: PD.ObjectList({
  502. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  503. material: Material.getParam(),
  504. clear: PD.Boolean(false)
  505. }, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
  506. defaultValue: [{
  507. script: Script('(sel.atom.all)', 'mol-script'),
  508. material: Material({ roughness: 1 }),
  509. clear: false
  510. }]
  511. }),
  512. })
  513. })({
  514. canAutoUpdate() {
  515. return true;
  516. },
  517. apply({ a, params }) {
  518. const structure = a.data.sourceData;
  519. const geometryVersion = a.data.repr.geometryVersion;
  520. const substance = Substance.ofScript(params.layers, structure);
  521. return new SO.Molecule.Structure.Representation3DState({
  522. state: { substance },
  523. initialState: { substance: Substance.Empty },
  524. info: { structure, geometryVersion },
  525. repr: a.data.repr
  526. }, { label: `Substance (${substance.layers.length} Layers)` });
  527. },
  528. update({ a, b, newParams, oldParams }) {
  529. const info = b.data.info as { structure: Structure, geometryVersion: number };
  530. const newStructure = a.data.sourceData;
  531. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  532. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  533. const newGeometryVersion = a.data.repr.geometryVersion;
  534. // smoothing needs to be re-calculated when geometry changes
  535. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  536. const oldSubstance = b.data.state.substance!;
  537. const newSubstance = Substance.ofScript(newParams.layers, newStructure);
  538. if (Substance.areEqual(oldSubstance, newSubstance)) return StateTransformer.UpdateResult.Unchanged;
  539. info.geometryVersion = newGeometryVersion;
  540. b.data.state.substance = newSubstance;
  541. b.data.repr = a.data.repr;
  542. b.label = `Substance (${newSubstance.layers.length} Layers)`;
  543. return StateTransformer.UpdateResult.Updated;
  544. }
  545. });
  546. type SubstanceStructureRepresentation3DFromBundle = typeof SubstanceStructureRepresentation3DFromBundle
  547. const SubstanceStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  548. name: 'substance-structure-representation-3d-from-bundle',
  549. display: 'Substance 3D Representation',
  550. from: SO.Molecule.Structure.Representation3D,
  551. to: SO.Molecule.Structure.Representation3DState,
  552. params: () => ({
  553. layers: PD.ObjectList({
  554. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  555. material: Material.getParam(),
  556. clear: PD.Boolean(false)
  557. }, e => `${e.clear ? 'Clear' : Material.toString(e.material)}`, {
  558. defaultValue: [{
  559. bundle: StructureElement.Bundle.Empty,
  560. material: Material({ roughness: 1 }),
  561. clear: false
  562. }],
  563. isHidden: true
  564. }),
  565. })
  566. })({
  567. canAutoUpdate() {
  568. return true;
  569. },
  570. apply({ a, params }) {
  571. const structure = a.data.sourceData;
  572. const geometryVersion = a.data.repr.geometryVersion;
  573. const substance = Substance.ofBundle(params.layers, structure);
  574. return new SO.Molecule.Structure.Representation3DState({
  575. state: { substance },
  576. initialState: { substance: Substance.Empty },
  577. info: { structure, geometryVersion },
  578. repr: a.data.repr
  579. }, { label: `Substance (${substance.layers.length} Layers)` });
  580. },
  581. update({ a, b, newParams, oldParams }) {
  582. const info = b.data.info as { structure: Structure, geometryVersion: number };
  583. const newStructure = a.data.sourceData;
  584. if (newStructure !== info.structure) return StateTransformer.UpdateResult.Recreate;
  585. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  586. const newGeometryVersion = a.data.repr.geometryVersion;
  587. // smoothing needs to be re-calculated when geometry changes
  588. if (newGeometryVersion !== info.geometryVersion && hasColorSmoothingProp(a.data.repr.props)) return StateTransformer.UpdateResult.Unchanged;
  589. const oldSubstance = b.data.state.substance!;
  590. const newSubstance = Substance.ofBundle(newParams.layers, newStructure);
  591. if (Substance.areEqual(oldSubstance, newSubstance)) return StateTransformer.UpdateResult.Unchanged;
  592. info.geometryVersion = newGeometryVersion;
  593. b.data.state.substance = newSubstance;
  594. b.data.repr = a.data.repr;
  595. b.label = `Substance (${newSubstance.layers.length} Layers)`;
  596. return StateTransformer.UpdateResult.Updated;
  597. }
  598. });
  599. type ClippingStructureRepresentation3DFromScript = typeof ClippingStructureRepresentation3DFromScript
  600. const ClippingStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  601. name: 'clipping-structure-representation-3d-from-script',
  602. display: 'Clipping 3D Representation',
  603. from: SO.Molecule.Structure.Representation3D,
  604. to: SO.Molecule.Structure.Representation3DState,
  605. params: () => ({
  606. layers: PD.ObjectList({
  607. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  608. groups: PD.Converted((g: Clipping.Groups) => Clipping.Groups.toNames(g), n => Clipping.Groups.fromNames(n), PD.MultiSelect(ObjectKeys(Clipping.Groups.Names), PD.objectToOptions(Clipping.Groups.Names))),
  609. }, e => `${Clipping.Groups.toNames(e.groups).length} group(s)`, {
  610. defaultValue: [{
  611. script: Script('(sel.atom.all)', 'mol-script'),
  612. groups: Clipping.Groups.Flag.None,
  613. }]
  614. }),
  615. })
  616. })({
  617. canAutoUpdate() {
  618. return true;
  619. },
  620. apply({ a, params }) {
  621. const structure = a.data.sourceData;
  622. const clipping = Clipping.ofScript(params.layers, structure);
  623. return new SO.Molecule.Structure.Representation3DState({
  624. state: { clipping },
  625. initialState: { clipping: Clipping.Empty },
  626. info: structure,
  627. repr: a.data.repr
  628. }, { label: `Clipping (${clipping.layers.length} Layers)` });
  629. },
  630. update({ a, b, newParams, oldParams }) {
  631. const structure = b.data.info as Structure;
  632. if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
  633. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  634. const oldClipping = b.data.state.clipping!;
  635. const newClipping = Clipping.ofScript(newParams.layers, structure);
  636. if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
  637. b.data.state.clipping = newClipping;
  638. b.data.repr = a.data.repr;
  639. b.label = `Clipping (${newClipping.layers.length} Layers)`;
  640. return StateTransformer.UpdateResult.Updated;
  641. }
  642. });
  643. type ClippingStructureRepresentation3DFromBundle = typeof ClippingStructureRepresentation3DFromBundle
  644. const ClippingStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  645. name: 'clipping-structure-representation-3d-from-bundle',
  646. display: 'Clipping 3D Representation',
  647. from: SO.Molecule.Structure.Representation3D,
  648. to: SO.Molecule.Structure.Representation3DState,
  649. params: () => ({
  650. layers: PD.ObjectList({
  651. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  652. groups: PD.Converted((g: Clipping.Groups) => Clipping.Groups.toNames(g), n => Clipping.Groups.fromNames(n), PD.MultiSelect(ObjectKeys(Clipping.Groups.Names), PD.objectToOptions(Clipping.Groups.Names))),
  653. }, e => `${Clipping.Groups.toNames(e.groups).length} group(s)`, {
  654. defaultValue: [{
  655. bundle: StructureElement.Bundle.Empty,
  656. groups: Clipping.Groups.Flag.None,
  657. }],
  658. isHidden: true
  659. }),
  660. })
  661. })({
  662. canAutoUpdate() {
  663. return true;
  664. },
  665. apply({ a, params }) {
  666. const structure = a.data.sourceData;
  667. const clipping = Clipping.ofBundle(params.layers, structure);
  668. return new SO.Molecule.Structure.Representation3DState({
  669. state: { clipping },
  670. initialState: { clipping: Clipping.Empty },
  671. info: structure,
  672. repr: a.data.repr
  673. }, { label: `Clipping (${clipping.layers.length} Layers)` });
  674. },
  675. update({ a, b, newParams, oldParams }) {
  676. const structure = b.data.info as Structure;
  677. if (a.data.sourceData !== structure) return StateTransformer.UpdateResult.Recreate;
  678. if (a.data.repr !== b.data.repr) return StateTransformer.UpdateResult.Recreate;
  679. const oldClipping = b.data.state.clipping!;
  680. const newClipping = Clipping.ofBundle(newParams.layers, structure);
  681. if (Clipping.areEqual(oldClipping, newClipping)) return StateTransformer.UpdateResult.Unchanged;
  682. b.data.state.clipping = newClipping;
  683. b.data.repr = a.data.repr;
  684. b.label = `Clipping (${newClipping.layers.length} Layers)`;
  685. return StateTransformer.UpdateResult.Updated;
  686. }
  687. });
  688. //
  689. export namespace VolumeRepresentation3DHelpers {
  690. export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: Volume, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
  691. const type = ctx.representation.volume.registry.get(name);
  692. const themeDataCtx = { volume };
  693. const colorParams = ctx.representation.volume.themes.colorThemeRegistry.get(type.defaultColorTheme.name).getParams(themeDataCtx);
  694. const sizeParams = ctx.representation.volume.themes.sizeThemeRegistry.get(type.defaultSizeTheme.name).getParams(themeDataCtx);
  695. const volumeDefaultParams = PD.getDefaultValues(type.getParams(ctx.representation.volume.themes, volume));
  696. return ({
  697. type: { name, params: volumeParams ? { ...volumeDefaultParams, ...volumeParams } : volumeDefaultParams },
  698. colorTheme: { name: type.defaultColorTheme.name, params: PD.getDefaultValues(colorParams) },
  699. sizeTheme: { name: type.defaultSizeTheme.name, params: PD.getDefaultValues(sizeParams) }
  700. });
  701. }
  702. export function getDefaultParamsStatic(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volumeParams?: Partial<PD.Values<PD.Params>>, colorName?: ColorTheme.BuiltIn, colorParams?: Partial<ColorTheme.Props>, sizeName?: SizeTheme.BuiltIn, sizeParams?: Partial<SizeTheme.Props>): StateTransformer.Params<VolumeRepresentation3D> {
  703. const type = ctx.representation.volume.registry.get(name);
  704. const colorType = ctx.representation.volume.themes.colorThemeRegistry.get(colorName || type.defaultColorTheme.name);
  705. const sizeType = ctx.representation.volume.themes.sizeThemeRegistry.get(sizeName || type.defaultSizeTheme.name);
  706. return ({
  707. type: { name, params: volumeParams ? { ...type.defaultValues, ...volumeParams } : type.defaultValues },
  708. colorTheme: { name: type.defaultColorTheme.name, params: colorParams ? { ...colorType.defaultValues, ...colorParams } : colorType.defaultValues },
  709. sizeTheme: { name: type.defaultSizeTheme.name, params: sizeParams ? { ...sizeType.defaultValues, ...sizeParams } : sizeType.defaultValues }
  710. });
  711. }
  712. export function getDescription(props: any) {
  713. if (props.isoValue) {
  714. return Volume.IsoValue.toString(props.isoValue);
  715. } else if (props.renderMode?.params?.isoValue) {
  716. return Volume.IsoValue.toString(props.renderMode?.params?.isoValue);
  717. }
  718. }
  719. }
  720. type VolumeRepresentation3D = typeof VolumeRepresentation3D
  721. const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
  722. name: 'volume-representation-3d',
  723. display: '3D Representation',
  724. from: SO.Volume.Data,
  725. to: SO.Volume.Representation3D,
  726. params: (a, ctx: PluginContext) => {
  727. const { registry, themes: themeCtx } = ctx.representation.volume;
  728. const type = registry.get(registry.default.name);
  729. if (!a) {
  730. return {
  731. type: PD.Mapped<any>(
  732. registry.default.name,
  733. registry.types,
  734. name => PD.Group<any>(registry.get(name).getParams(themeCtx, Volume.One))),
  735. colorTheme: PD.Mapped<any>(
  736. type.defaultColorTheme.name,
  737. themeCtx.colorThemeRegistry.types,
  738. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: Volume.One }))
  739. ),
  740. sizeTheme: PD.Mapped<any>(
  741. type.defaultSizeTheme.name,
  742. themeCtx.sizeThemeRegistry.types,
  743. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: Volume.One }))
  744. )
  745. };
  746. }
  747. const dataCtx = { volume: a.data };
  748. return ({
  749. type: PD.Mapped<any>(
  750. registry.default.name,
  751. registry.types,
  752. name => PD.Group<any>(registry.get(name).getParams(themeCtx, a.data))),
  753. colorTheme: PD.Mapped<any>(
  754. type.defaultColorTheme.name,
  755. themeCtx.colorThemeRegistry.getApplicableTypes(dataCtx),
  756. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams(dataCtx))
  757. ),
  758. sizeTheme: PD.Mapped<any>(
  759. type.defaultSizeTheme.name,
  760. themeCtx.sizeThemeRegistry.types,
  761. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams(dataCtx))
  762. )
  763. });
  764. }
  765. })({
  766. canAutoUpdate({ oldParams, newParams }) {
  767. return oldParams.type.name === newParams.type.name;
  768. },
  769. apply({ a, params }, plugin: PluginContext) {
  770. return Task.create('Volume Representation', async ctx => {
  771. const propertyCtx = { runtime: ctx, assetManager: plugin.managers.asset };
  772. const provider = plugin.representation.volume.registry.get(params.type.name);
  773. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data);
  774. const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams);
  775. repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, params));
  776. const props = params.type.params || {};
  777. await repr.createOrUpdate(props, a.data).runInContext(ctx);
  778. return new SO.Volume.Representation3D({ repr, sourceData: a.data }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
  779. });
  780. },
  781. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  782. return Task.create('Volume Representation', async ctx => {
  783. if (newParams.type.name !== oldParams.type.name) {
  784. const oldProvider = plugin.representation.volume.registry.get(oldParams.type.name);
  785. oldProvider.ensureCustomProperties?.detach(a.data);
  786. return StateTransformer.UpdateResult.Recreate;
  787. }
  788. const props = { ...b.data.repr.props, ...newParams.type.params };
  789. b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams));
  790. await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
  791. b.data.sourceData = a.data;
  792. b.description = VolumeRepresentation3DHelpers.getDescription(props);
  793. return StateTransformer.UpdateResult.Updated;
  794. });
  795. }
  796. });
  797. //
  798. export { ShapeRepresentation3D };
  799. type ShapeRepresentation3D = typeof ShapeRepresentation3D
  800. const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
  801. name: 'shape-representation-3d',
  802. display: '3D Representation',
  803. from: SO.Shape.Provider,
  804. to: SO.Shape.Representation3D,
  805. params: (a, ctx: PluginContext) => {
  806. return a ? a.data.params : BaseGeometry.Params;
  807. }
  808. })({
  809. canAutoUpdate() {
  810. return true;
  811. },
  812. apply({ a, params }, plugin: PluginContext) {
  813. return Task.create('Shape Representation', async ctx => {
  814. const props = { ...PD.getDefaultValues(a.data.params), ...params };
  815. const repr = ShapeRepresentation(a.data.getShape, a.data.geometryUtils);
  816. await repr.createOrUpdate(props, a.data.data).runInContext(ctx);
  817. return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: a.data.label });
  818. });
  819. },
  820. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  821. return Task.create('Shape Representation', async ctx => {
  822. const props = { ...b.data.repr.props, ...newParams };
  823. await b.data.repr.createOrUpdate(props, a.data.data).runInContext(ctx);
  824. b.data.sourceData = a.data;
  825. return StateTransformer.UpdateResult.Updated;
  826. });
  827. }
  828. });
  829. export { ModelUnitcell3D };
  830. type ModelUnitcell3D = typeof ModelUnitcell3D
  831. const ModelUnitcell3D = PluginStateTransform.BuiltIn({
  832. name: 'model-unitcell-3d',
  833. display: 'Model Unit Cell',
  834. from: SO.Molecule.Model,
  835. to: SO.Shape.Representation3D,
  836. params: () => ({
  837. ...UnitcellParams,
  838. })
  839. })({
  840. isApplicable: a => !!ModelSymmetry.Provider.get(a.data),
  841. canAutoUpdate({ oldParams, newParams }) {
  842. return true;
  843. },
  844. apply({ a, params }, plugin: PluginContext) {
  845. return Task.create('Model Unit Cell', async ctx => {
  846. const symmetry = ModelSymmetry.Provider.get(a.data);
  847. if (!symmetry) return StateObject.Null;
  848. const data = getUnitcellData(a.data, symmetry, params);
  849. const repr = UnitcellRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => UnitcellParams);
  850. await repr.createOrUpdate(params, data).runInContext(ctx);
  851. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Unit Cell`, description: symmetry.spacegroup.name });
  852. });
  853. },
  854. update({ a, b, newParams }) {
  855. return Task.create('Model Unit Cell', async ctx => {
  856. const symmetry = ModelSymmetry.Provider.get(a.data);
  857. if (!symmetry) return StateTransformer.UpdateResult.Null;
  858. const props = { ...b.data.repr.props, ...newParams };
  859. const data = getUnitcellData(a.data, symmetry, props);
  860. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  861. b.data.sourceData = data;
  862. return StateTransformer.UpdateResult.Updated;
  863. });
  864. }
  865. });
  866. export { StructureBoundingBox3D };
  867. type StructureBoundingBox3D = typeof StructureBoundingBox3D
  868. const StructureBoundingBox3D = PluginStateTransform.BuiltIn({
  869. name: 'structure-bounding-box-3d',
  870. display: 'Bounding Box',
  871. from: SO.Molecule.Structure,
  872. to: SO.Shape.Representation3D,
  873. params: {
  874. radius: PD.Numeric(0.05, { min: 0.01, max: 4, step: 0.01 }, { isEssential: true }),
  875. color: PD.Color(ColorNames.red, { isEssential: true }),
  876. ...Mesh.Params,
  877. }
  878. })({
  879. canAutoUpdate() {
  880. return true;
  881. },
  882. apply({ a, params }, plugin: PluginContext) {
  883. return Task.create('Bounding Box', async ctx => {
  884. const repr = ShapeRepresentation((_, data: { box: Box3D, radius: number, color: Color }, __, shape) => {
  885. const mesh = getBoxMesh(data.box, data.radius, shape?.geometry);
  886. return Shape.create('Bouding Box', data, mesh, () => data.color, () => 1, () => 'Bounding Box');
  887. }, Mesh.Utils);
  888. await repr.createOrUpdate(params, { box: a.data.boundary.box, radius: params.radius, color: params.color }).runInContext(ctx);
  889. return new SO.Shape.Representation3D({ repr, sourceData: a.data }, { label: `Bounding Box` });
  890. });
  891. },
  892. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  893. return Task.create('Bounding Box', async ctx => {
  894. await b.data.repr.createOrUpdate(newParams, { box: a.data.boundary.box, radius: newParams.radius, color: newParams.color }).runInContext(ctx);
  895. b.data.sourceData = a.data;
  896. return StateTransformer.UpdateResult.Updated;
  897. });
  898. }
  899. });
  900. export { StructureSelectionsDistance3D };
  901. type StructureSelectionsDistance3D = typeof StructureSelectionsDistance3D
  902. const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
  903. name: 'structure-selections-distance-3d',
  904. display: '3D Distance',
  905. from: SO.Molecule.Structure.Selections,
  906. to: SO.Shape.Representation3D,
  907. params: () => ({
  908. ...DistanceParams,
  909. })
  910. })({
  911. canAutoUpdate({ oldParams, newParams }) {
  912. return true;
  913. },
  914. apply({ a, params }, plugin: PluginContext) {
  915. return Task.create('Structure Distance', async ctx => {
  916. const data = getDistanceDataFromStructureSelections(a.data);
  917. const repr = DistanceRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DistanceParams);
  918. await repr.createOrUpdate(params, data).runInContext(ctx);
  919. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Distance` });
  920. });
  921. },
  922. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  923. return Task.create('Structure Distance', async ctx => {
  924. const props = { ...b.data.repr.props, ...newParams };
  925. const data = getDistanceDataFromStructureSelections(a.data);
  926. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  927. b.data.sourceData = data;
  928. return StateTransformer.UpdateResult.Updated;
  929. });
  930. },
  931. });
  932. export { StructureSelectionsAngle3D };
  933. type StructureSelectionsAngle3D = typeof StructureSelectionsAngle3D
  934. const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
  935. name: 'structure-selections-angle-3d',
  936. display: '3D Angle',
  937. from: SO.Molecule.Structure.Selections,
  938. to: SO.Shape.Representation3D,
  939. params: () => ({
  940. ...AngleParams,
  941. })
  942. })({
  943. canAutoUpdate({ oldParams, newParams }) {
  944. return true;
  945. },
  946. apply({ a, params }, plugin: PluginContext) {
  947. return Task.create('Structure Angle', async ctx => {
  948. const data = getAngleDataFromStructureSelections(a.data);
  949. const repr = AngleRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AngleParams);
  950. await repr.createOrUpdate(params, data).runInContext(ctx);
  951. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Angle` });
  952. });
  953. },
  954. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  955. return Task.create('Structure Angle', async ctx => {
  956. const props = { ...b.data.repr.props, ...newParams };
  957. const data = getAngleDataFromStructureSelections(a.data);
  958. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  959. b.data.sourceData = data;
  960. return StateTransformer.UpdateResult.Updated;
  961. });
  962. },
  963. });
  964. export { StructureSelectionsDihedral3D };
  965. type StructureSelectionsDihedral3D = typeof StructureSelectionsDihedral3D
  966. const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
  967. name: 'structure-selections-dihedral-3d',
  968. display: '3D Dihedral',
  969. from: SO.Molecule.Structure.Selections,
  970. to: SO.Shape.Representation3D,
  971. params: () => ({
  972. ...DihedralParams,
  973. })
  974. })({
  975. canAutoUpdate({ oldParams, newParams }) {
  976. return true;
  977. },
  978. apply({ a, params }, plugin: PluginContext) {
  979. return Task.create('Structure Dihedral', async ctx => {
  980. const data = getDihedralDataFromStructureSelections(a.data);
  981. const repr = DihedralRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DihedralParams);
  982. await repr.createOrUpdate(params, data).runInContext(ctx);
  983. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Dihedral` });
  984. });
  985. },
  986. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  987. return Task.create('Structure Dihedral', async ctx => {
  988. const props = { ...b.data.repr.props, ...newParams };
  989. const data = getDihedralDataFromStructureSelections(a.data);
  990. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  991. b.data.sourceData = data;
  992. return StateTransformer.UpdateResult.Updated;
  993. });
  994. },
  995. });
  996. export { StructureSelectionsLabel3D };
  997. type StructureSelectionsLabel3D = typeof StructureSelectionsLabel3D
  998. const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
  999. name: 'structure-selections-label-3d',
  1000. display: '3D Label',
  1001. from: SO.Molecule.Structure.Selections,
  1002. to: SO.Shape.Representation3D,
  1003. params: () => ({
  1004. ...LabelParams,
  1005. })
  1006. })({
  1007. canAutoUpdate({ oldParams, newParams }) {
  1008. return true;
  1009. },
  1010. apply({ a, params }, plugin: PluginContext) {
  1011. return Task.create('Structure Label', async ctx => {
  1012. const data = getLabelDataFromStructureSelections(a.data);
  1013. const repr = LabelRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => LabelParams);
  1014. await repr.createOrUpdate(params, data).runInContext(ctx);
  1015. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Label` });
  1016. });
  1017. },
  1018. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  1019. return Task.create('Structure Label', async ctx => {
  1020. const props = { ...b.data.repr.props, ...newParams };
  1021. const data = getLabelDataFromStructureSelections(a.data);
  1022. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  1023. b.data.sourceData = data;
  1024. return StateTransformer.UpdateResult.Updated;
  1025. });
  1026. },
  1027. });
  1028. export { StructureSelectionsOrientation3D };
  1029. type StructureSelectionsOrientation3D = typeof StructureSelectionsOrientation3D
  1030. const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
  1031. name: 'structure-selections-orientation-3d',
  1032. display: '3D Orientation',
  1033. from: SO.Molecule.Structure.Selections,
  1034. to: SO.Shape.Representation3D,
  1035. params: () => ({
  1036. ...OrientationParams,
  1037. })
  1038. })({
  1039. canAutoUpdate({ oldParams, newParams }) {
  1040. return true;
  1041. },
  1042. apply({ a, params }, plugin: PluginContext) {
  1043. return Task.create('Structure Orientation', async ctx => {
  1044. const data = getOrientationDataFromStructureSelections(a.data);
  1045. const repr = OrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => OrientationParams);
  1046. await repr.createOrUpdate(params, data).runInContext(ctx);
  1047. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Orientation` });
  1048. });
  1049. },
  1050. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  1051. return Task.create('Structure Orientation', async ctx => {
  1052. const props = { ...b.data.repr.props, ...newParams };
  1053. const data = getOrientationDataFromStructureSelections(a.data);
  1054. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  1055. b.data.sourceData = data;
  1056. return StateTransformer.UpdateResult.Updated;
  1057. });
  1058. },
  1059. });
  1060. export { StructureSelectionsPlane3D };
  1061. type StructureSelectionsPlane3D = typeof StructureSelectionsPlane3D
  1062. const StructureSelectionsPlane3D = PluginStateTransform.BuiltIn({
  1063. name: 'structure-selections-plane-3d',
  1064. display: '3D Plane',
  1065. from: SO.Molecule.Structure.Selections,
  1066. to: SO.Shape.Representation3D,
  1067. params: () => ({
  1068. ...PlaneParams,
  1069. })
  1070. })({
  1071. canAutoUpdate({ oldParams, newParams }) {
  1072. return true;
  1073. },
  1074. apply({ a, params }, plugin: PluginContext) {
  1075. return Task.create('Structure Plane', async ctx => {
  1076. const data = getPlaneDataFromStructureSelections(a.data);
  1077. const repr = PlaneRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => PlaneParams);
  1078. await repr.createOrUpdate(params, data).runInContext(ctx);
  1079. return new SO.Shape.Representation3D({ repr, sourceData: data }, { label: `Plane` });
  1080. });
  1081. },
  1082. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  1083. return Task.create('Structure Plane', async ctx => {
  1084. const props = { ...b.data.repr.props, ...newParams };
  1085. const data = getPlaneDataFromStructureSelections(a.data);
  1086. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  1087. b.data.sourceData = data;
  1088. return StateTransformer.UpdateResult.Updated;
  1089. });
  1090. },
  1091. });