representation.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
  6. */
  7. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  8. import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
  9. import { Representation, RepresentationContext, RepresentationParamsGetter } from '../../mol-repr/representation';
  10. import { Structure } from '../../mol-model/structure';
  11. import { Spheres } from '../../mol-geo/geometry/spheres/spheres';
  12. import { SpheresBuilder } from '../../mol-geo/geometry/spheres/spheres-builder';
  13. import { MembraneOrientationProvider } from '../../mol-model-props/computed/membrane-orientation';
  14. import { StructureRepresentationProvider, StructureRepresentation, StructureRepresentationStateBuilder } from '../../mol-repr/structure/representation';
  15. import { MembraneOrientation } from '../../mol-model/structure/model/properties/membrane-orientation';
  16. import { ThemeRegistryContext } from '../../mol-theme/theme';
  17. import { ShapeRepresentation } from '../../mol-repr/shape/representation';
  18. import { Shape } from '../../mol-model/shape';
  19. import { ColorNames } from '../../mol-util/color/names';
  20. import { RuntimeContext } from '../../mol-task';
  21. import { Lines } from '../../mol-geo/geometry/lines/lines';
  22. import { Mesh } from '../../mol-geo/geometry/mesh/mesh';
  23. import { LinesBuilder } from '../../mol-geo/geometry/lines/lines-builder';
  24. import { Circle } from '../../mol-geo/primitive/circle';
  25. import { transformPrimitive } from '../../mol-geo/primitive/primitive';
  26. import { MeshBuilder } from '../../mol-geo/geometry/mesh/mesh-builder';
  27. const SharedParams = {
  28. color: PD.Color(ColorNames.lightgrey)
  29. };
  30. const BilayerSpheresParams = {
  31. ...SharedParams,
  32. ...Spheres.Params,
  33. sphereSize: PD.Numeric(1, { min: 0.1, max: 10, step: 0.1 }, { description: 'Size of spheres that represent membrane planes' }),
  34. density: PD.Numeric(1, { min: 0.25, max: 10, step: 0.25 }, { description: 'Distance between spheres'})
  35. };
  36. export type BilayerSpheresParams = typeof BilayerSpheresParams
  37. export type BilayerSpheresProps = PD.Values<BilayerSpheresParams>
  38. const BilayerPlanesParams = {
  39. ...SharedParams,
  40. ...Mesh.Params,
  41. sectorOpacity: PD.Numeric(0.5, { min: 0, max: 1, step: 0.01 })
  42. };
  43. export type BilayerPlanesParams = typeof BilayerPlanesParams
  44. export type BilayerPlanesProps = PD.Values<BilayerPlanesParams>
  45. const BilayerRimsParams = {
  46. ...SharedParams,
  47. ...Lines.Params,
  48. lineSizeAttenuation: PD.Boolean(true),
  49. linesSize: PD.Numeric(1, { min: 0.01, max: 50, step: 0.01 }),
  50. dashedLines: PD.Boolean(true)
  51. };
  52. export type BilayerRimsParams = typeof BilayerRimsParams
  53. export type BilayerRimsProps = PD.Values<BilayerRimsParams>
  54. const MembraneOrientationVisuals = {
  55. 'bilayer-spheres': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerSpheresParams>) => ShapeRepresentation(getBilayerSpheres, Spheres.Utils, { modifyState: s => ({ ...s, pickable: false }) }),
  56. 'bilayer-planes': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerPlanesParams>) => ShapeRepresentation(getBilayerPlanes, Mesh.Utils, { modifyProps: p => ({ ...p, alpha: p.sectorOpacity }), modifyState: s => ({ ...s, pickable: false }) }),
  57. 'bilayer-rims': (ctx: RepresentationContext, getParams: RepresentationParamsGetter<MembraneOrientation, BilayerRimsParams>) => ShapeRepresentation(getBilayerRims, Lines.Utils, { modifyState: s => ({ ...s, pickable: false }) })
  58. };
  59. export const MembraneOrientationParams = {
  60. ...BilayerSpheresParams,
  61. ...BilayerPlanesParams,
  62. ...BilayerRimsParams,
  63. visuals: PD.MultiSelect(['bilayer-planes', 'bilayer-rims'], PD.objectToOptions(MembraneOrientationVisuals)),
  64. };
  65. export type MembraneOrientationParams = typeof MembraneOrientationParams
  66. export type MembraneOrientationProps = PD.Values<MembraneOrientationParams>
  67. export function getMembraneOrientationParams(ctx: ThemeRegistryContext, structure: Structure) {
  68. return PD.clone(MembraneOrientationParams);
  69. }
  70. export type MembraneOrientationRepresentation = StructureRepresentation<MembraneOrientationParams>
  71. export function MembraneOrientationRepresentation(ctx: RepresentationContext, getParams: RepresentationParamsGetter<Structure, MembraneOrientationParams>): MembraneOrientationRepresentation {
  72. return Representation.createMulti('Membrane Orientation', ctx, getParams, StructureRepresentationStateBuilder, MembraneOrientationVisuals as unknown as Representation.Def<Structure, MembraneOrientationParams>);
  73. }
  74. export const MembraneOrientationRepresentationProvider = StructureRepresentationProvider({
  75. name: 'membrane-orientation',
  76. label: 'Membrane Orientation',
  77. description: 'Displays a grid of points representing membrane layers.',
  78. factory: MembraneOrientationRepresentation,
  79. getParams: getMembraneOrientationParams,
  80. defaultValues: PD.getDefaultValues(MembraneOrientationParams),
  81. defaultColorTheme: { name: 'uniform' },
  82. defaultSizeTheme: { name: 'uniform' },
  83. isApplicable: (structure: Structure) => structure.elementCount > 0
  84. });
  85. function getBilayerRims(ctx: RuntimeContext, data: Structure, props: BilayerRimsProps): Shape<Lines> {
  86. const { p1, p2, centroid, normal, radius } = MembraneOrientationProvider.get(data).value!;
  87. const builder = LinesBuilder.create(128, 64);
  88. getLayerCircle(builder, p1, centroid, normal, radius, props);
  89. getLayerCircle(builder, p2, centroid, normal, radius, props);
  90. return Shape.create(name, data, builder.getLines(), () => props.color, () => props.linesSize, () => '');
  91. }
  92. function getLayerCircle(builder: LinesBuilder, p: Vec3, centroid: Vec3, normal: Vec3, radius: number, props: BilayerRimsProps) {
  93. const circle = getCircle(p, centroid, normal, radius);
  94. const { indices, vertices } = circle;
  95. for (let j = 0, jl = indices.length; j < jl; j += 3) {
  96. if (props.dashedLines && j % 2 === 1) continue; // draw every other segment to get dashes
  97. const start = indices[j] * 3;
  98. const end = indices[j + 1] * 3;
  99. const startX = vertices[start];
  100. const startY = vertices[start + 1];
  101. const startZ = vertices[start + 2];
  102. const endX = vertices[end];
  103. const endY = vertices[end + 1];
  104. const endZ = vertices[end + 2];
  105. builder.add(startX, startY, startZ, endX, endY, endZ, 0);
  106. }
  107. }
  108. const tmpMat = Mat4();
  109. function getCircle(p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
  110. Mat4.targetTo(tmpMat, p, centroid, normal);
  111. Mat4.setTranslation(tmpMat, p);
  112. Mat4.mul(tmpMat, tmpMat, Mat4.rotX90);
  113. const circle = Circle({ radius });
  114. return transformPrimitive(circle, tmpMat);
  115. }
  116. function getBilayerPlanes(ctx: RuntimeContext, data: Structure, props: BilayerPlanesProps, shape?: Shape<Mesh>): Shape<Mesh> {
  117. const { p1, p2, centroid, normal, radius } = MembraneOrientationProvider.get(data).value!;
  118. const state = MeshBuilder.createState(128, 64, shape && shape.geometry);
  119. getLayerPlane(state, p1, centroid, normal, radius);
  120. getLayerPlane(state, p2, centroid, normal, radius);
  121. return Shape.create(name, data, MeshBuilder.getMesh(state), () => props.color, () => 1, () => '');
  122. }
  123. function getLayerPlane(state: MeshBuilder.State, p: Vec3, centroid: Vec3, normal: Vec3, radius: number) {
  124. const circle = getCircle(p, centroid, normal, radius);
  125. state.currentGroup = 0;
  126. MeshBuilder.addPrimitive(state, Mat4.id, circle);
  127. MeshBuilder.addPrimitiveFlipped(state, Mat4.id, circle);
  128. }
  129. function getBilayerSpheres(ctx: RuntimeContext, data: Structure, props: BilayerSpheresProps): Shape<Spheres> {
  130. const { density } = props;
  131. const { radius, p1, p2, normal } = MembraneOrientationProvider.get(data).value!;
  132. const spheresBuilder = SpheresBuilder.create();
  133. getLayerSpheres(spheresBuilder, p1, normal, density, radius * radius);
  134. getLayerSpheres(spheresBuilder, p2, normal, density, radius * radius);
  135. return Shape.create(name, data, spheresBuilder.getSpheres(), () => props.color, () => props.sphereSize, () => '');
  136. }
  137. function getLayerSpheres(spheresBuilder: SpheresBuilder, point: Vec3, normalVector: Vec3, density: number, sqRadius: number) {
  138. Vec3.normalize(normalVector, normalVector);
  139. const d = -Vec3.dot(normalVector, point);
  140. const rep = Vec3();
  141. for (let i = -1000, il = 1000; i < il; i += density) {
  142. for (let j = -1000, jl = 1000; j < jl; j += density) {
  143. Vec3.set(rep, i, j, normalVector[2] === 0 ? 0 : -(d + i * normalVector[0] + j * normalVector[1]) / normalVector[2]);
  144. if (Vec3.squaredDistance(rep, point) < sqRadius) {
  145. spheresBuilder.add(rep[0], rep[1], rep[2], 0);
  146. }
  147. }
  148. }
  149. }