volume.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Grid } from './grid';
  7. import { OrderedSet } from '../../mol-data/int';
  8. import { Sphere3D } from '../../mol-math/geometry';
  9. import { Vec3, Mat4 } from '../../mol-math/linear-algebra';
  10. import { BoundaryHelper } from '../../mol-math/geometry/boundary-helper';
  11. import { CubeFormat } from '../../mol-model-formats/volume/cube';
  12. import { equalEps } from '../../mol-math/linear-algebra/3d/common';
  13. import { ModelFormat } from '../../mol-model-formats/format';
  14. import { CustomProperties } from '../custom-property';
  15. export interface Volume {
  16. readonly label?: string
  17. readonly entryId?: string,
  18. readonly grid: Grid
  19. readonly sourceData: ModelFormat
  20. // TODO use...
  21. customProperties: CustomProperties
  22. /**
  23. * Not to be accessed directly, each custom property descriptor
  24. * defines property accessors that use this field to store the data.
  25. */
  26. _propertyData: { [name: string]: any }
  27. // TODO add as customProperty?
  28. readonly colorVolume?: Volume
  29. }
  30. export namespace Volume {
  31. export type CellIndex = { readonly '@type': 'cell-index' } & number
  32. export type IsoValue = IsoValue.Absolute | IsoValue.Relative
  33. export namespace IsoValue {
  34. export type Relative = Readonly<{ kind: 'relative', relativeValue: number }>
  35. export type Absolute = Readonly<{ kind: 'absolute', absoluteValue: number }>
  36. export function areSame(a: IsoValue, b: IsoValue, stats: Grid['stats']) {
  37. return equalEps(toAbsolute(a, stats).absoluteValue, toAbsolute(b, stats).absoluteValue, stats.sigma / 100);
  38. }
  39. export function absolute(value: number): Absolute { return { kind: 'absolute', absoluteValue: value }; }
  40. export function relative(value: number): Relative { return { kind: 'relative', relativeValue: value }; }
  41. export function calcAbsolute(stats: Grid['stats'], relativeValue: number): number {
  42. return relativeValue * stats.sigma + stats.mean;
  43. }
  44. export function calcRelative(stats: Grid['stats'], absoluteValue: number): number {
  45. return stats.sigma === 0 ? 0 : ((absoluteValue - stats.mean) / stats.sigma);
  46. }
  47. export function toAbsolute(value: IsoValue, stats: Grid['stats']): Absolute {
  48. return value.kind === 'absolute' ? value : { kind: 'absolute', absoluteValue: IsoValue.calcAbsolute(stats, value.relativeValue) };
  49. }
  50. export function toRelative(value: IsoValue, stats: Grid['stats']): Relative {
  51. return value.kind === 'relative' ? value : { kind: 'relative', relativeValue: IsoValue.calcRelative(stats, value.absoluteValue) };
  52. }
  53. export function toString(value: IsoValue) {
  54. return value.kind === 'relative'
  55. ? `${value.relativeValue.toFixed(2)} σ`
  56. : `${value.absoluteValue.toPrecision(4)}`;
  57. }
  58. }
  59. export const One: Volume = {
  60. label: '',
  61. grid: Grid.One,
  62. sourceData: { kind: '', name: '', data: {} },
  63. customProperties: new CustomProperties(),
  64. _propertyData: Object.create(null),
  65. };
  66. export function areEquivalent(volA: Volume, volB: Volume) {
  67. return Grid.areEquivalent(volA.grid, volB.grid);
  68. }
  69. export function isEmpty(vol: Volume) {
  70. return Grid.isEmpty(vol.grid);
  71. }
  72. export function isOrbitals(volume: Volume) {
  73. if (!CubeFormat.is(volume.sourceData)) return false;
  74. return volume.sourceData.data.header.orbitals;
  75. }
  76. export interface Loci { readonly kind: 'volume-loci', readonly volume: Volume }
  77. export function Loci(volume: Volume): Loci { return { kind: 'volume-loci', volume }; }
  78. export function isLoci(x: any): x is Loci { return !!x && x.kind === 'volume-loci'; }
  79. export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume; }
  80. export function isLociEmpty(loci: Loci) { return Grid.isEmpty(loci.volume.grid); }
  81. export function getBoundingSphere(volume: Volume, boundingSphere?: Sphere3D) {
  82. if (!boundingSphere) boundingSphere = Sphere3D();
  83. const transform = Grid.getGridToCartesianTransform(volume.grid);
  84. const [x, y, z] = volume.grid.cells.space.dimensions;
  85. const cpA = Vec3.create(0, 0, 0); Vec3.transformMat4(cpA, cpA, transform);
  86. const cpB = Vec3.create(x, y, z); Vec3.transformMat4(cpB, cpB, transform);
  87. const cpC = Vec3.create(x, 0, 0); Vec3.transformMat4(cpC, cpC, transform);
  88. const cpD = Vec3.create(0, y, z); Vec3.transformMat4(cpD, cpC, transform);
  89. const cpE = Vec3.create(0, 0, z); Vec3.transformMat4(cpE, cpE, transform);
  90. const cpF = Vec3.create(x, 0, z); Vec3.transformMat4(cpF, cpF, transform);
  91. const cpG = Vec3.create(x, y, 0); Vec3.transformMat4(cpG, cpG, transform);
  92. const cpH = Vec3.create(0, y, 0); Vec3.transformMat4(cpH, cpH, transform);
  93. const center = Vec3();
  94. Vec3.add(center, cpA, cpB);
  95. Vec3.scale(center, center, 0.5);
  96. const d = Math.max(Vec3.distance(cpA, cpB), Vec3.distance(cpC, cpD));
  97. Sphere3D.set(boundingSphere, center, d / 2);
  98. Sphere3D.setExtrema(boundingSphere, [cpA, cpB, cpC, cpD, cpE, cpF, cpG, cpH]);
  99. return boundingSphere;
  100. }
  101. export namespace Isosurface {
  102. export interface Loci { readonly kind: 'isosurface-loci', readonly volume: Volume, readonly isoValue: Volume.IsoValue }
  103. export function Loci(volume: Volume, isoValue: Volume.IsoValue): Loci { return { kind: 'isosurface-loci', volume, isoValue }; }
  104. export function isLoci(x: any): x is Loci { return !!x && x.kind === 'isosurface-loci'; }
  105. export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && Volume.IsoValue.areSame(a.isoValue, b.isoValue, a.volume.grid.stats); }
  106. export function isLociEmpty(loci: Loci) { return loci.volume.grid.cells.data.length === 0; }
  107. export function getBoundingSphere(volume: Volume, isoValue: Volume.IsoValue, boundingSphere?: Sphere3D) {
  108. // TODO get bounding sphere for subgrid with values >= isoValue
  109. return Volume.getBoundingSphere(volume, boundingSphere);
  110. }
  111. }
  112. export namespace Cell {
  113. export interface Loci { readonly kind: 'cell-loci', readonly volume: Volume, readonly indices: OrderedSet<CellIndex> }
  114. export function Loci(volume: Volume, indices: OrderedSet<CellIndex>): Loci { return { kind: 'cell-loci', volume, indices }; }
  115. export function isLoci(x: any): x is Loci { return !!x && x.kind === 'cell-loci'; }
  116. export function areLociEqual(a: Loci, b: Loci) { return a.volume === b.volume && OrderedSet.areEqual(a.indices, b.indices); }
  117. export function isLociEmpty(loci: Loci) { return OrderedSet.size(loci.indices) === 0; }
  118. const boundaryHelper = new BoundaryHelper('98');
  119. const tmpBoundaryPos = Vec3();
  120. export function getBoundingSphere(volume: Volume, indices: OrderedSet<CellIndex>, boundingSphere?: Sphere3D) {
  121. boundaryHelper.reset();
  122. const transform = Grid.getGridToCartesianTransform(volume.grid);
  123. const { getCoords } = volume.grid.cells.space;
  124. for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
  125. const o = OrderedSet.getAt(indices, i);
  126. getCoords(o, tmpBoundaryPos);
  127. Vec3.transformMat4(tmpBoundaryPos, tmpBoundaryPos, transform);
  128. boundaryHelper.includePosition(tmpBoundaryPos);
  129. }
  130. boundaryHelper.finishedIncludeStep();
  131. for (let i = 0, _i = OrderedSet.size(indices); i < _i; i++) {
  132. const o = OrderedSet.getAt(indices, i);
  133. getCoords(o, tmpBoundaryPos);
  134. Vec3.transformMat4(tmpBoundaryPos, tmpBoundaryPos, transform);
  135. boundaryHelper.radiusPosition(tmpBoundaryPos);
  136. }
  137. const bs = boundaryHelper.getSphere(boundingSphere);
  138. return Sphere3D.expand(bs, bs, Mat4.getMaxScaleOnAxis(transform) * 10);
  139. }
  140. }
  141. }