bond-intra-unit-line.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /**
  2. * Copyright (c) 2020-2021 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 { Unit, Structure, StructureElement } from '../../../mol-model/structure';
  9. import { Theme } from '../../../mol-theme/theme';
  10. import { Vec3 } from '../../../mol-math/linear-algebra';
  11. import { arrayEqual } from '../../../mol-util';
  12. import { LinkStyle, createLinkLines } from './util/link';
  13. import { UnitsVisual, UnitsLinesParams, UnitsLinesVisual, StructureGroup } from '../units-visual';
  14. import { VisualUpdateState } from '../../util';
  15. import { BondType } from '../../../mol-model/structure/model/types';
  16. import { BondIterator, BondLineParams, getIntraBondLoci, eachIntraBond, makeIntraBondIgnoreTest } from './util/bond';
  17. import { Sphere3D } from '../../../mol-math/geometry';
  18. import { Lines } from '../../../mol-geo/geometry/lines/lines';
  19. import { IntAdjacencyGraph } from '../../../mol-math/graph';
  20. // avoiding namespace lookup improved performance in Chrome (Aug 2020)
  21. const isBondType = BondType.is;
  22. function createIntraUnitBondLines(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondLineParams>, lines?: Lines) {
  23. if (!Unit.isAtomic(unit)) return Lines.createEmpty(lines);
  24. const { child } = structure;
  25. const childUnit = child?.unitMap.get(unit.id);
  26. if (child && !childUnit) return Lines.createEmpty(lines);
  27. if (props.includeParent) {
  28. if (!child) throw new Error('expected child to exist');
  29. }
  30. const location = StructureElement.Location.create(structure, unit);
  31. const elements = unit.elements;
  32. const bonds = unit.bonds;
  33. const { edgeCount, a, b, edgeProps, offset } = bonds;
  34. const { order: _order, flags: _flags } = edgeProps;
  35. const { sizeFactor } = props;
  36. if (!edgeCount) return Lines.createEmpty(lines);
  37. const vRef = Vec3();
  38. const pos = unit.conformation.invariantPosition;
  39. const builderProps = {
  40. linkCount: edgeCount * 2,
  41. referencePosition: (edgeIndex: number) => {
  42. let aI = a[edgeIndex], bI = b[edgeIndex];
  43. if (aI > bI) [aI, bI] = [bI, aI];
  44. if (offset[aI + 1] - offset[aI] === 1) [aI, bI] = [bI, aI];
  45. // TODO prefer reference atoms within rings
  46. for (let i = offset[aI], il = offset[aI + 1]; i < il; ++i) {
  47. const _bI = b[i];
  48. if (_bI !== bI && _bI !== aI) return pos(elements[_bI], vRef);
  49. }
  50. for (let i = offset[bI], il = offset[bI + 1]; i < il; ++i) {
  51. const _aI = a[i];
  52. if (_aI !== aI && _aI !== bI) return pos(elements[_aI], vRef);
  53. }
  54. return null;
  55. },
  56. position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
  57. pos(elements[a[edgeIndex]], posA);
  58. pos(elements[b[edgeIndex]], posB);
  59. },
  60. style: (edgeIndex: number) => {
  61. const o = _order[edgeIndex];
  62. const f = _flags[edgeIndex];
  63. if (isBondType(f, BondType.Flag.MetallicCoordination) || isBondType(f, BondType.Flag.HydrogenBond)) {
  64. // show metall coordinations and hydrogen bonds with dashed cylinders
  65. return LinkStyle.Dashed;
  66. } else if (o === 2) {
  67. return LinkStyle.Double;
  68. } else if (o === 3) {
  69. return LinkStyle.Triple;
  70. } else {
  71. return LinkStyle.Solid;
  72. }
  73. },
  74. radius: (edgeIndex: number) => {
  75. location.element = elements[a[edgeIndex]];
  76. const sizeA = theme.size.size(location);
  77. location.element = elements[b[edgeIndex]];
  78. const sizeB = theme.size.size(location);
  79. return Math.min(sizeA, sizeB) * sizeFactor;
  80. },
  81. ignore: makeIntraBondIgnoreTest(structure, unit, props)
  82. };
  83. const l = createLinkLines(ctx, builderProps, props, lines);
  84. const sphere = Sphere3D.expand(Sphere3D(), (childUnit ?? unit).boundary.sphere, 1 * sizeFactor);
  85. l.setBoundingSphere(sphere);
  86. return l;
  87. }
  88. export const IntraUnitBondLineParams = {
  89. ...UnitsLinesParams,
  90. ...BondLineParams,
  91. includeParent: PD.Boolean(false),
  92. };
  93. export type IntraUnitBondLineParams = typeof IntraUnitBondLineParams
  94. export function IntraUnitBondLineVisual(materialId: number): UnitsVisual<IntraUnitBondLineParams> {
  95. return UnitsLinesVisual<IntraUnitBondLineParams>({
  96. defaultProps: PD.getDefaultValues(IntraUnitBondLineParams),
  97. createGeometry: createIntraUnitBondLines,
  98. createLocationIterator: BondIterator.fromGroup,
  99. getLoci: getIntraBondLoci,
  100. eachLocation: eachIntraBond,
  101. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<IntraUnitBondLineParams>, currentProps: PD.Values<IntraUnitBondLineParams>, newTheme: Theme, currentTheme: Theme, newStructureGroup: StructureGroup, currentStructureGroup: StructureGroup) => {
  102. state.createGeometry = (
  103. newProps.sizeFactor !== currentProps.sizeFactor ||
  104. newProps.linkScale !== currentProps.linkScale ||
  105. newProps.linkSpacing !== currentProps.linkSpacing ||
  106. newProps.dashCount !== currentProps.dashCount ||
  107. newProps.ignoreHydrogens !== currentProps.ignoreHydrogens ||
  108. !arrayEqual(newProps.includeTypes, currentProps.includeTypes) ||
  109. !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes)
  110. );
  111. const newUnit = newStructureGroup.group.units[0];
  112. const currentUnit = currentStructureGroup.group.units[0];
  113. if (Unit.isAtomic(newUnit) && Unit.isAtomic(currentUnit)) {
  114. if (!IntAdjacencyGraph.areEqual(newUnit.bonds, currentUnit.bonds)) {
  115. state.createGeometry = true;
  116. state.updateTransform = true;
  117. state.updateColor = true;
  118. state.updateSize = true;
  119. }
  120. }
  121. }
  122. }, materialId);
  123. }