orientation-ellipsoid-mesh.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * Copyright (c) 2019 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 { UnitsMeshParams, UnitsVisual, UnitsMeshVisual } from '../../../mol-repr/structure/units-visual';
  8. import { VisualUpdateState } from '../../../mol-repr/util';
  9. import { VisualContext } from '../../../mol-repr/visual';
  10. import { Unit, Structure, StructureElement } from '../../../mol-model/structure';
  11. import { Theme } from '../../../mol-theme/theme';
  12. import { Mesh } from '../../../mol-geo/geometry/mesh/mesh';
  13. import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder';
  14. import { Vec3 } from '../../../mol-math/linear-algebra';
  15. import { addEllipsoid } from '../../../mol-geo/geometry/mesh/builder/ellipsoid';
  16. import { Axes3D, Sphere3D } from '../../../mol-math/geometry';
  17. import { PickingId } from '../../../mol-geo/geometry/picking';
  18. import { OrderedSet, Interval } from '../../../mol-data/int';
  19. import { EmptyLoci, Loci } from '../../../mol-model/loci';
  20. import { UnitIndex } from '../../../mol-model/structure/structure/element/element';
  21. import { LocationIterator } from '../../../mol-geo/util/location-iterator';
  22. import { MoleculeType } from '../../../mol-model/structure/model/types';
  23. import { BaseGeometry } from '../../../mol-geo/geometry/base';
  24. import { StructureGroup } from './util/common';
  25. export const OrientationEllipsoidMeshParams = {
  26. ...UnitsMeshParams,
  27. sizeFactor: PD.Numeric(1, { min: 0, max: 2, step: 0.1 }),
  28. detail: PD.Numeric(0, { min: 0, max: 3, step: 1 }, BaseGeometry.CustomQualityParamInfo),
  29. };
  30. export type OrientationEllipsoidMeshParams = typeof OrientationEllipsoidMeshParams
  31. export function OrientationEllipsoidMeshVisual(materialId: number): UnitsVisual<OrientationEllipsoidMeshParams> {
  32. return UnitsMeshVisual<OrientationEllipsoidMeshParams>({
  33. defaultProps: PD.getDefaultValues(OrientationEllipsoidMeshParams),
  34. createGeometry: createOrientationEllipsoidMesh,
  35. createLocationIterator: UnitIterator,
  36. getLoci: getUnitLoci,
  37. eachLocation: eachUnit,
  38. setUpdateState: (state: VisualUpdateState, newProps: PD.Values<OrientationEllipsoidMeshParams>, currentProps: PD.Values<OrientationEllipsoidMeshParams>) => {
  39. state.createGeometry = (
  40. newProps.sizeFactor !== currentProps.sizeFactor ||
  41. newProps.detail !== currentProps.detail
  42. );
  43. }
  44. }, materialId);
  45. }
  46. //
  47. export interface OrientationEllipsoidMeshProps {
  48. detail: number,
  49. sizeFactor: number,
  50. }
  51. function isUnitApplicable(unit: Unit) {
  52. if (Unit.Traits.is(unit.traits, Unit.Trait.MultiChain)) return false;
  53. if (Unit.Traits.is(unit.traits, Unit.Trait.Partitioned)) return false;
  54. if (Unit.isCoarse(unit)) return true;
  55. if (unit.elements.length === 0) return false;
  56. unit.model.atomicHierarchy.derived.residue.moleculeType;
  57. const rI = unit.residueIndex[unit.elements[0]];
  58. const mt = unit.model.atomicHierarchy.derived.residue.moleculeType[rI];
  59. if (mt === MoleculeType.Ion) return false;
  60. if (mt === MoleculeType.Water) return false;
  61. return true;
  62. }
  63. export function createOrientationEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: OrientationEllipsoidMeshProps, mesh?: Mesh): Mesh {
  64. if (!isUnitApplicable(unit)) return Mesh.createEmpty(mesh);
  65. const { detail, sizeFactor } = props;
  66. const vertexCount = 256;
  67. const builderState = MeshBuilder.createState(vertexCount, vertexCount / 2, mesh);
  68. const axes = unit.principalAxes.boxAxes;
  69. const { origin, dirA, dirB } = axes;
  70. const size = Axes3D.size(Vec3(), axes);
  71. Vec3.scale(size, size, sizeFactor / 2);
  72. const radiusScale = Vec3.create(size[2], size[1], size[0]);
  73. builderState.currentGroup = 0;
  74. addEllipsoid(builderState, origin, dirA, dirB, radiusScale, detail + 1);
  75. const m = MeshBuilder.getMesh(builderState);
  76. const sphere = Sphere3D.expand(Sphere3D(), unit.boundary.sphere, 1 * props.sizeFactor);
  77. m.setBoundingSphere(sphere);
  78. return m;
  79. }
  80. //
  81. function UnitIterator(structureGroup: StructureGroup): LocationIterator {
  82. const { group, structure } = structureGroup;
  83. const groupCount = 1;
  84. const instanceCount = group.units.length;
  85. const location = StructureElement.Location.create(structure);
  86. const getLocation = (groupIndex: number, instanceIndex: number) => {
  87. const unit = group.units[instanceIndex];
  88. location.unit = unit;
  89. location.element = unit.elements[groupIndex];
  90. return location;
  91. };
  92. return LocationIterator(groupCount, instanceCount, 1, getLocation);
  93. }
  94. function getUnitLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
  95. const { objectId, instanceId } = pickingId;
  96. if (id === objectId) {
  97. const { structure, group } = structureGroup;
  98. const unit = group.units[instanceId];
  99. const indices = OrderedSet.ofBounds(0, unit.elements.length) as OrderedSet<UnitIndex>;
  100. return StructureElement.Loci(structure, [{ unit, indices }]);
  101. }
  102. return EmptyLoci;
  103. }
  104. function eachUnit(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) {
  105. let changed = false;
  106. if (!StructureElement.Loci.is(loci)) return false;
  107. const { structure, group } = structureGroup;
  108. if (!Structure.areEquivalent(loci.structure, structure)) return false;
  109. const elementCount = group.elements.length;
  110. for (const e of loci.elements) {
  111. const unitIdx = group.unitIndexMap.get(e.unit.id);
  112. if (unitIdx !== undefined) {
  113. if (OrderedSet.size(e.indices) === elementCount) {
  114. if (apply(Interval.ofSingleton(unitIdx))) changed = true;
  115. }
  116. }
  117. }
  118. return changed;
  119. }