representation.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Michal Malý <michal.maly@ibt.cas.cz>
  5. * @author Jiří Černý <jiri.cerny@ibt.cas.cz>
  6. */
  7. import { ConfalPyramidsProvider } from './property';
  8. import { ConfalPyramidsIterator } from './util';
  9. import { ConfalPyramidsTypes as CPT } from './types';
  10. import { Dnatco } from '../property';
  11. import { Interval } from '../../../mol-data/int';
  12. import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
  13. import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
  14. import { PickingId } from '../../../mol-geo/geometry/picking';
  15. import { PrimitiveBuilder } from '../../../mol-geo/primitive/primitive';
  16. import { LocationIterator } from '../../../mol-geo/util/location-iterator';
  17. import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
  18. import { EmptyLoci, Loci } from '../../../mol-model/loci';
  19. import { Structure, Unit } from '../../../mol-model/structure';
  20. import { CustomProperty } from '../../../mol-model-props/common/custom-property';
  21. import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../../mol-repr/representation';
  22. import { StructureRepresentation, StructureRepresentationProvider, StructureRepresentationStateBuilder, UnitsRepresentation } from '../../../mol-repr/structure/representation';
  23. import { UnitsMeshParams, UnitsMeshVisual, UnitsVisual } from '../../../mol-repr/structure/units-visual';
  24. import { VisualUpdateState } from '../../../mol-repr/util';
  25. import { VisualContext } from '../../../mol-repr/visual';
  26. import { StructureGroup } from '../../../mol-repr/structure/visual/util/common';
  27. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  28. import { Theme, ThemeRegistryContext } from '../../../mol-theme/theme';
  29. import { NullLocation } from '../../../mol-model/location';
  30. const t = Mat4.identity();
  31. const w = Vec3.zero();
  32. const mp = Vec3.zero();
  33. const posO3 = Vec3();
  34. const posP = Vec3();
  35. const posOP1 = Vec3();
  36. const posOP2 = Vec3();
  37. const posO5 = Vec3();
  38. function calcMidpoint(mp: Vec3, v: Vec3, w: Vec3) {
  39. Vec3.sub(mp, v, w);
  40. Vec3.scale(mp, mp, 0.5);
  41. Vec3.add(mp, mp, w);
  42. }
  43. function shiftVertex(vec: Vec3, ref: Vec3, scale: number) {
  44. Vec3.sub(w, vec, ref);
  45. Vec3.scale(w, w, scale);
  46. Vec3.add(vec, vec, w);
  47. }
  48. const ConfalPyramidsMeshParams = {
  49. ...UnitsMeshParams
  50. };
  51. type ConfalPyramidsMeshParams = typeof ConfalPyramidsMeshParams;
  52. function createConfalPyramidsIterator(structureGroup: StructureGroup): LocationIterator {
  53. const { structure, group } = structureGroup;
  54. const instanceCount = group.units.length;
  55. const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
  56. if (!data) return LocationIterator(0, 1, 1, () => NullLocation);
  57. const halfPyramidsCount = data.steps.length * 2;
  58. const getLocation = (groupIndex: number, instanceIndex: number) => {
  59. if (halfPyramidsCount <= groupIndex) return NullLocation;
  60. const idx = Math.floor(groupIndex / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
  61. return CPT.Location(data.steps[idx], groupIndex % 2 === 1);
  62. };
  63. return LocationIterator(halfPyramidsCount, instanceCount, 1, getLocation);
  64. }
  65. function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ConfalPyramidsMeshParams>, mesh?: Mesh) {
  66. if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh);
  67. const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
  68. if (!data) return Mesh.createEmpty(mesh);
  69. const { steps, mapping } = data;
  70. if (steps.length === 0) return Mesh.createEmpty(mesh);
  71. const vertexCount = (6 * steps.length) / mapping.length;
  72. const mb = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh);
  73. const it = new ConfalPyramidsIterator(structure, unit);
  74. while (it.hasNext) {
  75. const allPoints = it.move();
  76. if (!allPoints)
  77. continue;
  78. for (const points of allPoints) {
  79. const { O3, P, OP1, OP2, O5, confalScore } = points;
  80. const scale = (confalScore - 20.0) / 100.0;
  81. // Steps can be drawn in a different order than they are stored.
  82. // To make sure that we can get from the drawn pyramid back to the step in represents,
  83. // we need to use an appropriate groupId. The stepIdx passed from the iterator
  84. // is an index into the array of all steps in the structure.
  85. // Since a step is drawn as two "half-pyramids" we need two ids to map to a single step.
  86. // To do that, we just multiply the index by 2. idx*2 marks the "upper" half-pyramid,
  87. // (idx*2)+1 the "lower" half-pyramid.
  88. const groupIdx = points.stepIdx * 2;
  89. unit.conformation.invariantPosition(O3, posO3);
  90. unit.conformation.invariantPosition(P, posP);
  91. unit.conformation.invariantPosition(OP1, posOP1);
  92. unit.conformation.invariantPosition(OP2, posOP2);
  93. unit.conformation.invariantPosition(O5, posO5);
  94. shiftVertex(posO3, posP, scale);
  95. shiftVertex(posOP1, posP, scale);
  96. shiftVertex(posOP2, posP, scale);
  97. shiftVertex(posO5, posP, scale);
  98. calcMidpoint(mp, posO3, posO5);
  99. mb.currentGroup = groupIdx;
  100. let pb = PrimitiveBuilder(3);
  101. /* Upper part (for first residue in step) */
  102. pb.add(posO3, posOP1, posOP2);
  103. pb.add(posO3, mp, posOP1);
  104. pb.add(posO3, posOP2, mp);
  105. MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
  106. /* Lower part (for second residue in step) */
  107. mb.currentGroup = groupIdx + 1;
  108. pb = PrimitiveBuilder(3);
  109. pb.add(mp, posO5, posOP1);
  110. pb.add(mp, posOP2, posO5);
  111. pb.add(posO5, posOP2, posOP1);
  112. MeshBuilder.addPrimitive(mb, t, pb.getPrimitive());
  113. }
  114. }
  115. return MeshBuilder.getMesh(mb);
  116. }
  117. function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
  118. const { groupId, objectId, instanceId } = pickingId;
  119. if (objectId !== id) return EmptyLoci;
  120. const { structure } = structureGroup;
  121. const unit = structureGroup.group.units[instanceId];
  122. if (!Unit.isAtomic(unit)) return EmptyLoci;
  123. const data = ConfalPyramidsProvider.get(structure.model)?.value?.data;
  124. if (!data) return EmptyLoci;
  125. const halfPyramidsCount = data.steps.length * 2;
  126. if (halfPyramidsCount <= groupId) return EmptyLoci;
  127. const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation
  128. return CPT.Loci(data.steps, [idx]);
  129. }
  130. function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
  131. return false; // TODO: Implement me
  132. }
  133. function ConfalPyramidsVisual(materialId: number): UnitsVisual<ConfalPyramidsMeshParams> {
  134. return UnitsMeshVisual<ConfalPyramidsMeshParams>({
  135. defaultProps: PD.getDefaultValues(ConfalPyramidsMeshParams),
  136. createGeometry: createConfalPyramidsMesh,
  137. createLocationIterator: createConfalPyramidsIterator,
  138. getLoci: getConfalPyramidLoci,
  139. eachLocation: eachConfalPyramid,
  140. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<ConfalPyramidsMeshParams>, currentProps: PD.Values<ConfalPyramidsMeshParams>) => {
  141. }
  142. }, materialId);
  143. }
  144. const ConfalPyramidsVisuals = {
  145. 'confal-pyramids-symbol': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, UnitsMeshParams>) => UnitsRepresentation('Confal Pyramids Symbol Mesh', ctx, getParams, ConfalPyramidsVisual),
  146. };
  147. export const ConfalPyramidsParams = {
  148. ...UnitsMeshParams
  149. };
  150. export type ConfalPyramidsParams = typeof ConfalPyramidsParams;
  151. export function getConfalPyramidsParams(ctx: ThemeRegistryContext, structure: Structure) {
  152. return PD.clone(ConfalPyramidsParams);
  153. }
  154. export type ConfalPyramidsRepresentation = StructureRepresentation<ConfalPyramidsParams>;
  155. export function ConfalPyramidsRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, ConfalPyramidsParams>): ConfalPyramidsRepresentation {
  156. const repr = Representation.createMulti('Confal Pyramids', ctx, getParams, StructureRepresentationStateBuilder, ConfalPyramidsVisuals as unknown as Representation.Def<Structure, ConfalPyramidsParams>);
  157. return repr;
  158. }
  159. export const ConfalPyramidsRepresentationProvider = StructureRepresentationProvider({
  160. name: 'confal-pyramids',
  161. label: 'Confal Pyramids',
  162. description: 'Displays schematic depiction of conformer classes and confal values',
  163. factory: ConfalPyramidsRepresentation,
  164. getParams: getConfalPyramidsParams,
  165. defaultValues: PD.getDefaultValues(ConfalPyramidsParams),
  166. defaultColorTheme: { name: 'confal-pyramids' },
  167. defaultSizeTheme: { name: 'uniform' },
  168. isApplicable: (structure: Structure) => structure.models.some(m => Dnatco.isApplicable(m)),
  169. ensureCustomProperties: {
  170. attach: (ctx: CustomProperty.Context, structure: Structure) => ConfalPyramidsProvider.attach(ctx, structure.model, void 0, true),
  171. detach: (data) => ConfalPyramidsProvider.ref(data.model, false),
  172. }
  173. });