bond-inter-unit-cylinder.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. */
  6. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  7. import { VisualContext } from '../../visual';
  8. import { Structure, StructureElement, Bond, Unit } from '../../../mol-model/structure';
  9. import { Theme } from '../../../mol-theme/theme';
  10. import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
  11. import { Vec3 } from '../../../mol-math/linear-algebra';
  12. import { BitFlags, arrayEqual } from '../../../mol-util';
  13. import { createLinkCylinderImpostors, createLinkCylinderMesh, LinkStyle } from './util/link';
  14. import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual, ComplexCylindersParams, ComplexCylindersVisual } from '../complex-visual';
  15. import { VisualUpdateState } from '../../util';
  16. import { BondType } from '../../../mol-model/structure/model/types';
  17. import { BondCylinderParams, BondIterator, getInterBondLoci, eachInterBond, makeInterBondIgnoreTest } from './util/bond';
  18. import { Sphere3D } from '../../../mol-math/geometry';
  19. import { Cylinders } from '../../../mol-geo/geometry/cylinders/cylinders';
  20. import { WebGLContext } from '../../../mol-gl/webgl/context';
  21. const tmpRefPosBondIt = new Bond.ElementBondIterator();
  22. function setRefPosition(pos: Vec3, structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex) {
  23. tmpRefPosBondIt.setElement(structure, unit, index);
  24. while (tmpRefPosBondIt.hasNext) {
  25. const bA = tmpRefPosBondIt.move();
  26. bA.otherUnit.conformation.position(bA.otherUnit.elements[bA.otherIndex], pos);
  27. return pos;
  28. }
  29. return null;
  30. }
  31. const tmpRef = Vec3();
  32. function getInterUnitBondCylinderBuilderProps(structure: Structure, theme: Theme, props: PD.Values<InterUnitBondCylinderParams>) {
  33. const locE = StructureElement.Location.create(structure);
  34. const locB = Bond.Location(structure, undefined, undefined, structure, undefined, undefined);
  35. const bonds = structure.interUnitBonds;
  36. const { edgeCount, edges } = bonds;
  37. const { sizeFactor, sizeAspectRatio } = props;
  38. const delta = Vec3();
  39. const radius = (edgeIndex: number) => {
  40. const b = edges[edgeIndex];
  41. locB.aUnit = structure.unitMap.get(b.unitA);
  42. locB.aIndex = b.indexA;
  43. locB.bUnit = structure.unitMap.get(b.unitB);
  44. locB.bIndex = b.indexB;
  45. return theme.size.size(locB) * sizeFactor;
  46. };
  47. const radiusA = (edgeIndex: number) => {
  48. const b = edges[edgeIndex];
  49. locE.unit = structure.unitMap.get(b.unitA);
  50. locE.element = locE.unit.elements[b.indexA];
  51. return theme.size.size(locE) * sizeFactor;
  52. };
  53. const radiusB = (edgeIndex: number) => {
  54. const b = edges[edgeIndex];
  55. locE.unit = structure.unitMap.get(b.unitB);
  56. locE.element = locE.unit.elements[b.indexB];
  57. return theme.size.size(locE) * sizeFactor;
  58. };
  59. return {
  60. linkCount: edgeCount,
  61. referencePosition: (edgeIndex: number) => {
  62. const b = edges[edgeIndex];
  63. let unitA: Unit.Atomic, unitB: Unit.Atomic;
  64. let indexA: StructureElement.UnitIndex, indexB: StructureElement.UnitIndex;
  65. if (b.unitA < b.unitB) {
  66. unitA = structure.unitMap.get(b.unitA) as Unit.Atomic;
  67. unitB = structure.unitMap.get(b.unitB) as Unit.Atomic;
  68. indexA = b.indexA;
  69. indexB = b.indexB;
  70. } else if (b.unitA > b.unitB) {
  71. unitA = structure.unitMap.get(b.unitB) as Unit.Atomic;
  72. unitB = structure.unitMap.get(b.unitA) as Unit.Atomic;
  73. indexA = b.indexB;
  74. indexB = b.indexA;
  75. } else {
  76. throw new Error('same units in createInterUnitBondCylinderMesh');
  77. }
  78. return setRefPosition(tmpRef, structure, unitA, indexA) || setRefPosition(tmpRef, structure, unitB, indexB);
  79. },
  80. position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
  81. const b = edges[edgeIndex];
  82. const uA = structure.unitMap.get(b.unitA);
  83. const uB = structure.unitMap.get(b.unitB);
  84. const rA = radiusA(edgeIndex), rB = radiusB(edgeIndex);
  85. const r = Math.min(rA, rB) * sizeAspectRatio;
  86. const oA = Math.sqrt(Math.max(0, rA * rA - r * r)) - 0.05;
  87. const oB = Math.sqrt(Math.max(0, rB * rB - r * r)) - 0.05;
  88. uA.conformation.position(uA.elements[b.indexA], posA);
  89. uB.conformation.position(uB.elements[b.indexB], posB);
  90. if (oA <= 0.01 && oB <= 0.01) return;
  91. Vec3.normalize(delta, Vec3.sub(delta, posB, posA));
  92. Vec3.scaleAndAdd(posA, posA, delta, oA);
  93. Vec3.scaleAndAdd(posB, posB, delta, -oB);
  94. },
  95. style: (edgeIndex: number) => {
  96. const o = edges[edgeIndex].props.order;
  97. const f = BitFlags.create(edges[edgeIndex].props.flag);
  98. if (BondType.is(f, BondType.Flag.MetallicCoordination) || BondType.is(f, BondType.Flag.HydrogenBond)) {
  99. // show metall coordinations and hydrogen bonds with dashed cylinders
  100. return LinkStyle.Dashed;
  101. } else if (o === 2) {
  102. return LinkStyle.Double;
  103. } else if (o === 3) {
  104. return LinkStyle.Triple;
  105. } else {
  106. return LinkStyle.Solid;
  107. }
  108. },
  109. radius: (edgeIndex: number) => {
  110. return radius(edgeIndex) * sizeAspectRatio;
  111. },
  112. ignore: makeInterBondIgnoreTest(structure, props)
  113. };
  114. }
  115. function createInterUnitBondCylinderImpostors(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitBondCylinderParams>, cylinders?: Cylinders) {
  116. if (!structure.interUnitBonds.edgeCount) return Cylinders.createEmpty(cylinders);
  117. const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
  118. const m = createLinkCylinderImpostors(ctx, builderProps, props, cylinders);
  119. const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
  120. m.setBoundingSphere(sphere);
  121. return m;
  122. }
  123. function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitBondCylinderParams>, mesh?: Mesh) {
  124. if (!structure.interUnitBonds.edgeCount) return Mesh.createEmpty(mesh);
  125. const builderProps = getInterUnitBondCylinderBuilderProps(structure, theme, props);
  126. const m = createLinkCylinderMesh(ctx, builderProps, props, mesh);
  127. const sphere = Sphere3D.expand(Sphere3D(), structure.boundary.sphere, 1 * props.sizeFactor);
  128. m.setBoundingSphere(sphere);
  129. return m;
  130. }
  131. export const InterUnitBondCylinderParams = {
  132. ...ComplexMeshParams,
  133. ...ComplexCylindersParams,
  134. ...BondCylinderParams,
  135. sizeFactor: PD.Numeric(0.3, { min: 0, max: 10, step: 0.01 }),
  136. sizeAspectRatio: PD.Numeric(2 / 3, { min: 0, max: 3, step: 0.01 }),
  137. useImpostor: PD.Boolean(true),
  138. };
  139. export type InterUnitBondCylinderParams = typeof InterUnitBondCylinderParams
  140. export function InterUnitBondCylinderVisual(materialId: number, props?: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) {
  141. return props?.useImpostor && webgl && webgl.extensions.fragDepth
  142. ? InterUnitBondCylinderImpostorVisual(materialId)
  143. : InterUnitBondCylinderMeshVisual(materialId);
  144. }
  145. export function InterUnitBondCylinderImpostorVisual(materialId: number): ComplexVisual<InterUnitBondCylinderParams> {
  146. return ComplexCylindersVisual<InterUnitBondCylinderParams>({
  147. defaultProps: PD.getDefaultValues(InterUnitBondCylinderParams),
  148. createGeometry: createInterUnitBondCylinderImpostors,
  149. createLocationIterator: BondIterator.fromStructure,
  150. getLoci: getInterBondLoci,
  151. eachLocation: eachInterBond,
  152. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitBondCylinderParams>, currentProps: PD.Values<InterUnitBondCylinderParams>) => {
  153. state.createGeometry = (
  154. newProps.linkScale !== currentProps.linkScale ||
  155. newProps.linkSpacing !== currentProps.linkSpacing ||
  156. newProps.ignoreHydrogens !== currentProps.ignoreHydrogens ||
  157. newProps.linkCap !== currentProps.linkCap ||
  158. newProps.dashCount !== currentProps.dashCount ||
  159. newProps.dashScale !== currentProps.dashScale ||
  160. newProps.dashCap !== currentProps.dashCap ||
  161. !arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
  162. !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
  163. );
  164. },
  165. mustRecreate: (props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {
  166. return !props.useImpostor || !webgl;
  167. }
  168. }, materialId);
  169. }
  170. export function InterUnitBondCylinderMeshVisual(materialId: number): ComplexVisual<InterUnitBondCylinderParams> {
  171. return ComplexMeshVisual<InterUnitBondCylinderParams>({
  172. defaultProps: PD.getDefaultValues(InterUnitBondCylinderParams),
  173. createGeometry: createInterUnitBondCylinderMesh,
  174. createLocationIterator: BondIterator.fromStructure,
  175. getLoci: getInterBondLoci,
  176. eachLocation: eachInterBond,
  177. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<InterUnitBondCylinderParams>, currentProps: PD.Values<InterUnitBondCylinderParams>) => {
  178. state.createGeometry = (
  179. newProps.sizeFactor !== currentProps.sizeFactor ||
  180. newProps.sizeAspectRatio !== currentProps.sizeAspectRatio ||
  181. newProps.radialSegments !== currentProps.radialSegments ||
  182. newProps.linkScale !== currentProps.linkScale ||
  183. newProps.linkSpacing !== currentProps.linkSpacing ||
  184. newProps.ignoreHydrogens !== currentProps.ignoreHydrogens ||
  185. newProps.linkCap !== currentProps.linkCap ||
  186. newProps.dashCount !== currentProps.dashCount ||
  187. newProps.dashScale !== currentProps.dashScale ||
  188. newProps.dashCap !== currentProps.dashCap ||
  189. !arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
  190. !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
  191. );
  192. },
  193. mustRecreate: (props: PD.Values<InterUnitBondCylinderParams>, webgl?: WebGLContext) => {
  194. return props.useImpostor && !!webgl;
  195. }
  196. }, materialId);
  197. }