boundary-helper.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Vec3 } from '../../mol-math/linear-algebra/3d';
  7. import { Box3D } from './primitives/box3d';
  8. import { Sphere3D } from './primitives/sphere3d';
  9. /**
  10. * Usage:
  11. *
  12. * 1. .reset(tolerance); tolerance plays part in the "extend" step
  13. * 2. for each point/sphere call boundaryStep()
  14. * 3. .finishBoundaryStep
  15. * 4. for each point/sphere call extendStep
  16. * 5. use .center/.radius or call getSphere/getBox
  17. */
  18. export class BoundaryHelper {
  19. private count = 0;
  20. private extremes = [Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero(), Vec3.zero()];
  21. private u = Vec3.zero();
  22. private v = Vec3.zero();
  23. tolerance = 0;
  24. center: Vec3 = Vec3.zero();
  25. radius = 0;
  26. reset(tolerance: number) {
  27. Vec3.set(this.center, 0, 0, 0);
  28. for (let i = 0; i < 6; i++) {
  29. const e = i % 2 === 0 ? Number.MAX_VALUE : -Number.MAX_VALUE;
  30. Vec3.set(this.extremes[i], e, e, e);
  31. }
  32. this.radius = 0;
  33. this.count = 0;
  34. this.tolerance = tolerance;
  35. }
  36. boundaryStep(p: Vec3, r: number) {
  37. updateExtremeMin(0, this.extremes[0], p, r);
  38. updateExtremeMax(0, this.extremes[1], p, r);
  39. updateExtremeMin(1, this.extremes[2], p, r);
  40. updateExtremeMax(1, this.extremes[3], p, r);
  41. updateExtremeMin(2, this.extremes[4], p, r);
  42. updateExtremeMax(2, this.extremes[5], p, r);
  43. this.count++;
  44. }
  45. finishBoundaryStep() {
  46. if (this.count === 0) return;
  47. let maxSpan = 0, mI = 0, mJ = 0;
  48. for (let i = 0; i < 5; i++) {
  49. for (let j = i + 1; j < 6; j++) {
  50. const d = Vec3.squaredDistance(this.extremes[i], this.extremes[j]);
  51. if (d > maxSpan) {
  52. maxSpan = d;
  53. mI = i;
  54. mJ = j;
  55. }
  56. }
  57. }
  58. Vec3.add(this.center, this.extremes[mI], this.extremes[mJ]);
  59. Vec3.scale(this.center, this.center, 0.5);
  60. this.radius = Vec3.distance(this.center, this.extremes[mI]);
  61. }
  62. extendStep(p: Vec3, r: number) {
  63. const d = Vec3.distance(p, this.center);
  64. if ((1 + this.tolerance) * this.radius >= r + d) return;
  65. Vec3.sub(this.u, p, this.center);
  66. Vec3.normalize(this.u, this.u);
  67. Vec3.scale(this.v, this.u, -this.radius);
  68. Vec3.add(this.v, this.v, this.center);
  69. Vec3.scale(this.u, this.u, r + d);
  70. Vec3.add(this.u, this.u, this.center);
  71. Vec3.add(this.center, this.u, this.v);
  72. Vec3.scale(this.center, this.center, 0.5);
  73. this.radius = 0.5 * (r + d + this.radius);
  74. }
  75. getBox(): Box3D {
  76. Vec3.copy(this.u, this.extremes[0]);
  77. Vec3.copy(this.v, this.extremes[0]);
  78. for (let i = 1; i < 6; i++) {
  79. Vec3.min(this.u, this.u, this.extremes[i]);
  80. Vec3.max(this.v, this.v, this.extremes[i]);
  81. }
  82. return { min: Vec3.clone(this.u), max: Vec3.clone(this.v) };
  83. }
  84. getSphere(): Sphere3D {
  85. return { center: Vec3.clone(this.center), radius: this.radius };
  86. }
  87. constructor() {
  88. this.reset(0);
  89. }
  90. }
  91. function updateExtremeMin(d: number, e: Vec3, center: Vec3, r: number) {
  92. if (center[d] - r < e[d]) {
  93. Vec3.copy(e, center);
  94. e[d] -= r;
  95. }
  96. }
  97. function updateExtremeMax(d: number, e: Vec3, center: Vec3, r: number) {
  98. if (center[d] + r > e[d]) {
  99. Vec3.copy(e, center);
  100. e[d] += r;
  101. }
  102. }