state.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /**
  2. * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author Ludovic Autin <ludovic.autin@gmail.com>
  6. */
  7. import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
  8. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  9. import { Task } from '../../mol-task';
  10. import { CellPack as _CellPack, Cell, CellPacking } from './data';
  11. import { createStructureFromCellPack } from './model';
  12. import { IngredientFiles } from './util';
  13. import { Asset } from '../../mol-util/assets';
  14. import { PluginContext } from '../../mol-plugin/context';
  15. import { CellPackInfoProvider } from './property';
  16. import { Structure, StructureSymmetry, Unit, Model } from '../../mol-model/structure';
  17. import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
  18. import { Vec3, Quat } from '../../mol-math/linear-algebra';
  19. import { StateTransformer } from '../../mol-state';
  20. import { MBRepresentation, MBParams } from './representation';
  21. import { IsNativeEndianLittle, flipByteOrder } from '../../mol-io/common/binary';
  22. import { getFloatValue } from './util';
  23. export const DefaultCellPackBaseUrl = 'https://raw.githubusercontent.com/mesoscope/cellPACK_data/master/cellPACK_database_1.1.0';
  24. export class CellPack extends PSO.Create<_CellPack>({ name: 'CellPack', typeClass: 'Object' }) { }
  25. export { ParseCellPack };
  26. type ParseCellPack = typeof ParseCellPack
  27. const ParseCellPack = PluginStateTransform.BuiltIn({
  28. name: 'parse-cellpack',
  29. display: { name: 'Parse CellPack', description: 'Parse CellPack from JSON data' },
  30. from: PSO.Format.Json,
  31. to: CellPack,
  32. params: a => {
  33. return {
  34. resultsFile: PD.File({ accept: '.bin' }),
  35. baseUrl: PD.Text(DefaultCellPackBaseUrl)
  36. };
  37. }
  38. })({
  39. apply({ a, params, cache }, plugin: PluginContext) {
  40. return Task.create('Parse CellPack', async ctx => {
  41. const cell = a.data as Cell;
  42. let counter_id = 0;
  43. let fiber_counter_id = 0;
  44. let comp_counter = 0;
  45. const packings: CellPacking[] = [];
  46. const { compartments, cytoplasme } = cell;
  47. if (!cell.mapping_ids) cell.mapping_ids = {};
  48. if (cytoplasme) {
  49. packings.push({ name: 'Cytoplasme', location: 'cytoplasme', ingredients: cytoplasme.ingredients });
  50. for (const iName in cytoplasme.ingredients) {
  51. if (cytoplasme.ingredients[iName].ingtype === 'fiber') {
  52. cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
  53. if (!cytoplasme.ingredients[iName].nbCurve) cytoplasme.ingredients[iName].nbCurve = 0;
  54. fiber_counter_id++;
  55. } else {
  56. cell.mapping_ids[counter_id] = [comp_counter, iName];
  57. if (!cytoplasme.ingredients[iName].results) { cytoplasme.ingredients[iName].results = []; }
  58. counter_id++;
  59. }
  60. }
  61. comp_counter++;
  62. }
  63. if (compartments) {
  64. for (const name in compartments) {
  65. const { surface, interior } = compartments[name];
  66. let filename = '';
  67. if (compartments[name].geom_type === 'file') {
  68. filename = (compartments[name].geom) ? compartments[name].geom as string : '';
  69. }
  70. const compartment = { filename: filename, geom_type: compartments[name].geom_type, compartment_primitives: compartments[name].mb };
  71. if (surface) {
  72. packings.push({ name, location: 'surface', ingredients: surface.ingredients, compartment: compartment });
  73. for (const iName in surface.ingredients) {
  74. if (surface.ingredients[iName].ingtype === 'fiber') {
  75. cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
  76. if (!surface.ingredients[iName].nbCurve) surface.ingredients[iName].nbCurve = 0;
  77. fiber_counter_id++;
  78. } else {
  79. cell.mapping_ids[counter_id] = [comp_counter, iName];
  80. if (!surface.ingredients[iName].results) { surface.ingredients[iName].results = []; }
  81. counter_id++;
  82. }
  83. }
  84. comp_counter++;
  85. }
  86. if (interior) {
  87. if (!surface) packings.push({ name, location: 'interior', ingredients: interior.ingredients, compartment: compartment });
  88. else packings.push({ name, location: 'interior', ingredients: interior.ingredients });
  89. for (const iName in interior.ingredients) {
  90. if (interior.ingredients[iName].ingtype === 'fiber') {
  91. cell.mapping_ids[-(fiber_counter_id + 1)] = [comp_counter, iName];
  92. if (!interior.ingredients[iName].nbCurve) interior.ingredients[iName].nbCurve = 0;
  93. fiber_counter_id++;
  94. } else {
  95. cell.mapping_ids[counter_id] = [comp_counter, iName];
  96. if (!interior.ingredients[iName].results) { interior.ingredients[iName].results = []; }
  97. counter_id++;
  98. }
  99. }
  100. comp_counter++;
  101. }
  102. }
  103. }
  104. const { options } = cell;
  105. let resultsAsset: Asset.Wrapper<'binary'> | undefined;
  106. if (params.resultsFile) {
  107. resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(params.resultsFile, 'binary', true));
  108. } else if (options?.resultfile) {
  109. const url = `${params.baseUrl}/results/${options.resultfile}`;
  110. resultsAsset = await plugin.runTask(plugin.managers.asset.resolve(Asset.getUrlAsset(plugin.managers.asset, url), 'binary', true));
  111. }
  112. if (resultsAsset) {
  113. (cache as any).asset = resultsAsset;
  114. const results = resultsAsset.data;
  115. // flip the byte order if needed
  116. const buffer = IsNativeEndianLittle ? results.buffer : flipByteOrder(results, 4);
  117. const numbers = new DataView(buffer);
  118. const ninst = getFloatValue(numbers, 0);
  119. const npoints = getFloatValue(numbers, 4);
  120. const ncurve = getFloatValue(numbers, 8);
  121. let offset = 12;
  122. if (ninst !== 0) {
  123. const pos = new Float32Array(buffer, offset, ninst * 4);
  124. offset += ninst * 4 * 4;
  125. const quat = new Float32Array(buffer, offset, ninst * 4);
  126. offset += ninst * 4 * 4;
  127. for (let i = 0; i < ninst; i++) {
  128. const x: number = pos[i * 4 + 0];
  129. const y: number = pos[i * 4 + 1];
  130. const z: number = pos[i * 4 + 2];
  131. const ingr_id = pos[i * 4 + 3] as number;
  132. const pid = cell.mapping_ids![ingr_id];
  133. if (!packings[pid[0]].ingredients[pid[1]].results) {
  134. packings[pid[0]].ingredients[pid[1]].results = [];
  135. }
  136. packings[pid[0]].ingredients[pid[1]].results.push([Vec3.create(x, y, z),
  137. Quat.create(quat[i * 4 + 0], quat[i * 4 + 1], quat[i * 4 + 2], quat[i * 4 + 3])]);
  138. }
  139. }
  140. if (npoints !== 0) {
  141. const ctr_pos = new Float32Array(buffer, offset, npoints * 4);
  142. offset += npoints * 4 * 4;
  143. offset += npoints * 4 * 4;
  144. const ctr_info = new Float32Array(buffer, offset, npoints * 4);
  145. offset += npoints * 4 * 4;
  146. const curve_ids = new Float32Array(buffer, offset, ncurve * 4);
  147. offset += ncurve * 4 * 4;
  148. let counter = 0;
  149. let ctr_points: Vec3[] = [];
  150. let prev_ctype = 0;
  151. let prev_cid = 0;
  152. for (let i = 0; i < npoints; i++) {
  153. const x: number = -ctr_pos[i * 4 + 0];
  154. const y: number = ctr_pos[i * 4 + 1];
  155. const z: number = ctr_pos[i * 4 + 2];
  156. const cid: number = ctr_info[i * 4 + 0]; // curve id
  157. const ctype: number = curve_ids[cid * 4 + 0]; // curve type
  158. // cid 148 165 -1 0
  159. // console.log("cid ",cid,ctype,prev_cid,prev_ctype);//165,148
  160. if (prev_ctype !== ctype) {
  161. const pid = cell.mapping_ids![-prev_ctype - 1];
  162. const cname = `curve${counter}`;
  163. packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
  164. packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
  165. ctr_points = [];
  166. counter = 0;
  167. } else if (prev_cid !== cid) {
  168. ctr_points = [];
  169. const pid = cell.mapping_ids![-prev_ctype - 1];
  170. const cname = `curve${counter}`;
  171. packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
  172. counter += 1;
  173. }
  174. ctr_points.push(Vec3.create(x, y, z));
  175. prev_ctype = ctype;
  176. prev_cid = cid;
  177. }
  178. // do the last one
  179. const pid = cell.mapping_ids![-prev_ctype - 1];
  180. const cname = `curve${counter}`;
  181. packings[pid[0]].ingredients[pid[1]].nbCurve = counter + 1;
  182. packings[pid[0]].ingredients[pid[1]][cname] = ctr_points;
  183. }
  184. }
  185. return new CellPack({ cell, packings });
  186. });
  187. },
  188. dispose({ cache }) {
  189. ((cache as any)?.asset as Asset.Wrapper | undefined)?.dispose();
  190. },
  191. });
  192. export { StructureFromCellpack };
  193. type StructureFromCellpack = typeof ParseCellPack
  194. const StructureFromCellpack = PluginStateTransform.BuiltIn({
  195. name: 'structure-from-cellpack',
  196. display: { name: 'Structure from CellPack', description: 'Create Structure from CellPack Packing' },
  197. from: CellPack,
  198. to: PSO.Molecule.Structure,
  199. params: a => {
  200. const options = a ? a.data.packings.map((d, i) => [i, d.name] as const) : [];
  201. return {
  202. packing: PD.Select(0, options),
  203. baseUrl: PD.Text(DefaultCellPackBaseUrl),
  204. ingredientFiles: PD.FileList({ accept: '.cif,.bcif,.pdb' })
  205. };
  206. }
  207. })({
  208. apply({ a, params, cache }, plugin: PluginContext) {
  209. return Task.create('Structure from CellPack', async ctx => {
  210. const packing = a.data.packings[params.packing];
  211. const ingredientFiles: IngredientFiles = {};
  212. if (params.ingredientFiles !== null) {
  213. for (const file of params.ingredientFiles) {
  214. ingredientFiles[file.name] = file;
  215. }
  216. }
  217. const { structure, assets, colors } = await createStructureFromCellPack(plugin, packing, params.baseUrl, ingredientFiles).runInContext(ctx);
  218. await CellPackInfoProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, structure, {
  219. info: { packingsCount: a.data.packings.length, packingIndex: params.packing, colors }
  220. });
  221. (cache as any).assets = assets;
  222. return new PSO.Molecule.Structure(structure, { label: packing.name + '.' + packing.location });
  223. });
  224. },
  225. dispose({ b, cache }) {
  226. const assets = (cache as any).assets as Asset.Wrapper[];
  227. if (assets) {
  228. for (const a of assets) a.dispose();
  229. }
  230. if (b) {
  231. b.data.customPropertyDescriptors.dispose();
  232. for (const m of b.data.models) {
  233. m.customProperties.dispose();
  234. }
  235. }
  236. }
  237. });
  238. export { StructureFromAssemblies };
  239. type StructureFromAssemblies = typeof StructureFromAssemblies
  240. const StructureFromAssemblies = PluginStateTransform.BuiltIn({
  241. name: 'Structure from all assemblies',
  242. display: { name: 'Structure from all assemblies' },
  243. from: PSO.Molecule.Model,
  244. to: PSO.Molecule.Structure,
  245. params: {
  246. }
  247. })({
  248. canAutoUpdate({ newParams }) {
  249. return true;
  250. },
  251. apply({ a, params }) {
  252. return Task.create('Build Structure', async ctx => {
  253. // TODO: optimze
  254. // TODO: think of ways how to fast-track changes to this for animations
  255. const model = a.data;
  256. const initial_structure = Structure.ofModel(model);
  257. const structures: Structure[] = [];
  258. let structure: Structure = initial_structure;
  259. // the list of asambly *?
  260. const symmetry = ModelSymmetry.Provider.get(model);
  261. if (symmetry && symmetry.assemblies.length !== 0) {
  262. for (const a of symmetry.assemblies) {
  263. const s = await StructureSymmetry.buildAssembly(initial_structure, a.id).runInContext(ctx);
  264. structures.push(s);
  265. }
  266. const builder = Structure.Builder({ label: 'Membrane' });
  267. let offsetInvariantId = 0;
  268. for (const s of structures) {
  269. let maxInvariantId = 0;
  270. for (const u of s.units) {
  271. const invariantId = u.invariantId + offsetInvariantId;
  272. if (u.invariantId > maxInvariantId) maxInvariantId = u.invariantId;
  273. builder.addUnit(u.kind, u.model, u.conformation.operator, u.elements, Unit.Trait.None, invariantId);
  274. }
  275. offsetInvariantId += maxInvariantId + 1;
  276. }
  277. structure = builder.getStructure();
  278. for (let i = 0, il = structure.models.length; i < il; ++i) {
  279. Model.TrajectoryInfo.set(structure.models[i], { size: il, index: i });
  280. }
  281. }
  282. return new PSO.Molecule.Structure(structure, { label: a.label, description: `${a.description}` });
  283. });
  284. },
  285. dispose({ b }) {
  286. b?.data.customPropertyDescriptors.dispose();
  287. }
  288. });
  289. const CreateTransformer = StateTransformer.builderFactory('cellPACK');
  290. export const CreateCompartmentSphere = CreateTransformer({
  291. name: 'create-compartment-sphere',
  292. display: 'CompartmentSphere',
  293. from: PSO.Root, // or whatever data source
  294. to: PSO.Shape.Representation3D,
  295. params: {
  296. center: PD.Vec3(Vec3()),
  297. radius: PD.Numeric(1),
  298. label: PD.Text(`Compartment Sphere`)
  299. }
  300. })({
  301. canAutoUpdate({ oldParams, newParams }) {
  302. return true;
  303. },
  304. apply({ a, params }, plugin: PluginContext) {
  305. return Task.create('Compartment Sphere', async ctx => {
  306. const data = params;
  307. const repr = MBRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => (MBParams));
  308. await repr.createOrUpdate({ ...params, quality: 'custom', xrayShaded: true, doubleSided: true }, data).runInContext(ctx);
  309. return new PSO.Shape.Representation3D({ repr, sourceData: a }, { label: data.label });
  310. });
  311. }
  312. });