representation.ts 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /**
  2. * Copyright (c) 2018-2020 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 { VolumeData, VolumeIsoValue } 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 } 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 } 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 } 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. export { StructureRepresentation3D }
  37. export { ExplodeStructureRepresentation3D }
  38. export { UnwindStructureAssemblyRepresentation3D }
  39. export { OverpaintStructureRepresentation3DFromScript }
  40. export { OverpaintStructureRepresentation3DFromBundle }
  41. export { TransparencyStructureRepresentation3DFromScript }
  42. export { TransparencyStructureRepresentation3DFromBundle }
  43. export { VolumeRepresentation3D }
  44. type StructureRepresentation3D = typeof StructureRepresentation3D
  45. const StructureRepresentation3D = PluginStateTransform.BuiltIn({
  46. name: 'structure-representation-3d',
  47. display: '3D Representation',
  48. from: SO.Molecule.Structure,
  49. to: SO.Molecule.Structure.Representation3D,
  50. params: (a, ctx: PluginContext) => {
  51. const { registry, themes: themeCtx } = ctx.representation.structure
  52. const type = registry.get(registry.default.name);
  53. if (!a) {
  54. const colorThemeInfo = {
  55. help: (value: { name: string, params: {} }) => {
  56. const { name, params } = value
  57. const p = themeCtx.colorThemeRegistry.get(name)
  58. const ct = p.factory({}, params)
  59. return { description: ct.description, legend: ct.legend }
  60. }
  61. }
  62. return {
  63. type: PD.Mapped<any>(
  64. registry.default.name,
  65. registry.types,
  66. name => PD.Group<any>(registry.get(name).getParams(themeCtx, Structure.Empty))),
  67. colorTheme: PD.Mapped<any>(
  68. type.defaultColorTheme.name,
  69. themeCtx.colorThemeRegistry.types,
  70. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ structure: Structure.Empty })),
  71. colorThemeInfo
  72. ),
  73. sizeTheme: PD.Mapped<any>(
  74. type.defaultSizeTheme.name,
  75. themeCtx.sizeThemeRegistry.types,
  76. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ structure: Structure.Empty }))
  77. )
  78. }
  79. }
  80. const dataCtx = { structure: a.data }
  81. const colorThemeInfo = {
  82. help: (value: { name: string, params: {} }) => {
  83. const { name, params } = value
  84. const p = themeCtx.colorThemeRegistry.get(name)
  85. const ct = p.factory(dataCtx, params)
  86. return { description: ct.description, legend: ct.legend }
  87. }
  88. }
  89. return ({
  90. type: PD.Mapped<any>(
  91. registry.default.name,
  92. registry.getApplicableTypes(a.data),
  93. name => PD.Group<any>(registry.get(name).getParams(themeCtx, a.data))),
  94. colorTheme: PD.Mapped<any>(
  95. type.defaultColorTheme.name,
  96. themeCtx.colorThemeRegistry.getApplicableTypes(dataCtx),
  97. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams(dataCtx)),
  98. colorThemeInfo
  99. ),
  100. sizeTheme: PD.Mapped<any>(
  101. type.defaultSizeTheme.name,
  102. themeCtx.sizeThemeRegistry.types,
  103. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams(dataCtx))
  104. )
  105. })
  106. }
  107. })({
  108. canAutoUpdate({ a, oldParams, newParams }) {
  109. // TODO: other criteria as well?
  110. return a.data.elementCount < 10000 || (
  111. oldParams.type.name === newParams.type.name && newParams.type.params.quality !== 'custom'
  112. );
  113. },
  114. apply({ a, params }, plugin: PluginContext) {
  115. return Task.create('Structure Representation', async ctx => {
  116. const propertyCtx = { runtime: ctx, fetch: plugin.fetch }
  117. const provider = plugin.representation.structure.registry.get(params.type.name)
  118. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data)
  119. const props = params.type.params || {}
  120. const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, provider.getParams)
  121. await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, params)
  122. repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, params))
  123. // TODO set initial state, repr.setState({})
  124. await repr.createOrUpdate(props, a.data).runInContext(ctx);
  125. return new SO.Molecule.Structure.Representation3D({ repr, source: a } , { label: provider.label });
  126. });
  127. },
  128. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  129. return Task.create('Structure Representation', async ctx => {
  130. const oldProvider = plugin.representation.structure.registry.get(oldParams.type.name);
  131. const propertyCtx = { runtime: ctx, fetch: plugin.fetch }
  132. if (oldProvider.ensureCustomProperties) oldProvider.ensureCustomProperties.detach(propertyCtx, a.data);
  133. Theme.releaseDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, oldParams);
  134. if (newParams.type.name !== oldParams.type.name) return StateTransformer.UpdateResult.Recreate;
  135. const provider = plugin.representation.structure.registry.get(newParams.type.name)
  136. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data)
  137. const props = { ...b.data.repr.props, ...newParams.type.params }
  138. await Theme.ensureDependencies(propertyCtx, plugin.representation.structure.themes, { structure: a.data }, newParams)
  139. b.data.repr.setTheme(Theme.create(plugin.representation.structure.themes, { structure: a.data }, newParams));
  140. await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
  141. b.data.source = a
  142. return StateTransformer.UpdateResult.Updated;
  143. });
  144. },
  145. interpolate(src, tar, t) {
  146. if (src.colorTheme.name !== 'uniform' || tar.colorTheme.name !== 'uniform') {
  147. return t <= 0.5 ? src : tar;
  148. }
  149. const from = src.colorTheme.params.value as Color, to = tar.colorTheme.params.value as Color;
  150. const value = Color.interpolate(from, to, t);
  151. return {
  152. type: t <= 0.5 ? src.type : tar.type,
  153. colorTheme: { name: 'uniform', params: { value } },
  154. sizeTheme: t <= 0.5 ? src.sizeTheme : tar.sizeTheme,
  155. };
  156. }
  157. });
  158. type UnwindStructureAssemblyRepresentation3D = typeof UnwindStructureAssemblyRepresentation3D
  159. const UnwindStructureAssemblyRepresentation3D = PluginStateTransform.BuiltIn({
  160. name: 'unwind-structure-assembly-representation-3d',
  161. display: 'Unwind Assembly 3D Representation',
  162. from: SO.Molecule.Structure.Representation3D,
  163. to: SO.Molecule.Structure.Representation3DState,
  164. params: { t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }) }
  165. })({
  166. canAutoUpdate() {
  167. return true;
  168. },
  169. apply({ a, params }) {
  170. const structure = a.data.source.data;
  171. const unitTransforms = new StructureUnitTransforms(structure);
  172. unwindStructureAssembly(structure, unitTransforms, params.t);
  173. return new SO.Molecule.Structure.Representation3DState({
  174. state: { unitTransforms },
  175. initialState: { unitTransforms: new StructureUnitTransforms(structure) },
  176. info: structure,
  177. source: a
  178. }, { label: `Unwind T = ${params.t.toFixed(2)}` });
  179. },
  180. update({ a, b, newParams, oldParams }) {
  181. const structure = b.data.info as Structure;
  182. if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate;
  183. if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
  184. const unitTransforms = b.data.state.unitTransforms!;
  185. unwindStructureAssembly(structure, unitTransforms, newParams.t);
  186. b.label = `Unwind T = ${newParams.t.toFixed(2)}`;
  187. b.data.source = a;
  188. return StateTransformer.UpdateResult.Updated;
  189. }
  190. });
  191. type ExplodeStructureRepresentation3D = typeof ExplodeStructureRepresentation3D
  192. const ExplodeStructureRepresentation3D = PluginStateTransform.BuiltIn({
  193. name: 'explode-structure-representation-3d',
  194. display: 'Explode 3D Representation',
  195. from: SO.Molecule.Structure.Representation3D,
  196. to: SO.Molecule.Structure.Representation3DState,
  197. params: { t: PD.Numeric(0, { min: 0, max: 1, step: 0.01 }) }
  198. })({
  199. canAutoUpdate() {
  200. return true;
  201. },
  202. apply({ a, params }) {
  203. const structure = a.data.source.data;
  204. const unitTransforms = new StructureUnitTransforms(structure.root);
  205. explodeStructure(structure, unitTransforms, params.t);
  206. return new SO.Molecule.Structure.Representation3DState({
  207. state: { unitTransforms },
  208. initialState: { unitTransforms: new StructureUnitTransforms(structure.root) },
  209. info: structure.root,
  210. source: a
  211. }, { label: `Explode T = ${params.t.toFixed(2)}` });
  212. },
  213. update({ a, b, newParams, oldParams }) {
  214. const structure = a.data.source.data;
  215. if (b.data.info !== structure.root) return StateTransformer.UpdateResult.Recreate;
  216. if (oldParams.t === newParams.t) return StateTransformer.UpdateResult.Unchanged;
  217. const unitTransforms = b.data.state.unitTransforms!;
  218. explodeStructure(structure.root, unitTransforms, newParams.t);
  219. b.label = `Explode T = ${newParams.t.toFixed(2)}`;
  220. b.data.source = a;
  221. return StateTransformer.UpdateResult.Updated;
  222. }
  223. });
  224. type OverpaintStructureRepresentation3DFromScript = typeof OverpaintStructureRepresentation3DFromScript
  225. const OverpaintStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  226. name: 'overpaint-structure-representation-3d-from-script',
  227. display: 'Overpaint 3D Representation',
  228. from: SO.Molecule.Structure.Representation3D,
  229. to: SO.Molecule.Structure.Representation3DState,
  230. params: {
  231. layers: PD.ObjectList({
  232. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  233. color: PD.Color(ColorNames.blueviolet),
  234. clear: PD.Boolean(false)
  235. }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, {
  236. defaultValue: [{
  237. script: Script('(sel.atom.all)', 'mol-script'),
  238. color: ColorNames.blueviolet,
  239. clear: false
  240. }]
  241. }),
  242. alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
  243. }
  244. })({
  245. canAutoUpdate() {
  246. return true;
  247. },
  248. apply({ a, params }) {
  249. const structure = a.data.source.data
  250. const overpaint = Overpaint.ofScript(params.layers, params.alpha, structure)
  251. return new SO.Molecule.Structure.Representation3DState({
  252. state: { overpaint },
  253. initialState: { overpaint: Overpaint.Empty },
  254. info: structure,
  255. source: a
  256. }, { label: `Overpaint (${overpaint.layers.length} Layers)` })
  257. },
  258. update({ a, b, newParams, oldParams }) {
  259. const oldStructure = b.data.info as Structure
  260. const newStructure = a.data.source.data
  261. if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate
  262. const oldOverpaint = b.data.state.overpaint!
  263. const newOverpaint = Overpaint.ofScript(newParams.layers, newParams.alpha, newStructure)
  264. if (oldParams.alpha === newParams.alpha && Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged
  265. b.data.state.overpaint = newOverpaint
  266. b.data.source = a
  267. b.label = `Overpaint (${newOverpaint.layers.length} Layers)`
  268. return StateTransformer.UpdateResult.Updated
  269. }
  270. });
  271. type OverpaintStructureRepresentation3DFromBundle = typeof OverpaintStructureRepresentation3DFromBundle
  272. const OverpaintStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  273. name: 'overpaint-structure-representation-3d-from-bundle',
  274. display: 'Overpaint 3D Representation',
  275. from: SO.Molecule.Structure.Representation3D,
  276. to: SO.Molecule.Structure.Representation3DState,
  277. params: {
  278. layers: PD.ObjectList({
  279. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  280. color: PD.Color(ColorNames.blueviolet),
  281. clear: PD.Boolean(false)
  282. }, e => `${e.clear ? 'Clear' : Color.toRgbString(e.color)}`, {
  283. defaultValue: [{
  284. bundle: StructureElement.Bundle.Empty,
  285. color: ColorNames.blueviolet,
  286. clear: false
  287. }],
  288. isHidden: true
  289. }),
  290. alpha: PD.Numeric(1, { min: 0, max: 1, step: 0.01 }, { label: 'Opacity' }),
  291. }
  292. })({
  293. canAutoUpdate() {
  294. return true;
  295. },
  296. apply({ a, params }) {
  297. const structure = a.data.source.data
  298. const overpaint = Overpaint.ofBundle(params.layers, params.alpha, structure)
  299. return new SO.Molecule.Structure.Representation3DState({
  300. state: { overpaint },
  301. initialState: { overpaint: Overpaint.Empty },
  302. info: structure,
  303. source: a
  304. }, { label: `Overpaint (${overpaint.layers.length} Layers)` })
  305. },
  306. update({ a, b, newParams, oldParams }) {
  307. const oldStructure = b.data.info as Structure
  308. const newStructure = a.data.source.data
  309. if (newStructure !== oldStructure) return StateTransformer.UpdateResult.Recreate
  310. const oldOverpaint = b.data.state.overpaint!
  311. const newOverpaint = Overpaint.ofBundle(newParams.layers, newParams.alpha, newStructure)
  312. if (oldParams.alpha === newParams.alpha && Overpaint.areEqual(oldOverpaint, newOverpaint)) return StateTransformer.UpdateResult.Unchanged
  313. b.data.state.overpaint = newOverpaint
  314. b.data.source = a
  315. b.label = `Overpaint (${newOverpaint.layers.length} Layers)`
  316. return StateTransformer.UpdateResult.Updated
  317. }
  318. });
  319. type TransparencyStructureRepresentation3DFromScript = typeof TransparencyStructureRepresentation3DFromScript
  320. const TransparencyStructureRepresentation3DFromScript = PluginStateTransform.BuiltIn({
  321. name: 'transparency-structure-representation-3d-from-script',
  322. display: 'Transparency 3D Representation',
  323. from: SO.Molecule.Structure.Representation3D,
  324. to: SO.Molecule.Structure.Representation3DState,
  325. params: {
  326. script: PD.Script(Script('(sel.atom.all)', 'mol-script')),
  327. value: PD.Numeric(0.75, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
  328. variant: PD.Select('single', [['single', 'Single-layer'], ['multi', 'Multi-layer']] as ['single' | 'multi', string][])
  329. }
  330. })({
  331. canAutoUpdate() {
  332. return true;
  333. },
  334. apply({ a, params }) {
  335. const structure = a.data.source.data
  336. const transparency = Transparency.ofScript(params.script, params.value, params.variant, structure)
  337. return new SO.Molecule.Structure.Representation3DState({
  338. state: { transparency },
  339. initialState: { transparency: Transparency.Empty },
  340. info: structure,
  341. source: a
  342. }, { label: `Transparency (${transparency.value})` })
  343. },
  344. update({ a, b, newParams, oldParams }) {
  345. const structure = b.data.info as Structure
  346. if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate
  347. const oldTransparency = b.data.state.transparency!
  348. const newTransparency = Transparency.ofScript(newParams.script, newParams.value, newParams.variant, structure)
  349. if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged
  350. b.data.state.transparency = newTransparency
  351. b.data.source = a
  352. b.label = `Transparency (${newTransparency.value})`
  353. return StateTransformer.UpdateResult.Updated
  354. }
  355. });
  356. type TransparencyStructureRepresentation3DFromBundle = typeof TransparencyStructureRepresentation3DFromBundle
  357. const TransparencyStructureRepresentation3DFromBundle = PluginStateTransform.BuiltIn({
  358. name: 'transparency-structure-representation-3d-from-bundle',
  359. display: 'Transparency 3D Representation',
  360. from: SO.Molecule.Structure.Representation3D,
  361. to: SO.Molecule.Structure.Representation3DState,
  362. params: {
  363. bundle: PD.Value<StructureElement.Bundle>(StructureElement.Bundle.Empty),
  364. value: PD.Numeric(0.75, { min: 0, max: 1, step: 0.01 }, { label: 'Transparency' }),
  365. variant: PD.Select('single', [['single', 'Single-layer'], ['multi', 'Multi-layer']] as ['single' | 'multi', string][])
  366. }
  367. })({
  368. canAutoUpdate() {
  369. return true;
  370. },
  371. apply({ a, params }) {
  372. const structure = a.data.source.data
  373. const transparency = Transparency.ofBundle(params.bundle, params.value, params.variant, structure)
  374. return new SO.Molecule.Structure.Representation3DState({
  375. state: { transparency },
  376. initialState: { transparency: Transparency.Empty },
  377. info: structure,
  378. source: a
  379. }, { label: `Transparency (${transparency.value})` })
  380. },
  381. update({ a, b, newParams, oldParams }) {
  382. const structure = b.data.info as Structure
  383. if (a.data.source.data !== structure) return StateTransformer.UpdateResult.Recreate
  384. const oldTransparency = b.data.state.transparency!
  385. const newTransparency = Transparency.ofBundle(newParams.bundle, newParams.value, newParams.variant, structure)
  386. if (Transparency.areEqual(oldTransparency, newTransparency)) return StateTransformer.UpdateResult.Unchanged
  387. b.data.state.transparency = newTransparency
  388. b.data.source = a
  389. b.label = `Transparency (${newTransparency.value})`
  390. return StateTransformer.UpdateResult.Updated
  391. }
  392. });
  393. //
  394. export namespace VolumeRepresentation3DHelpers {
  395. export function getDefaultParams(ctx: PluginContext, name: VolumeRepresentationRegistry.BuiltIn, volume: VolumeData, volumeParams?: Partial<PD.Values<VolumeParams>>): StateTransformer.Params<VolumeRepresentation3D> {
  396. const type = ctx.representation.volume.registry.get(name);
  397. const themeDataCtx = { volume };
  398. const colorParams = ctx.representation.volume.themes.colorThemeRegistry.get(type.defaultColorTheme.name).getParams(themeDataCtx);
  399. const sizeParams = ctx.representation.volume.themes.sizeThemeRegistry.get(type.defaultSizeTheme.name).getParams(themeDataCtx)
  400. const volumeDefaultParams = PD.getDefaultValues(type.getParams(ctx.representation.volume.themes, volume))
  401. return ({
  402. type: { name, params: volumeParams ? { ...volumeDefaultParams, ...volumeParams } : volumeDefaultParams },
  403. colorTheme: { name: type.defaultColorTheme.name, params: PD.getDefaultValues(colorParams) },
  404. sizeTheme: { name: type.defaultSizeTheme.name, params: PD.getDefaultValues(sizeParams) }
  405. })
  406. }
  407. 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> {
  408. const type = ctx.representation.volume.registry.get(name);
  409. const colorType = ctx.representation.volume.themes.colorThemeRegistry.get(colorName || type.defaultColorTheme.name);
  410. const sizeType = ctx.representation.volume.themes.sizeThemeRegistry.get(sizeName || type.defaultSizeTheme.name);
  411. return ({
  412. type: { name, params: volumeParams ? { ...type.defaultValues, ...volumeParams } : type.defaultValues },
  413. colorTheme: { name: type.defaultColorTheme.name, params: colorParams ? { ...colorType.defaultValues, ...colorParams } : colorType.defaultValues },
  414. sizeTheme: { name: type.defaultSizeTheme.name, params: sizeParams ? { ...sizeType.defaultValues, ...sizeParams } : sizeType.defaultValues }
  415. })
  416. }
  417. export function getDescription(props: any) {
  418. return props.isoValue && VolumeIsoValue.toString(props.isoValue)
  419. }
  420. }
  421. type VolumeRepresentation3D = typeof VolumeRepresentation3D
  422. const VolumeRepresentation3D = PluginStateTransform.BuiltIn({
  423. name: 'volume-representation-3d',
  424. display: '3D Representation',
  425. from: SO.Volume.Data,
  426. to: SO.Volume.Representation3D,
  427. params: (a, ctx: PluginContext) => {
  428. const { registry, themes: themeCtx } = ctx.representation.volume
  429. const type = registry.get(registry.default.name);
  430. if (!a) {
  431. return {
  432. type: PD.Mapped<any>(
  433. registry.default.name,
  434. registry.types,
  435. name => PD.Group<any>(registry.get(name).getParams(themeCtx, VolumeData.One ))),
  436. colorTheme: PD.Mapped<any>(
  437. type.defaultColorTheme.name,
  438. themeCtx.colorThemeRegistry.types,
  439. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
  440. ),
  441. sizeTheme: PD.Mapped<any>(
  442. type.defaultSizeTheme.name,
  443. themeCtx.sizeThemeRegistry.types,
  444. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams({ volume: VolumeData.One }))
  445. )
  446. }
  447. }
  448. const dataCtx = { volume: a.data }
  449. return ({
  450. type: PD.Mapped<any>(
  451. registry.default.name,
  452. registry.types,
  453. name => PD.Group<any>(registry.get(name).getParams(themeCtx, a.data))),
  454. colorTheme: PD.Mapped<any>(
  455. type.defaultColorTheme.name,
  456. themeCtx.colorThemeRegistry.getApplicableTypes(dataCtx),
  457. name => PD.Group<any>(themeCtx.colorThemeRegistry.get(name).getParams(dataCtx))
  458. ),
  459. sizeTheme: PD.Mapped<any>(
  460. type.defaultSizeTheme.name,
  461. themeCtx.sizeThemeRegistry.types,
  462. name => PD.Group<any>(themeCtx.sizeThemeRegistry.get(name).getParams(dataCtx))
  463. )
  464. })
  465. }
  466. })({
  467. canAutoUpdate({ oldParams, newParams }) {
  468. // TODO: allow for small molecules
  469. return oldParams.type.name === newParams.type.name;
  470. },
  471. apply({ a, params }, plugin: PluginContext) {
  472. return Task.create('Volume Representation', async ctx => {
  473. const propertyCtx = { runtime: ctx, fetch: plugin.fetch }
  474. const provider = plugin.representation.volume.registry.get(params.type.name)
  475. if (provider.ensureCustomProperties) await provider.ensureCustomProperties.attach(propertyCtx, a.data)
  476. const props = params.type.params || {}
  477. const repr = provider.factory({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.volume.themes }, provider.getParams)
  478. repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, params))
  479. // TODO set initial state, repr.setState({})
  480. await repr.createOrUpdate(props, a.data).runInContext(ctx);
  481. return new SO.Volume.Representation3D({ repr, source: a }, { label: provider.label, description: VolumeRepresentation3DHelpers.getDescription(props) });
  482. });
  483. },
  484. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  485. return Task.create('Volume Representation', async ctx => {
  486. if (newParams.type.name !== oldParams.type.name) {
  487. const oldProvider = plugin.representation.volume.registry.get(oldParams.type.name);
  488. if (oldProvider.ensureCustomProperties) {
  489. const propertyCtx = { runtime: ctx, fetch: plugin.fetch }
  490. oldProvider.ensureCustomProperties.detach(propertyCtx, a.data)
  491. }
  492. return StateTransformer.UpdateResult.Recreate;
  493. }
  494. const props = { ...b.data.repr.props, ...newParams.type.params }
  495. b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: a.data }, newParams))
  496. await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
  497. b.description = VolumeRepresentation3DHelpers.getDescription(props)
  498. return StateTransformer.UpdateResult.Updated;
  499. });
  500. }
  501. });
  502. //
  503. export { ShapeRepresentation3D }
  504. type ShapeRepresentation3D = typeof ShapeRepresentation3D
  505. const ShapeRepresentation3D = PluginStateTransform.BuiltIn({
  506. name: 'shape-representation-3d',
  507. display: '3D Representation',
  508. from: SO.Shape.Provider,
  509. to: SO.Shape.Representation3D,
  510. params: (a, ctx: PluginContext) => {
  511. return a ? a.data.params : BaseGeometry.Params
  512. }
  513. })({
  514. canAutoUpdate() {
  515. return true;
  516. },
  517. apply({ a, params }, plugin: PluginContext) {
  518. return Task.create('Shape Representation', async ctx => {
  519. const props = { ...PD.getDefaultValues(a.data.params), params }
  520. const repr = ShapeRepresentation(a.data.getShape, a.data.geometryUtils)
  521. // TODO set initial state, repr.setState({})
  522. await repr.createOrUpdate(props, a.data.data).runInContext(ctx);
  523. return new SO.Shape.Representation3D({ repr, source: a }, { label: a.data.label });
  524. });
  525. },
  526. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  527. return Task.create('Shape Representation', async ctx => {
  528. const props = { ...b.data.repr.props, ...newParams }
  529. await b.data.repr.createOrUpdate(props, a.data.data).runInContext(ctx);
  530. return StateTransformer.UpdateResult.Updated;
  531. });
  532. }
  533. });
  534. export { ModelUnitcell3D }
  535. type ModelUnitcell3D = typeof ModelUnitcell3D
  536. const ModelUnitcell3D = PluginStateTransform.BuiltIn({
  537. name: 'model-unitcell-3d',
  538. display: 'Model Unitcell',
  539. from: SO.Molecule.Model,
  540. to: SO.Shape.Representation3D,
  541. params: {
  542. ...UnitcellParams,
  543. }
  544. })({
  545. isApplicable: a => !!ModelSymmetry.Provider.get(a.data),
  546. canAutoUpdate({ oldParams, newParams }) {
  547. return true;
  548. },
  549. apply({ a, params }, plugin: PluginContext) {
  550. return Task.create('Model Unitcell', async ctx => {
  551. const symmetry = ModelSymmetry.Provider.get(a.data)
  552. if (!symmetry) return StateObject.Null
  553. const data = getUnitcellData(a.data, symmetry)
  554. const repr = UnitcellRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => UnitcellParams)
  555. await repr.createOrUpdate(params, data).runInContext(ctx);
  556. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Unitcell`, description: symmetry.spacegroup.name });
  557. });
  558. },
  559. update({ a, b, newParams }) {
  560. return Task.create('Model Unitcell', async ctx => {
  561. const symmetry = ModelSymmetry.Provider.get(a.data)
  562. if (!symmetry) return StateTransformer.UpdateResult.Null
  563. const props = { ...b.data.repr.props, ...newParams }
  564. const data = getUnitcellData(a.data, symmetry)
  565. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  566. b.data.source = a
  567. return StateTransformer.UpdateResult.Updated;
  568. });
  569. }
  570. });
  571. export { StructureSelectionsDistance3D }
  572. type StructureSelectionsDistance3D = typeof StructureSelectionsDistance3D
  573. const StructureSelectionsDistance3D = PluginStateTransform.BuiltIn({
  574. name: 'structure-selections-distance-3d',
  575. display: '3D Distance',
  576. from: SO.Molecule.Structure.Selections,
  577. to: SO.Shape.Representation3D,
  578. params: {
  579. ...DistanceParams,
  580. }
  581. })({
  582. canAutoUpdate({ oldParams, newParams }) {
  583. return true;
  584. },
  585. apply({ a, params }, plugin: PluginContext) {
  586. return Task.create('Structure Distance', async ctx => {
  587. const data = getDistanceDataFromStructureSelections(a.data)
  588. const repr = DistanceRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DistanceParams)
  589. await repr.createOrUpdate(params, data).runInContext(ctx);
  590. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Distance` });
  591. });
  592. },
  593. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  594. return Task.create('Structure Distance', async ctx => {
  595. const props = { ...b.data.repr.props, ...newParams }
  596. const data = getDistanceDataFromStructureSelections(a.data)
  597. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  598. b.data.source = a
  599. return StateTransformer.UpdateResult.Updated;
  600. });
  601. },
  602. });
  603. export { StructureSelectionsAngle3D }
  604. type StructureSelectionsAngle3D = typeof StructureSelectionsAngle3D
  605. const StructureSelectionsAngle3D = PluginStateTransform.BuiltIn({
  606. name: 'structure-selections-angle-3d',
  607. display: '3D Angle',
  608. from: SO.Molecule.Structure.Selections,
  609. to: SO.Shape.Representation3D,
  610. params: {
  611. ...AngleParams,
  612. }
  613. })({
  614. canAutoUpdate({ oldParams, newParams }) {
  615. return true;
  616. },
  617. apply({ a, params }, plugin: PluginContext) {
  618. return Task.create('Structure Angle', async ctx => {
  619. const data = getAngleDataFromStructureSelections(a.data)
  620. const repr = AngleRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => AngleParams)
  621. await repr.createOrUpdate(params, data).runInContext(ctx);
  622. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Angle` });
  623. });
  624. },
  625. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  626. return Task.create('Structure Angle', async ctx => {
  627. const props = { ...b.data.repr.props, ...newParams }
  628. const data = getAngleDataFromStructureSelections(a.data)
  629. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  630. b.data.source = a
  631. return StateTransformer.UpdateResult.Updated;
  632. });
  633. },
  634. });
  635. export { StructureSelectionsDihedral3D }
  636. type StructureSelectionsDihedral3D = typeof StructureSelectionsDihedral3D
  637. const StructureSelectionsDihedral3D = PluginStateTransform.BuiltIn({
  638. name: 'structure-selections-dihedral-3d',
  639. display: '3D Dihedral',
  640. from: SO.Molecule.Structure.Selections,
  641. to: SO.Shape.Representation3D,
  642. params: {
  643. ...DihedralParams,
  644. }
  645. })({
  646. canAutoUpdate({ oldParams, newParams }) {
  647. return true;
  648. },
  649. apply({ a, params }, plugin: PluginContext) {
  650. return Task.create('Structure Dihedral', async ctx => {
  651. const data = getDihedralDataFromStructureSelections(a.data)
  652. const repr = DihedralRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => DihedralParams)
  653. await repr.createOrUpdate(params, data).runInContext(ctx);
  654. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Dihedral` });
  655. });
  656. },
  657. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  658. return Task.create('Structure Dihedral', async ctx => {
  659. const props = { ...b.data.repr.props, ...newParams }
  660. const data = getDihedralDataFromStructureSelections(a.data)
  661. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  662. b.data.source = a
  663. return StateTransformer.UpdateResult.Updated;
  664. });
  665. },
  666. });
  667. export { StructureSelectionsLabel3D }
  668. type StructureSelectionsLabel3D = typeof StructureSelectionsLabel3D
  669. const StructureSelectionsLabel3D = PluginStateTransform.BuiltIn({
  670. name: 'structure-selections-label-3d',
  671. display: '3D Label',
  672. from: SO.Molecule.Structure.Selections,
  673. to: SO.Shape.Representation3D,
  674. params: {
  675. ...LabelParams,
  676. }
  677. })({
  678. canAutoUpdate({ oldParams, newParams }) {
  679. return true;
  680. },
  681. apply({ a, params }, plugin: PluginContext) {
  682. return Task.create('Structure Label', async ctx => {
  683. const data = getLabelDataFromStructureSelections(a.data)
  684. const repr = LabelRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => LabelParams)
  685. await repr.createOrUpdate(params, data).runInContext(ctx);
  686. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Label` });
  687. });
  688. },
  689. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  690. return Task.create('Structure Label', async ctx => {
  691. const props = { ...b.data.repr.props, ...newParams }
  692. const data = getLabelDataFromStructureSelections(a.data)
  693. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  694. b.data.source = a
  695. return StateTransformer.UpdateResult.Updated;
  696. });
  697. },
  698. });
  699. export { StructureSelectionsOrientation3D }
  700. type StructureSelectionsOrientation3D = typeof StructureSelectionsOrientation3D
  701. const StructureSelectionsOrientation3D = PluginStateTransform.BuiltIn({
  702. name: 'structure-selections-orientation-3d',
  703. display: '3D Orientation',
  704. from: SO.Molecule.Structure.Selections,
  705. to: SO.Shape.Representation3D,
  706. params: {
  707. ...OrientationParams,
  708. }
  709. })({
  710. canAutoUpdate({ oldParams, newParams }) {
  711. return true;
  712. },
  713. apply({ a, params }, plugin: PluginContext) {
  714. return Task.create('Structure Orientation', async ctx => {
  715. const data = getOrientationDataFromStructureSelections(a.data)
  716. const repr = OrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => OrientationParams)
  717. await repr.createOrUpdate(params, data).runInContext(ctx);
  718. return new SO.Shape.Representation3D({ repr, source: a }, { label: `Orientation` });
  719. });
  720. },
  721. update({ a, b, oldParams, newParams }, plugin: PluginContext) {
  722. return Task.create('Structure Orientation', async ctx => {
  723. const props = { ...b.data.repr.props, ...newParams }
  724. const data = getOrientationDataFromStructureSelections(a.data)
  725. await b.data.repr.createOrUpdate(props, data).runInContext(ctx);
  726. b.data.source = a
  727. return StateTransformer.UpdateResult.Updated;
  728. });
  729. },
  730. });