representation.ts 42 KB

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