representation.ts 42 KB

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