carbohydrate-terminal-link-cylinder.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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, 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 { createLinkCylinderMesh, LinkCylinderParams, LinkStyle } from './util/link';
  13. import { UnitsMeshParams } from '../units-visual';
  14. import { ComplexVisual, ComplexMeshVisual } from '../complex-visual';
  15. import { VisualUpdateState } from '../../util';
  16. import { LocationIterator } from '../../../mol-geo/util/location-iterator';
  17. import { OrderedSet, Interval } from '../../../mol-data/int';
  18. import { PickingId } from '../../../mol-geo/geometry/picking';
  19. import { EmptyLoci, Loci } from '../../../mol-model/loci';
  20. import { getElementIdx, MetalsSet } from '../../../mol-model/structure/structure/unit/bonds/common';
  21. import { getAltResidueLociFromId, getAltResidueLoci } from './util/common';
  22. function createCarbohydrateTerminalLinkCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<CarbohydrateTerminalLinkParams>, mesh?: Mesh) {
  23. const { terminalLinks, elements } = structure.carbohydrates;
  24. const { terminalLinkSizeFactor } = props;
  25. const location = StructureElement.Location.create(structure);
  26. const builderProps = {
  27. linkCount: terminalLinks.length,
  28. position: (posA: Vec3, posB: Vec3, edgeIndex: number) => {
  29. const l = terminalLinks[edgeIndex];
  30. if (l.fromCarbohydrate) {
  31. Vec3.copy(posA, elements[l.carbohydrateIndex].geometry.center);
  32. l.elementUnit.conformation.position(l.elementUnit.elements[l.elementIndex], posB);
  33. } else {
  34. l.elementUnit.conformation.position(l.elementUnit.elements[l.elementIndex], posA);
  35. Vec3.copy(posB, elements[l.carbohydrateIndex].geometry.center);
  36. }
  37. },
  38. radius: (edgeIndex: number) => {
  39. const l = terminalLinks[edgeIndex];
  40. if (l.fromCarbohydrate) {
  41. const carb = elements[l.carbohydrateIndex];
  42. const ring = carb.unit.rings.all[carb.ringIndex];
  43. location.unit = carb.unit;
  44. location.element = carb.unit.elements[ring[0]];
  45. } else {
  46. location.unit = l.elementUnit;
  47. location.element = l.elementUnit.elements[l.elementIndex];
  48. }
  49. return theme.size.size(location) * terminalLinkSizeFactor;
  50. },
  51. style: (edgeIndex: number) => {
  52. const l = terminalLinks[edgeIndex];
  53. const eI = l.elementUnit.elements[l.elementIndex];
  54. const beI = getElementIdx(l.elementUnit.model.atomicHierarchy.atoms.type_symbol.value(eI));
  55. return MetalsSet.has(beI) ? LinkStyle.Dashed : LinkStyle.Solid;
  56. }
  57. };
  58. return createLinkCylinderMesh(ctx, builderProps, props, mesh);
  59. }
  60. export const CarbohydrateTerminalLinkParams = {
  61. ...UnitsMeshParams,
  62. ...LinkCylinderParams,
  63. terminalLinkSizeFactor: PD.Numeric(0.2, { min: 0, max: 3, step: 0.01 }),
  64. };
  65. export type CarbohydrateTerminalLinkParams = typeof CarbohydrateTerminalLinkParams
  66. export function CarbohydrateTerminalLinkVisual(materialId: number): ComplexVisual<CarbohydrateTerminalLinkParams> {
  67. return ComplexMeshVisual<CarbohydrateTerminalLinkParams>({
  68. defaultProps: PD.getDefaultValues(CarbohydrateTerminalLinkParams),
  69. createGeometry: createCarbohydrateTerminalLinkCylinderMesh,
  70. createLocationIterator: CarbohydrateTerminalLinkIterator,
  71. getLoci: getTerminalLinkLoci,
  72. eachLocation: eachTerminalLink,
  73. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<CarbohydrateTerminalLinkParams>, currentProps: PD.Values<CarbohydrateTerminalLinkParams>) => {
  74. state.createGeometry = (
  75. newProps.terminalLinkSizeFactor !== currentProps.terminalLinkSizeFactor ||
  76. newProps.radialSegments !== currentProps.radialSegments ||
  77. newProps.linkCap !== currentProps.linkCap
  78. );
  79. }
  80. }, materialId);
  81. }
  82. function CarbohydrateTerminalLinkIterator(structure: Structure): LocationIterator {
  83. const { elements, terminalLinks } = structure.carbohydrates;
  84. const groupCount = terminalLinks.length;
  85. const instanceCount = 1;
  86. const location = StructureElement.Location.create(structure);
  87. const getLocation = (groupIndex: number) => {
  88. const terminalLink = terminalLinks[groupIndex];
  89. if (terminalLink.fromCarbohydrate) {
  90. const carb = elements[terminalLink.carbohydrateIndex];
  91. const ring = carb.unit.rings.all[carb.ringIndex];
  92. location.unit = carb.unit;
  93. location.element = carb.unit.elements[ring[0]];
  94. } else {
  95. location.unit = terminalLink.elementUnit;
  96. location.element = terminalLink.elementUnit.elements[terminalLink.elementIndex];
  97. }
  98. return location;
  99. };
  100. return LocationIterator(groupCount, instanceCount, 1, getLocation, true);
  101. }
  102. function getTerminalLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
  103. const { objectId, groupId } = pickingId;
  104. if (id === objectId) {
  105. const { terminalLinks, elements } = structure.carbohydrates;
  106. const l = terminalLinks[groupId];
  107. const carb = elements[l.carbohydrateIndex];
  108. return StructureElement.Loci.union(
  109. getAltResidueLociFromId(structure, carb.unit, carb.residueIndex, carb.altId),
  110. getAltResidueLoci(structure, l.elementUnit, l.elementUnit.elements[l.elementIndex])
  111. );
  112. }
  113. return EmptyLoci;
  114. }
  115. function eachTerminalLink(loci: Loci, structure: Structure, apply: (interval: Interval) => boolean) {
  116. let changed = false;
  117. if (!StructureElement.Loci.is(loci)) return false;
  118. if (!Structure.areEquivalent(loci.structure, structure)) return false;
  119. const { getTerminalLinkIndices } = structure.carbohydrates;
  120. for (const { unit, indices } of loci.elements) {
  121. if (!Unit.isAtomic(unit)) continue;
  122. OrderedSet.forEach(indices, v => {
  123. // TODO avoid duplicate calls to apply
  124. const linkIndices = getTerminalLinkIndices(unit, unit.elements[v]);
  125. for (let i = 0, il = linkIndices.length; i < il; ++i) {
  126. if (apply(Interval.ofSingleton(linkIndices[i]))) changed = true;
  127. }
  128. });
  129. }
  130. return changed;
  131. }