@woosh/meep-engine 2.118.5 → 2.118.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/build/meep.cjs +520 -479
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +520 -479
  4. package/package.json +4 -4
  5. package/src/core/binary/BitSet.d.ts +0 -5
  6. package/src/core/binary/BitSet.d.ts.map +1 -1
  7. package/src/core/binary/BitSet.js +2 -10
  8. package/src/core/binary/base64/Base64.d.ts.map +1 -0
  9. package/src/core/binary/largest_common_alignment_uint32.d.ts.map +1 -0
  10. package/src/core/{collection/array/typed → binary}/largest_common_alignment_uint32.js +1 -1
  11. package/src/core/collection/CuckooFilter.d.ts +34 -8
  12. package/src/core/collection/CuckooFilter.d.ts.map +1 -1
  13. package/src/core/collection/CuckooFilter.js +29 -1
  14. package/src/core/collection/array/typed/array_buffer_copy.d.ts.map +1 -1
  15. package/src/core/collection/array/typed/array_buffer_copy.js +3 -1
  16. package/src/core/collection/array/typed/is_array_buffer_equals.js +1 -1
  17. package/src/core/collection/array/typed/is_typed_array_equals.js +1 -1
  18. package/src/core/collection/map/HashMap.js +7 -7
  19. package/src/core/geom/2d/aabb/AABB2.d.ts.map +1 -1
  20. package/src/core/geom/2d/aabb/AABB2.js +43 -22
  21. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.d.ts.map +1 -1
  22. package/src/core/geom/2d/aabb/aabb2_compute_center_from_multiple.js +6 -1
  23. package/src/core/geom/2d/quad-tree/QuadTreeDatum.d.ts +3 -5
  24. package/src/core/geom/2d/quad-tree/QuadTreeDatum.d.ts.map +1 -1
  25. package/src/core/geom/2d/quad-tree/QuadTreeDatum.js +9 -19
  26. package/src/core/geom/2d/rectangle_to_aabb.d.ts +8 -0
  27. package/src/core/geom/2d/rectangle_to_aabb.d.ts.map +1 -0
  28. package/src/core/geom/2d/rectangle_to_aabb.js +29 -0
  29. package/src/core/geom/3d/tetrahedra/TetrahedralMesh.js +1 -1
  30. package/src/core/graph/graph_compute_distance_matrix.d.ts +1 -1
  31. package/src/core/graph/graph_compute_distance_matrix.d.ts.map +1 -1
  32. package/src/core/graph/graph_compute_distance_matrix.js +1 -1
  33. package/src/core/graph/graph_compute_laplacian_matrix.d.ts +1 -1
  34. package/src/core/graph/graph_compute_laplacian_matrix.d.ts.map +1 -1
  35. package/src/core/graph/graph_compute_laplacian_matrix.js +1 -1
  36. package/src/core/graph/layout/CircleLayout.js +2 -2
  37. package/src/core/graph/layout/box/BoxLayouter.d.ts.map +1 -1
  38. package/src/core/graph/layout/box/BoxLayouter.js +11 -5
  39. package/src/core/graph/layout/box/aabb2_force_into_container.d.ts +7 -0
  40. package/src/core/graph/layout/box/aabb2_force_into_container.d.ts.map +1 -0
  41. package/src/core/graph/layout/box/aabb2_force_into_container.js +56 -0
  42. package/src/core/graph/layout/box/applyCentralGravityAABB2.d.ts.map +1 -1
  43. package/src/core/graph/layout/box/applyCentralGravityAABB2.js +2 -8
  44. package/src/core/graph/layout/box/forceIntoBox.d.ts +4 -2
  45. package/src/core/graph/layout/box/forceIntoBox.d.ts.map +1 -1
  46. package/src/core/graph/layout/box/forceIntoBox.js +5 -31
  47. package/src/core/graph/layout/box/position_box_next_to_box.d.ts +10 -0
  48. package/src/core/graph/layout/box/position_box_next_to_box.d.ts.map +1 -0
  49. package/src/core/graph/layout/box/position_box_next_to_box.js +218 -0
  50. package/src/core/graph/layout/box/pullBoxTowardsPoint.d.ts.map +1 -1
  51. package/src/core/graph/layout/box/pullBoxTowardsPoint.js +8 -3
  52. package/src/core/graph/layout/box/resolveAABB2Overlap.d.ts +2 -2
  53. package/src/core/graph/layout/box/resolveAABB2Overlap.d.ts.map +1 -1
  54. package/src/core/graph/layout/box/resolveAABB2Overlap.js +12 -4
  55. package/src/core/graph/layout/box/resolveBoxOverlapUsingForce.d.ts +1 -0
  56. package/src/core/graph/layout/box/resolveBoxOverlapUsingForce.d.ts.map +1 -1
  57. package/src/core/graph/layout/box/resolveBoxOverlapUsingForce.js +1 -0
  58. package/src/core/math/matrix/SquareMatrix.d.ts.map +1 -0
  59. package/src/core/{graph → math/matrix}/SquareMatrix.js +3 -3
  60. package/src/core/math/spline/cubicCurve.d.ts +3 -8
  61. package/src/core/math/spline/cubicCurve.d.ts.map +1 -1
  62. package/src/core/math/spline/cubicCurve.js +4 -32
  63. package/src/core/model/reactive/js/compileReactiveToJS.d.ts.map +1 -1
  64. package/src/core/model/reactive/js/compileReactiveToJS.js +56 -1
  65. package/src/core/model/reactive/model/terminal/ReactiveReference.d.ts.map +1 -1
  66. package/src/core/model/reactive/model/terminal/ReactiveReference.js +2 -2
  67. package/src/core/model/reactive/trigger/ReactiveTrigger.d.ts +2 -2
  68. package/src/core/model/reactive/trigger/ReactiveTrigger.d.ts.map +1 -1
  69. package/src/core/model/reactive/trigger/ReactiveTrigger.js +1 -1
  70. package/src/core/process/BaseProcess.d.ts.map +1 -1
  71. package/src/core/process/BaseProcess.js +8 -7
  72. package/src/core/process/SimpleLifecycle.d.ts +5 -0
  73. package/src/core/process/SimpleLifecycle.d.ts.map +1 -1
  74. package/src/core/process/SimpleLifecycle.js +8 -0
  75. package/src/core/process/task/Task.js +1 -1
  76. package/src/engine/animation/clip/AnimationClip.d.ts.map +1 -1
  77. package/src/engine/animation/clip/AnimationClip.js +50 -8
  78. package/src/engine/animation/clip/AnimationClipBinding.js +1 -1
  79. package/src/engine/animation/clip/AnimationTrack.d.ts +11 -1
  80. package/src/engine/animation/clip/AnimationTrack.d.ts.map +1 -1
  81. package/src/engine/animation/clip/AnimationTrack.js +50 -7
  82. package/src/engine/animation/curve/binding/BoundPlainNumericPropertyWriter.d.ts +8 -0
  83. package/src/engine/animation/curve/binding/BoundPlainNumericPropertyWriter.d.ts.map +1 -0
  84. package/src/engine/animation/curve/binding/BoundPlainNumericPropertyWriter.js +16 -0
  85. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  86. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
  87. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +6 -6
  88. package/src/engine/graphics/texture/sampler/Sampler2D.js +1 -1
  89. package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.d.ts +3 -3
  90. package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.d.ts.map +1 -1
  91. package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js +6 -6
  92. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.d.ts.map +1 -1
  93. package/src/generation/grid/generation/discrete/GridTaskCellularAutomata.js +35 -31
  94. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.d.ts.map +1 -1
  95. package/src/generation/grid/generation/grid/GridTaskAddNodesFixed.js +8 -11
  96. package/src/generation/grid/generation/road/RoadConnectionNetwork.d.ts +6 -2
  97. package/src/generation/grid/generation/road/RoadConnectionNetwork.d.ts.map +1 -1
  98. package/src/generation/grid/generation/road/RoadConnectionNetwork.js +36 -30
  99. package/src/generation/placement/action/GridCellAction.js +1 -1
  100. package/src/generation/theme/AreaTheme.d.ts.map +1 -1
  101. package/src/generation/theme/AreaTheme.js +0 -1
  102. package/src/generation/theme/TerrainLayerRule.d.ts.map +1 -1
  103. package/src/generation/theme/TerrainLayerRule.js +2 -1
  104. package/src/generation/theme/TerrainLayerRuleAggregator.d.ts +5 -1
  105. package/src/generation/theme/TerrainLayerRuleAggregator.d.ts.map +1 -1
  106. package/src/generation/theme/TerrainLayerRuleAggregator.js +7 -2
  107. package/src/generation/theme/ThemeEngine.d.ts +2 -2
  108. package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
  109. package/src/generation/theme/ThemeEngine.js +1 -4
  110. package/src/generation/theme/cell/CellProcessingRule.d.ts.map +1 -1
  111. package/src/generation/theme/cell/CellProcessingRule.js +10 -13
  112. package/src/generation/theme/cell/CellProcessingRuleSet.d.ts.map +1 -1
  113. package/src/generation/theme/cell/CellProcessingRuleSet.js +6 -7
  114. package/src/view/SVG.d.ts.map +1 -1
  115. package/src/view/SVG.js +0 -15
  116. package/src/view/html_element_to_aabb.d.ts +8 -0
  117. package/src/view/html_element_to_aabb.d.ts.map +1 -0
  118. package/src/view/html_element_to_aabb.js +29 -0
  119. package/src/view/tooltip/TooltipManager.d.ts.map +1 -1
  120. package/src/view/tooltip/TooltipManager.js +7 -9
  121. package/src/view/tooltip/TooltipView.d.ts +0 -1
  122. package/src/view/tooltip/TooltipView.d.ts.map +1 -1
  123. package/src/view/tooltip/TooltipView.js +10 -280
  124. package/src/view/tooltip/gml/TooltipParser.d.ts.map +1 -1
  125. package/src/view/tooltip/gml/TooltipParser.js +1 -13
  126. package/src/core/binary/Base64.d.ts.map +0 -1
  127. package/src/core/collection/array/typed/largest_common_alignment_uint32.d.ts.map +0 -1
  128. package/src/core/graph/SquareMatrix.d.ts.map +0 -1
  129. package/src/engine/simulation/DormandPrince.d.ts +0 -19
  130. package/src/engine/simulation/DormandPrince.d.ts.map +0 -1
  131. package/src/engine/simulation/DormandPrince.js +0 -242
  132. /package/src/core/binary/{Base64.d.ts → base64/Base64.d.ts} +0 -0
  133. /package/src/core/binary/{Base64.js → base64/Base64.js} +0 -0
  134. /package/src/core/{collection/array/typed → binary}/largest_common_alignment_uint32.d.ts +0 -0
  135. /package/src/core/{graph → math/matrix}/SquareMatrix.d.ts +0 -0
package/build/meep.cjs CHANGED
@@ -60349,15 +60349,15 @@ class HashMap {
60349
60349
 
60350
60350
  this.__entries_bound++;
60351
60351
 
60352
- if (this.__entries[i] !== undefined) {
60352
+ const existing_entry = this.__entries[i];
60353
60353
 
60354
- // entry exists, let's reuse it
60354
+ if (existing_entry !== undefined) {
60355
60355
 
60356
- const entry = this.__entries[i];
60356
+ // entry exists, let's reuse it
60357
60357
 
60358
- entry.hash = hash;
60359
- entry.key = k;
60360
- entry.value = v;
60358
+ existing_entry.hash = hash;
60359
+ existing_entry.key = k;
60360
+ existing_entry.value = v;
60361
60361
 
60362
60362
  } else {
60363
60363
 
@@ -64357,7 +64357,7 @@ class Task {
64357
64357
  * Run entire task synchronously to completion
64358
64358
  */
64359
64359
  executeSync() {
64360
- this.initialize();
64360
+ this.initialize(this,null);
64361
64361
 
64362
64362
  this.on.started.send0();
64363
64363
 
@@ -71113,7 +71113,7 @@ const GROW_FACTOR$1 = 1.3;
71113
71113
  * @constant
71114
71114
  * @type {number}
71115
71115
  */
71116
- const SHRINK_FACTOR$1 = 0.5;
71116
+ const DEFAULT_SHRINK_FACTOR = 0.5;
71117
71117
 
71118
71118
  /**
71119
71119
  * Minimum number of bits to increase the capacity by
@@ -71159,7 +71159,7 @@ class BitSet {
71159
71159
  /**
71160
71160
  * @type {number}
71161
71161
  */
71162
- this.__shrinkFactor = SHRINK_FACTOR$1;
71162
+ this.__shrinkFactor = DEFAULT_SHRINK_FACTOR;
71163
71163
  }
71164
71164
 
71165
71165
  preventShrink() {
@@ -71502,14 +71502,6 @@ class BitSet {
71502
71502
  }
71503
71503
  }
71504
71504
 
71505
- /**
71506
- *
71507
- * @param {BitSet} set
71508
- */
71509
- and(set) {
71510
- throw new Error("NIY");
71511
- }
71512
-
71513
71505
  /**
71514
71506
  * Returns the value of the bit with the specified index.
71515
71507
  * @param {int} bitIndex
@@ -78937,13 +78929,13 @@ const ProcessState = {
78937
78929
  class BaseProcess {
78938
78930
 
78939
78931
  id = "";
78932
+
78940
78933
  /**
78941
78934
  * @readonly
78942
78935
  * @type {ObservedEnum.<ProcessState>}
78943
78936
  */
78944
78937
  __state = new ObservedEnum(ProcessState.New, ProcessState);
78945
78938
 
78946
-
78947
78939
  /**
78948
78940
  * @returns {ObservedEnum<ProcessState>}
78949
78941
  */
@@ -78953,10 +78945,10 @@ class BaseProcess {
78953
78945
 
78954
78946
  initialize() {
78955
78947
 
78956
- const currentState = this.__state.getValue();
78948
+ const state_current = this.__state.getValue();
78957
78949
 
78958
- if (currentState !== ProcessState.New && currentState !== ProcessState.Finalized) {
78959
- throw new IllegalStateException(`Expected New or Finalized state, instead got ${objectKeyByValue(ProcessState, currentState)}`);
78950
+ if (state_current !== ProcessState.New && state_current !== ProcessState.Finalized) {
78951
+ throw new IllegalStateException(`Expected New or Finalized state, instead got ${objectKeyByValue(ProcessState, state_current)}`);
78960
78952
  }
78961
78953
 
78962
78954
 
@@ -78971,6 +78963,7 @@ class BaseProcess {
78971
78963
  this.shutdown();
78972
78964
  }
78973
78965
 
78966
+ // note that we don't bind state value, because it is liable to change in "shutdown"
78974
78967
  if (state.getValue() !== ProcessState.Stopped && state.getValue() !== ProcessState.Initialized) {
78975
78968
  throw new IllegalStateException(`Expected Initialized or Stopped state, instead got ${objectKeyByValue(ProcessState, state.getValue())}`);
78976
78969
  }
@@ -78979,10 +78972,10 @@ class BaseProcess {
78979
78972
  }
78980
78973
 
78981
78974
  startup() {
78982
- const currentState = this.__state.getValue();
78975
+ const state_current = this.__state.getValue();
78983
78976
 
78984
- if (currentState !== ProcessState.Initialized && currentState !== ProcessState.Stopped) {
78985
- throw new IllegalStateException(`Expected Initial state, instead got ${objectKeyByValue(ProcessState, currentState)}`);
78977
+ if (state_current !== ProcessState.Initialized && state_current !== ProcessState.Stopped) {
78978
+ throw new IllegalStateException(`Expected Initial state, instead got ${objectKeyByValue(ProcessState, state_current)}`);
78986
78979
  }
78987
78980
 
78988
78981
  this.__state.set(ProcessState.Running);
@@ -80835,22 +80828,6 @@ class AABB2 {
80835
80828
  this._expandToFit(x, y, x, y);
80836
80829
  }
80837
80830
 
80838
- /**
80839
- * retrieve midpoint of AABB along X axis
80840
- * @returns {number}
80841
- */
80842
- midX() {
80843
- return (this.x1 + this.x0) / 2;
80844
- }
80845
-
80846
- /**
80847
- * retrieve midpoint of AABB along Y axis
80848
- * @returns {number}
80849
- */
80850
- midY() {
80851
- return (this.y1 + this.y0) / 2;
80852
- }
80853
-
80854
80831
  /**
80855
80832
  * NOTE: only 1 intersection point is produced
80856
80833
  * @param {Vector2} p0
@@ -81002,16 +80979,35 @@ class AABB2 {
81002
80979
 
81003
80980
  /**
81004
80981
  *
81005
- * @param {Vector2} result
80982
+ * @param {Vector2} [result]
81006
80983
  */
81007
80984
  getCenter(result = new Vector2()) {
80985
+
81008
80986
  result.set(this.centerX, this.centerY);
81009
80987
 
81010
80988
  return result;
81011
80989
  }
81012
80990
 
81013
80991
  /**
81014
- *
80992
+ * retrieve midpoint of AABB along X axis
80993
+ * @deprecated use {@link centerX} instead
80994
+ * @returns {number}
80995
+ */
80996
+ midX() {
80997
+ return this.centerX;
80998
+ }
80999
+
81000
+ /**
81001
+ * retrieve midpoint of AABB along Y axis
81002
+ * @deprecated use {@link centerY} instead
81003
+ * @returns {number}
81004
+ */
81005
+ midY() {
81006
+ return this.centerY;
81007
+ }
81008
+
81009
+ /**
81010
+ * midpoint along X axis
81015
81011
  * @return {number}
81016
81012
  */
81017
81013
  get centerX() {
@@ -81019,7 +81015,7 @@ class AABB2 {
81019
81015
  }
81020
81016
 
81021
81017
  /**
81022
- *
81018
+ * midpoint along Y axis
81023
81019
  * @return {number}
81024
81020
  */
81025
81021
  get centerY() {
@@ -81095,7 +81091,10 @@ class AABB2 {
81095
81091
  * @param {number} deltaY
81096
81092
  */
81097
81093
  move(deltaX, deltaY) {
81098
- this.set(this.x0 + deltaX, this.y0 + deltaY, this.x1 + deltaX, this.y1 + deltaY);
81094
+ this.set(
81095
+ this.x0 + deltaX, this.y0 + deltaY,
81096
+ this.x1 + deltaX, this.y1 + deltaY
81097
+ );
81099
81098
  }
81100
81099
 
81101
81100
  /**
@@ -81194,6 +81193,21 @@ class AABB2 {
81194
81193
  fromJSON(json) {
81195
81194
  this.set(json.x0, json.y0, json.x1, json.y1);
81196
81195
  }
81196
+
81197
+ /**
81198
+ * @param {number[]|Float32Array} target
81199
+ * @param {number} offset
81200
+ * @returns {number[]|Float32Array}
81201
+ */
81202
+ toArray(target=[], offset=0){
81203
+
81204
+ target[offset] = this.x0;
81205
+ target[offset+1] = this.y0;
81206
+ target[offset+2] = this.x1;
81207
+ target[offset+3] = this.y1;
81208
+
81209
+ return target;
81210
+ }
81197
81211
  }
81198
81212
 
81199
81213
 
@@ -81207,8 +81221,8 @@ class AABB2 {
81207
81221
  */
81208
81222
  function computeLineBetweenTwoBoxes(b0, b1, p0, p1) {
81209
81223
  //compute box centers
81210
- const c0 = new Vector2(b0.midX(), b0.midY());
81211
- const c1 = new Vector2(b1.midX(), b1.midY());
81224
+ const c0 = new Vector2(b0.centerX, b0.centerY);
81225
+ const c1 = new Vector2(b1.centerX, b1.centerY);
81212
81226
 
81213
81227
  const i0 = b0.lineIntersectionPoint(c0, c1, p0);
81214
81228
 
@@ -97730,6 +97744,14 @@ class SimpleLifecycle {
97730
97744
  this.priority = priority;
97731
97745
  }
97732
97746
 
97747
+ /**
97748
+ *
97749
+ * @returns {SimpleLifecycleStateType}
97750
+ */
97751
+ get state(){
97752
+ return this.sm.getState();
97753
+ }
97754
+
97733
97755
  makeReady() {
97734
97756
  const s = this.sm.getState();
97735
97757
  if (s === SimpleLifecycleStateType.Ready) {
@@ -100306,20 +100328,8 @@ class TooltipParser {
100306
100328
  */
100307
100329
  parse(code) {
100308
100330
 
100309
- const existing = this.#cache.get(code);
100331
+ return this.#cache.getOrCompute(code, parseTooltipString);
100310
100332
 
100311
- if (existing !== null) {
100312
- // result is cached, reuse
100313
- return existing;
100314
- }
100315
-
100316
- // no cached result, parse
100317
- const tokens = parseTooltipString(code);
100318
-
100319
- // cache result
100320
- this.#cache.put(code, tokens);
100321
-
100322
- return tokens;
100323
100333
  }
100324
100334
  }
100325
100335
 
@@ -100850,146 +100860,95 @@ class GMLEngine {
100850
100860
 
100851
100861
  }
100852
100862
 
100853
- const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
100854
-
100855
100863
  /**
100856
100864
  *
100857
- * @param {number} r0 inner radius
100858
- * @param {number} r1 outer radius
100859
- * @param {number} a0 starting angle
100860
- * @param {number} a1 end angle
100861
- * @returns {string}
100862
- */
100863
- function svgArc(r0, r1, a0, a1) {
100864
- let da, df;
100865
- const c0 = Math.cos(a0);
100866
- const s0 = Math.sin(a0);
100867
- const c1 = Math.cos(a1);
100868
- const s1 = Math.sin(a1);
100869
-
100870
- da = (a1 < a0 && (da = a0, a0 = a1, a1 = da), a1 - a0);
100871
-
100872
- df = da < Math.PI ? "0" : "1";
100873
-
100874
- return "M" + r1 * c0 + "," + r1 * s0
100875
- + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1
100876
- + "L" + r0 * c1 + "," + r0 * s1
100877
- + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0
100878
- + "Z";
100879
- }
100880
-
100881
- /**
100882
- *
100883
- * @param {number} r0 Inner radius
100884
- * @param {number} r1 Outer Radius
100885
- * @param {number} a0 Start of the inner arc (in rad)
100886
- * @param {number} b0 End of the inner arc (in rad)
100887
- * @param {number} a1 Start of the outer arc (in rad)
100888
- * @param {number} b1 End of the outer arc (in rad)
100889
- * @returns {string}
100865
+ * @param {Rectangle} rect
100866
+ * @returns {AABB2}
100890
100867
  */
100891
- function svgArc2(r0, r1, a0, b0, a1, b1) {
100892
- const ac0 = Math.cos(a0);
100893
- const as0 = Math.sin(a0);
100868
+ function rectangle_to_aabb(rect) {
100869
+ const position = rect.position;
100894
100870
 
100895
- const bc0 = Math.cos(b0);
100896
- const bs0 = Math.sin(b0);
100871
+ const x0 = position.x;
100872
+ const y0 = position.y;
100897
100873
 
100898
- const ac1 = Math.cos(a1);
100899
- const as1 = Math.sin(a1);
100874
+ const size = rect.size;
100900
100875
 
100901
- const bc1 = Math.cos(b1);
100902
- const bs1 = Math.sin(b1);
100876
+ let x1 = x0;
100877
+ let y1 = y0;
100903
100878
 
100904
- const largeArcFlag0 = b0 - a0 <= Math.PI ? "0" : "1";
100905
- const largeArcFlag1 = b1 - a1 <= Math.PI ? "0" : "1";
100879
+ // sanitize bounds
100880
+ if (size.x > 0) {
100881
+ x1 += size.x;
100882
+ }
100906
100883
 
100907
- return `
100908
- M${r1 * ac1},${r1 * as1}
100909
- A${r1},${r1} 0 ${largeArcFlag1},1 ${r1 * bc1},${r1 * bs1}
100910
- L${r0 * bc0},${r0 * bs0}
100911
- A${r0},${r0} 0 ${largeArcFlag0},0 ${r0 * ac0},${r0 * as0}
100912
- Z
100913
- `;
100914
- }
100884
+ if (size.y > 0) {
100885
+ y1 += size.y;
100886
+ }
100915
100887
 
100888
+ return new AABB2(x0, y0, x1, y1);
100889
+ }
100890
+
100916
100891
  /**
100917
100892
  *
100918
- * @param {String} tag
100919
- * @returns {Element}
100920
- */
100921
- function createSVGElement(tag) {
100922
- return document.createElementNS(SVG_NAMESPACE, tag);
100923
- }
100924
-
100925
- var SVG = {
100926
- arcPath: svgArc,
100927
- svgArc2,
100928
- createElement: createSVGElement
100929
- };
100930
-
100931
- class CompassArrowView extends View {
100932
- constructor() {
100933
- super();
100934
-
100935
-
100936
- const elSVG = SVG.createElement('svg');
100937
-
100938
- const elDefs = SVG.createElement('defs');
100939
- const elGradient = SVG.createElement('linearGradient');
100940
- elGradient.setAttribute('id', 'gradient');
100941
-
100942
- elDefs.appendChild(elGradient);
100943
- const elStop0 = SVG.createElement('stop');
100944
- const elStop1 = SVG.createElement('stop');
100945
-
100946
- elStop0.setAttribute('offset', '50%');
100947
- elStop1.setAttribute('offset', '100%');
100948
-
100949
- elStop0.classList.add('color-stop-0');
100950
- elStop1.classList.add('color-stop-1');
100893
+ * @param {number} subject_v0
100894
+ * @param {number} subject_v1
100895
+ * @param {number} bounds_v0
100896
+ * @param {number} bounds_v1
100897
+ * @returns {number} displacement for subject to fit into bounds
100898
+ */
100899
+ function interval_force_into_bounds(
100900
+ subject_v0, subject_v1,
100901
+ bounds_v0, bounds_v1
100902
+ ){
100951
100903
 
100952
- elGradient.appendChild(elStop0);
100953
- elGradient.appendChild(elStop1);
100954
- elSVG.appendChild(elDefs);
100904
+ let displacement = 0;
100955
100905
 
100956
- const h = 28;
100957
- const w = 24;
100906
+ const bounds_span = bounds_v1 - bounds_v0;
100958
100907
 
100959
- elSVG.classList.add('compass');
100908
+ const subject_span = subject_v1 - subject_v0;
100960
100909
 
100961
- elSVG.setAttribute('width', h);
100962
- elSVG.setAttribute('height', w);
100910
+ if(bounds_span >= subject_span) {
100911
+ // fits
100963
100912
 
100964
- const elPolygon = SVG.createElement('polygon');
100913
+ if (subject_v0 < bounds_v0) {
100914
+ displacement = bounds_v0 - subject_v0;
100915
+ } else if (subject_v1 > bounds_v1) {
100916
+ displacement = bounds_v1 - subject_v1;
100917
+ }
100965
100918
 
100966
- const half_w = w / 2;
100967
- elPolygon.setAttribute('points', `${h},${half_w} 0,${w} 0,0`);
100968
- elPolygon.classList.add('compass-triangle');
100919
+ }else {
100920
+ // too big to fit, pick the smallest displacement to occupy entire bounds
100969
100921
 
100970
- elSVG.appendChild(elPolygon);
100922
+ const a = subject_v0 - bounds_v0;
100923
+ const b = bounds_v1 - subject_v1;
100971
100924
 
100972
- //set rotation origin to the bottom middle of the element
100973
- const t = half_w * Math.SQRT2;
100925
+ if(a > 0 && a < b){
100926
+ displacement = -a;
100927
+ }else if(b > 0){
100928
+ displacement = b;
100929
+ }
100930
+ }
100974
100931
 
100975
- elSVG.style.transformOrigin = `${t}px ${half_w}px`;
100976
- elSVG.style.top = `-${half_w}px`;
100977
- elSVG.style.left = `-${t}px`;
100932
+ return displacement;
100933
+ }
100978
100934
 
100979
- this.el = elSVG;
100980
- }
100935
+ /**
100936
+ *
100937
+ * @param {AABB2} container
100938
+ * @param {AABB2} box
100939
+ */
100940
+ function aabb2_force_into_container(container, box) {
100981
100941
 
100982
- rotateFromDirectionVector(x, y) {
100983
- const angle = Math.atan2(y, x);
100942
+ const dX = interval_force_into_bounds(box.x0, box.x1, container.x0, container.x1);
100943
+ const dY = interval_force_into_bounds(box.y0, box.y1, container.y0, container.y1);
100984
100944
 
100985
- this.rotation.set(angle);
100986
- }
100945
+ box.move(dX, dY);
100987
100946
  }
100988
100947
 
100989
100948
  /**
100990
100949
  * Note: assumes that boxes to be contained are strictly smaller than the container in both dimensions
100991
100950
  * @param {AABB2} container
100992
- * @param {Array.<AABB2>} boxes
100951
+ * @param {Array.<AABB2|{locked?:boolean}>} boxes
100993
100952
  */
100994
100953
  function forceIntoBox(container, boxes) {
100995
100954
  for (let i = 0, l = boxes.length; i < l; i++) {
@@ -100999,34 +100958,8 @@ function forceIntoBox(container, boxes) {
100999
100958
  continue;
101000
100959
  }
101001
100960
 
101002
- let dX = 0;
101003
- let dY = 0;
100961
+ aabb2_force_into_container(container, box);
101004
100962
 
101005
- const bx0 = box.x0;
101006
- const bx1 = box.x1;
101007
-
101008
- const by0 = box.y0;
101009
- const by1 = box.y1;
101010
-
101011
- const cx0 = container.x0;
101012
- const cx1 = container.x1;
101013
-
101014
- const cy0 = container.y0;
101015
- const cy1 = container.y1;
101016
-
101017
- if (bx0 < cx0) {
101018
- dX = cx0 - bx0;
101019
- } else if (bx1 > cx1) {
101020
- dX = cx1 - bx1;
101021
- }
101022
-
101023
- if (by0 < cy0) {
101024
- dY = cy0 - by0;
101025
- } else if (by1 > cy1) {
101026
- dY = cy1 - by1;
101027
- }
101028
-
101029
- box.move(dX, dY);
101030
100963
  }
101031
100964
  }
101032
100965
 
@@ -101037,9 +100970,14 @@ function forceIntoBox(container, boxes) {
101037
100970
  * @param {number} targetY target point Y coordinate
101038
100971
  * @param {number} strength
101039
100972
  */
101040
- function pullBoxTowardsPoint(box, targetX, targetY, strength) {
101041
- const midX = box.midX();
101042
- const midY = box.midY();
100973
+ function pullBoxTowardsPoint(
100974
+ box,
100975
+ targetX, targetY,
100976
+ strength
100977
+ ) {
100978
+
100979
+ const midX = box.centerX;
100980
+ const midY = box.centerY;
101043
100981
 
101044
100982
  const deltaX = targetX - midX;
101045
100983
  const deltaY = targetY - midY;
@@ -101055,6 +100993,7 @@ function pullBoxTowardsPoint(box, targetX, targetY, strength) {
101055
100993
  *
101056
100994
  * @param {Vector2[]} forces
101057
100995
  * @param {Array.<AABB2>} boxes
100996
+ * @returns {number}
101058
100997
  */
101059
100998
  function resolveBoxOverlapUsingForce(forces, boxes) {
101060
100999
  const numBoxes = boxes.length;
@@ -101152,113 +101091,363 @@ function resolveBoxOverlapUsingForce(forces, boxes) {
101152
101091
  /**
101153
101092
  *
101154
101093
  * @param {AABB2[]} boxes
101155
- * @param {number} maxSteps
101094
+ * @param {number} [maxSteps]
101156
101095
  */
101157
- function resolveAABB2Overlap(boxes, maxSteps) {
101096
+ function resolveAABB2Overlap(boxes, maxSteps=3) {
101158
101097
  const forces = [];
101159
101098
 
101160
101099
  const n = boxes.length;
101100
+
101161
101101
  for (let i = 0; i < n; i++) {
101162
101102
  forces.push(new Vector2(0, 0));
101163
101103
  }
101164
101104
 
101165
101105
  let overlapMoves = -1;
101166
- while (maxSteps > 0 && overlapMoves !== 0) {
101167
- maxSteps--;
101106
+
101107
+ let steps = maxSteps;
101108
+
101109
+ while (steps > 0 && overlapMoves !== 0) {
101110
+
101111
+ steps--;
101112
+
101168
101113
  overlapMoves = resolveBoxOverlapUsingForce(forces, boxes);
101114
+
101169
101115
  }
101116
+
101170
101117
  }
101171
101118
 
101172
- class TooltipView extends View {
101119
+ /**
101120
+ *
101121
+ * @param {AABB2} box
101122
+ * @param {AABB2} target
101123
+ * @param {AABB2} bounds
101124
+ * @returns {Vector2}
101125
+ */
101126
+ function computeInitialPlacement(box, target, bounds) {
101173
101127
  /**
101174
- *
101175
- * @param {VisualTip} tip
101176
- * @param {View} contentView
101177
- * @constructor
101128
+ * list of preferred directions, ordered. First direction is higher priority than the next, last is lowest priority
101129
+ * @type {Vector2[]}
101178
101130
  */
101179
- constructor(tip, contentView) {
101180
- super();
101131
+ const preferredDirections = [
101132
+ Vector2.up,
101133
+ Vector2.right,
101134
+ Vector2.down,
101135
+ Vector2.left
101136
+ ];
101181
101137
 
101138
+ const targetCenter = target.getCenter();
101182
101139
 
101183
- /**
101184
- *
101185
- * @type {VisualTip}
101186
- */
101187
- this.model = tip;
101140
+ const targetH = target.getHeight();
101141
+ const targetW = target.getWidth();
101188
101142
 
101189
- //set dom element
101190
- this.el = document.createElement('div');
101191
- this.addClass("tooltip-view");
101143
+ const targetDiagonal = Math.sqrt(targetH * targetH + targetW * targetW);
101192
101144
 
101193
- const vContents = new EmptyView({ classList: ['contents'] });
101145
+ const boxWidth = box.getWidth();
101146
+ const boxHeight = box.getHeight();
101194
101147
 
101195
- this.addChild(vContents);
101148
+ const boxWidth_2 = boxWidth / 2;
101149
+ const boxHeight_2 = boxHeight / 2;
101196
101150
 
101197
- vContents.addChild(contentView);
101151
+ const intersectionPoint = new Vector2();
101198
101152
 
101199
- this.contentView = vContents;
101153
+ const placements = preferredDirections.map(function (direction) {
101154
+ //invert Y axis, screen-space direction is Down(+) and Up(-)
101155
+ const viewportDirection = direction.clone()._multiply(1, -1);
101200
101156
 
101157
+ const pointOutside = viewportDirection.clone();
101158
+ //scale vector to be larger than the box diagonal so it would intersect the boundary when placed in the middle
101159
+ pointOutside.multiplyScalar(targetDiagonal + 1);
101160
+ //move vector to the middle od the box
101161
+ pointOutside.add(targetCenter);
101201
101162
 
101202
- const vCompass = new CompassArrowView();
101163
+ target.lineIntersectionPoint(targetCenter, pointOutside, intersectionPoint);
101203
101164
 
101204
- this.addChild(vCompass);
101165
+ //align placement to the opposite edge of the box
101166
+ const offset = new Vector2(boxWidth_2, boxHeight_2);
101205
101167
 
101206
- this.compass = vCompass;
101207
- }
101168
+ offset.multiply(viewportDirection);
101169
+
101170
+ offset._sub(boxWidth_2, boxHeight_2);
101171
+
101172
+ offset.add(intersectionPoint);
101173
+
101174
+ return offset;
101175
+ });
101208
101176
 
101209
101177
  /**
101210
101178
  *
101211
- * @param {AABB2} bounds
101179
+ * @param {Vector2} placement
101180
+ * @returns {number}
101212
101181
  */
101213
- layout(bounds) {
101214
- const tipBox = element2aabb(this.contentView.el);
101182
+ function computePlacementScore(placement) {
101183
+ //find space for the tip between bounds and the target
101184
+ const availableSpace = new AABB2();
101215
101185
 
101216
- const SPACING = 16;
101186
+ const px0 = placement.x;
101187
+ const px1 = px0 + boxWidth;
101188
+ const py0 = placement.y;
101189
+ const py1 = py0 + boxHeight;
101217
101190
 
101218
- tipBox.grow(SPACING);
101191
+ if (px1 <= target.x0) {
101192
+ //left
101193
+ availableSpace.set(
101194
+ bounds.x0,
101195
+ bounds.y0,
101196
+ target.x0,
101197
+ bounds.y1
101198
+ );
101199
+ } else if (px0 >= target.x1) {
101200
+ //right
101201
+ availableSpace.set(
101202
+ target.x1,
101203
+ bounds.y0,
101204
+ bounds.x1,
101205
+ bounds.y1
101206
+ );
101207
+ } else if (py1 <= target.y0) {
101208
+ //up
101209
+ availableSpace.set(
101210
+ bounds.x0,
101211
+ bounds.y0,
101212
+ bounds.x1,
101213
+ target.y0
101214
+ );
101215
+ } else if (py0 >= target.y1) {
101216
+ //bottom
101217
+ availableSpace.set(
101218
+ bounds.x0,
101219
+ target.y1,
101220
+ bounds.x1,
101221
+ bounds.y1
101222
+ );
101223
+ } else ;
101219
101224
 
101220
- /**
101221
- *
101222
- * @type {VisualTip}
101223
- */
101224
- const tip = this.model;
101225
+ let score = 0;
101225
101226
 
101226
- const tipTarget = tip.target;
101227
+ const availableWidth = availableSpace.getWidth();
101228
+ const availableHeight = availableSpace.getHeight();
101227
101229
 
101228
- const target = rectangle2aabb(tipTarget);
101230
+ const spareWidth = availableWidth - boxWidth;
101231
+ const spareHeight = availableHeight - boxHeight;
101229
101232
 
101230
- const box = positionBoxNextToBox(tipBox, target, bounds);
101233
+ if (spareHeight < 0) {
101234
+ score--;
101235
+ }
101231
101236
 
101232
- box.shrink(SPACING);
101237
+ if (spareWidth < 0) {
101238
+ score--;
101239
+ }
101233
101240
 
101234
- const compass = this.compass;
101241
+ return score;
101242
+ }
101235
101243
 
101236
- function setCompas() {
101244
+ //score placements
101245
+ const scores = placements.map(computePlacementScore);
101237
101246
 
101238
- const targetCenter = new Vector2(target.midX(), target.midY());
101247
+ //find best placement
101248
+ let bestIndex = 0;
101249
+ let bestScore = scores[0];
101239
101250
 
101240
- const tipPoint = new Vector2();
101251
+ for (let i = 1; i < scores.length; i++) {
101252
+ const score = scores[i];
101241
101253
 
101242
- box.computeNearestPointToPoint(targetCenter, tipPoint);
101254
+ if (score > bestScore + EPSILON) {
101255
+ bestScore = score;
101256
+ bestIndex = i;
101257
+ }
101258
+ }
101243
101259
 
101244
- compass.rotateFromDirectionVector(targetCenter.x - tipPoint.x, targetCenter.y - tipPoint.y);
101260
+ return placements[bestIndex];
101261
+ }
101245
101262
 
101246
- compass.position.copy(tipPoint)._add(-box.x0, -box.y0);
101247
- }
101263
+ /**
101264
+ *
101265
+ * @param {AABB2} box
101266
+ * @param {AABB2} target
101267
+ * @param {AABB2} bounds
101268
+ * @returns {AABB2} input {@link box} parameter
101269
+ */
101270
+ function position_box_next_to_box(box, target, bounds) {
101271
+ const b = box.clone();
101272
+ const t = target.clone();
101248
101273
 
101249
- setCompas();
101274
+ //center box inside the bounds
101275
+ const y0 = (bounds.height + b.height) / 2;
101276
+ const x0 = (bounds.width + b.width) / 2;
101250
101277
 
101251
- this.position.set(box.x0, box.y0);
101278
+ b.set(x0, y0, x0 + b.width, y0 + b.height);
101279
+
101280
+ new Vector2(t.centerX, t.centerY);
101281
+
101282
+ t.locked = true;
101283
+
101284
+
101285
+ const initialPosition = computeInitialPlacement(b, t, bounds);
101286
+ //set bounds to initial position
101287
+ b.set(initialPosition.x, initialPosition.y, initialPosition.x + b.width, initialPosition.y + b.height);
101288
+
101289
+ // touch();
101290
+
101291
+ function step() {
101292
+ //pull
101293
+ pullBoxTowardsPoint(b, initialPosition.x + b.width / 2, initialPosition.y + b.height / 2, 0.1);
101294
+
101295
+ resolveAABB2Overlap([b, t], 10);
101296
+
101297
+ forceIntoBox(bounds, [b]);
101298
+ }
101299
+
101300
+ for (let i = 0; i < 5; i++) {
101301
+ step();
101252
101302
  }
101303
+
101304
+ box.copy(b);
101305
+
101306
+ return box;
101307
+ }
101308
+
101309
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
101310
+
101311
+ /**
101312
+ *
101313
+ * @param {number} r0 inner radius
101314
+ * @param {number} r1 outer radius
101315
+ * @param {number} a0 starting angle
101316
+ * @param {number} a1 end angle
101317
+ * @returns {string}
101318
+ */
101319
+ function svgArc(r0, r1, a0, a1) {
101320
+ let da, df;
101321
+ const c0 = Math.cos(a0);
101322
+ const s0 = Math.sin(a0);
101323
+ const c1 = Math.cos(a1);
101324
+ const s1 = Math.sin(a1);
101325
+
101326
+ da = (a1 < a0 && (da = a0, a0 = a1, a1 = da), a1 - a0);
101327
+
101328
+ df = da < Math.PI ? "0" : "1";
101329
+
101330
+ return "M" + r1 * c0 + "," + r1 * s0
101331
+ + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1
101332
+ + "L" + r0 * c1 + "," + r0 * s1
101333
+ + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0
101334
+ + "Z";
101335
+ }
101336
+
101337
+ /**
101338
+ *
101339
+ * @param {number} r0 Inner radius
101340
+ * @param {number} r1 Outer Radius
101341
+ * @param {number} a0 Start of the inner arc (in rad)
101342
+ * @param {number} b0 End of the inner arc (in rad)
101343
+ * @param {number} a1 Start of the outer arc (in rad)
101344
+ * @param {number} b1 End of the outer arc (in rad)
101345
+ * @returns {string}
101346
+ */
101347
+ function svgArc2(r0, r1, a0, b0, a1, b1) {
101348
+ const ac0 = Math.cos(a0);
101349
+ const as0 = Math.sin(a0);
101350
+
101351
+ const bc0 = Math.cos(b0);
101352
+ const bs0 = Math.sin(b0);
101353
+
101354
+ const ac1 = Math.cos(a1);
101355
+ const as1 = Math.sin(a1);
101356
+
101357
+ const bc1 = Math.cos(b1);
101358
+ const bs1 = Math.sin(b1);
101359
+
101360
+ const largeArcFlag0 = b0 - a0 <= Math.PI ? "0" : "1";
101361
+ const largeArcFlag1 = b1 - a1 <= Math.PI ? "0" : "1";
101362
+
101363
+ return `
101364
+ M${r1 * ac1},${r1 * as1}
101365
+ A${r1},${r1} 0 ${largeArcFlag1},1 ${r1 * bc1},${r1 * bs1}
101366
+ L${r0 * bc0},${r0 * bs0}
101367
+ A${r0},${r0} 0 ${largeArcFlag0},0 ${r0 * ac0},${r0 * as0}
101368
+ Z
101369
+ `;
101370
+ }
101371
+
101372
+ /**
101373
+ *
101374
+ * @param {String} tag
101375
+ * @returns {Element}
101376
+ */
101377
+ function createSVGElement(tag) {
101378
+ return document.createElementNS(SVG_NAMESPACE, tag);
101253
101379
  }
101254
101380
 
101381
+ var SVG = {
101382
+ arcPath: svgArc,
101383
+ svgArc2,
101384
+ createElement: createSVGElement
101385
+ };
101386
+
101387
+ class CompassArrowView extends View {
101388
+ constructor() {
101389
+ super();
101390
+
101391
+
101392
+ const elSVG = SVG.createElement('svg');
101393
+
101394
+ const elDefs = SVG.createElement('defs');
101395
+ const elGradient = SVG.createElement('linearGradient');
101396
+ elGradient.setAttribute('id', 'gradient');
101397
+
101398
+ elDefs.appendChild(elGradient);
101399
+ const elStop0 = SVG.createElement('stop');
101400
+ const elStop1 = SVG.createElement('stop');
101401
+
101402
+ elStop0.setAttribute('offset', '50%');
101403
+ elStop1.setAttribute('offset', '100%');
101404
+
101405
+ elStop0.classList.add('color-stop-0');
101406
+ elStop1.classList.add('color-stop-1');
101407
+
101408
+ elGradient.appendChild(elStop0);
101409
+ elGradient.appendChild(elStop1);
101410
+ elSVG.appendChild(elDefs);
101411
+
101412
+ const h = 28;
101413
+ const w = 24;
101414
+
101415
+ elSVG.classList.add('compass');
101416
+
101417
+ elSVG.setAttribute('width', h);
101418
+ elSVG.setAttribute('height', w);
101419
+
101420
+ const elPolygon = SVG.createElement('polygon');
101421
+
101422
+ const half_w = w / 2;
101423
+ elPolygon.setAttribute('points', `${h},${half_w} 0,${w} 0,0`);
101424
+ elPolygon.classList.add('compass-triangle');
101425
+
101426
+ elSVG.appendChild(elPolygon);
101427
+
101428
+ //set rotation origin to the bottom middle of the element
101429
+ const t = half_w * Math.SQRT2;
101255
101430
 
101431
+ elSVG.style.transformOrigin = `${t}px ${half_w}px`;
101432
+ elSVG.style.top = `-${half_w}px`;
101433
+ elSVG.style.left = `-${t}px`;
101434
+
101435
+ this.el = elSVG;
101436
+ }
101437
+
101438
+ rotateFromDirectionVector(x, y) {
101439
+ const angle = Math.atan2(y, x);
101440
+
101441
+ this.rotation.set(angle);
101442
+ }
101443
+ }
101444
+
101256
101445
  /**
101257
101446
  *
101258
101447
  * @param {Element} el
101259
101448
  * @returns {AABB2}
101260
101449
  */
101261
- function element2aabb(el) {
101450
+ function html_element_to_aabb(el) {
101262
101451
  const clientRect = el.getBoundingClientRect();
101263
101452
 
101264
101453
  let x0 = clientRect.left;
@@ -101279,224 +101468,89 @@ function element2aabb(el) {
101279
101468
  // write bounds
101280
101469
 
101281
101470
  return new AABB2(x0, y0, x1, y1);
101282
- }
101283
-
101284
- /**
101285
- *
101286
- * @param {Rectangle} rect
101287
- * @returns {AABB2}
101288
- */
101289
- function rectangle2aabb(rect) {
101290
- const position = rect.position;
101291
-
101292
- const x0 = position.x;
101293
- const y0 = position.y;
101294
-
101295
- const size = rect.size;
101296
-
101297
- let x1 = x0;
101298
- let y1 = y0;
101299
-
101300
- // sanitize bounds
101301
- if (size.x > 0) {
101302
- x1 += size.x;
101303
- }
101304
-
101305
- if (size.y > 0) {
101306
- y1 += size.y;
101307
- }
101308
-
101309
- return new AABB2(x0, y0, x1, y1);
101310
- }
101311
-
101312
- /**
101313
- *
101314
- * @param {AABB2} box
101315
- * @param {AABB2} target
101316
- * @param {AABB2} bounds
101317
- * @returns {Vector2}
101318
- */
101319
- function computeInitialPlacement(box, target, bounds) {
101471
+ }
101472
+
101473
+ class TooltipView extends View {
101320
101474
  /**
101321
- * list of preferred directions, ordered. First direction is higher priority than the next, last is lowest priority
101322
- * @type {Vector2[]}
101475
+ *
101476
+ * @param {VisualTip} tip
101477
+ * @param {View} contentView
101478
+ * @constructor
101323
101479
  */
101324
- const preferredDirections = [
101325
- Vector2.up,
101326
- Vector2.right,
101327
- Vector2.down,
101328
- Vector2.left
101329
- ];
101330
-
101331
- const targetCenter = new Vector2(target.midX(), target.midY());
101332
-
101333
- const targetH = target.getHeight();
101334
- const targetW = target.getWidth();
101335
-
101336
- const targetDiagonal = Math.sqrt(targetH * targetH + targetW * targetW);
101480
+ constructor(tip, contentView) {
101481
+ super();
101337
101482
 
101338
- const boxWidth = box.getWidth();
101339
- const boxHeight = box.getHeight();
101340
101483
 
101341
- const boxWidth_2 = boxWidth / 2;
101342
- const boxHeight_2 = boxHeight / 2;
101484
+ /**
101485
+ *
101486
+ * @type {VisualTip}
101487
+ */
101488
+ this.model = tip;
101343
101489
 
101344
- const intersectionPoint = new Vector2();
101490
+ //set dom element
101491
+ this.el = document.createElement('div');
101492
+ this.addClass("tooltip-view");
101345
101493
 
101346
- const placements = preferredDirections.map(function (direction) {
101347
- //invert Y axis, screen-space direction is Down(+) and Up(-)
101348
- const viewportDirection = direction.clone()._multiply(1, -1);
101494
+ const vContents = new EmptyView({ classList: ['contents'] });
101349
101495
 
101350
- const pointOutside = viewportDirection.clone();
101351
- //scale vector to be larger than the box diagonal so it would intersect the boundary when placed in the middle
101352
- pointOutside.multiplyScalar(targetDiagonal + 1);
101353
- //move vector to the middle od the box
101354
- pointOutside.add(targetCenter);
101496
+ this.addChild(vContents);
101355
101497
 
101356
- target.lineIntersectionPoint(targetCenter, pointOutside, intersectionPoint);
101498
+ vContents.addChild(contentView);
101357
101499
 
101358
- //align placement to the opposite edge of the box
101359
- const offset = new Vector2(boxWidth_2, boxHeight_2);
101500
+ this.contentView = vContents;
101360
101501
 
101361
- offset.multiply(viewportDirection);
101362
101502
 
101363
- offset._sub(boxWidth_2, boxHeight_2);
101503
+ const vCompass = new CompassArrowView();
101364
101504
 
101365
- offset.add(intersectionPoint);
101505
+ this.addChild(vCompass);
101366
101506
 
101367
- return offset;
101368
- });
101507
+ this.compass = vCompass;
101508
+ }
101369
101509
 
101370
101510
  /**
101371
101511
  *
101372
- * @param {Vector2} placement
101373
- * @returns {number}
101512
+ * @param {AABB2} bounds
101374
101513
  */
101375
- function computePlacementScore(placement) {
101376
- //find space for the tip between bounds and the target
101377
- const availableSpace = new AABB2();
101378
-
101379
- const px0 = placement.x;
101380
- const px1 = px0 + boxWidth;
101381
- const py0 = placement.y;
101382
- const py1 = py0 + boxHeight;
101383
-
101384
- if (px1 <= target.x0) {
101385
- //left
101386
- availableSpace.set(
101387
- bounds.x0,
101388
- bounds.y0,
101389
- target.x0,
101390
- bounds.y1
101391
- );
101392
- } else if (px0 >= target.x1) {
101393
- //right
101394
- availableSpace.set(
101395
- target.x1,
101396
- bounds.y0,
101397
- bounds.x1,
101398
- bounds.y1
101399
- );
101400
- } else if (py1 <= target.y0) {
101401
- //up
101402
- availableSpace.set(
101403
- bounds.x0,
101404
- bounds.y0,
101405
- bounds.x1,
101406
- target.y0
101407
- );
101408
- } else if (py0 >= target.y1) {
101409
- //bottom
101410
- availableSpace.set(
101411
- bounds.x0,
101412
- target.y1,
101413
- bounds.x1,
101414
- bounds.y1
101415
- );
101416
- } else ;
101417
-
101418
- let score = 0;
101419
-
101420
- const availableWidth = availableSpace.getWidth();
101421
- const availableHeight = availableSpace.getHeight();
101422
-
101423
- const spareWidth = availableWidth - boxWidth;
101424
- const spareHeight = availableHeight - boxHeight;
101425
-
101426
- if (spareHeight < 0) {
101427
- score--;
101428
- }
101429
-
101430
- if (spareWidth < 0) {
101431
- score--;
101432
- }
101433
-
101434
- return score;
101435
- }
101436
-
101437
- //score placements
101438
- const scores = placements.map(computePlacementScore);
101514
+ layout(bounds) {
101515
+ const tipBox = html_element_to_aabb(this.contentView.el);
101439
101516
 
101440
- //find best placement
101441
- let bestIndex = 0;
101442
- let bestScore = scores[0];
101517
+ const SPACING = 16;
101443
101518
 
101444
- for (let i = 1; i < scores.length; i++) {
101445
- const score = scores[i];
101519
+ tipBox.grow(SPACING);
101446
101520
 
101447
- if (score > bestScore + EPSILON) {
101448
- bestScore = score;
101449
- bestIndex = i;
101450
- }
101451
- }
101521
+ /**
101522
+ *
101523
+ * @type {VisualTip}
101524
+ */
101525
+ const tip = this.model;
101452
101526
 
101453
- return placements[bestIndex];
101454
- }
101527
+ const tipTarget = tip.target;
101455
101528
 
101529
+ const target = rectangle_to_aabb(tipTarget);
101456
101530
 
101457
- /**
101458
- *
101459
- * @param {AABB2} box
101460
- * @param {AABB2} target
101461
- * @param {AABB2} bounds
101462
- */
101463
- function positionBoxNextToBox(box, target, bounds) {
101464
- const b = box.clone();
101465
- const t = target.clone();
101466
-
101467
- //center box inside the bounds
101468
- const y0 = (bounds.getHeight() + b.getHeight()) / 2;
101469
- const x0 = (bounds.getWidth() + b.getWidth()) / 2;
101470
- b.set(x0, y0, x0 + b.getWidth(), y0 + b.getHeight());
101531
+ const box = position_box_next_to_box(tipBox, target, bounds);
101471
101532
 
101533
+ box.shrink(SPACING);
101472
101534
 
101473
- new Vector2(t.midX(), t.midY());
101535
+ const compass = this.compass;
101474
101536
 
101475
- t.locked = true;
101537
+ function setCompass() {
101476
101538
 
101539
+ const targetCenter = target.getCenter();
101477
101540
 
101478
- const initialPosition = computeInitialPlacement(b, t, bounds);
101479
- //set bounds to initial position
101480
- b.set(initialPosition.x, initialPosition.y, initialPosition.x + b.getWidth(), initialPosition.y + b.getHeight());
101541
+ const tipPoint = new Vector2();
101481
101542
 
101482
- // touch();
101543
+ box.computeNearestPointToPoint(targetCenter, tipPoint);
101483
101544
 
101484
- function step() {
101485
- //pull
101486
- pullBoxTowardsPoint(b, initialPosition.x + b.getWidth() / 2, initialPosition.y + b.getHeight() / 2, 0.1);
101545
+ compass.rotateFromDirectionVector(targetCenter.x - tipPoint.x, targetCenter.y - tipPoint.y);
101487
101546
 
101488
- resolveAABB2Overlap([b, t], 10);
101547
+ compass.position.copy(tipPoint)._add(-box.x0, -box.y0);
101548
+ }
101489
101549
 
101490
- forceIntoBox(bounds, [b]);
101491
- }
101550
+ setCompass();
101492
101551
 
101493
- for (let i = 0; i < 5; i++) {
101494
- step();
101552
+ this.position.set(box.x0, box.y0);
101495
101553
  }
101496
-
101497
- box.copy(b);
101498
-
101499
- return box;
101500
101554
  }
101501
101555
 
101502
101556
  class TooltipManager {
@@ -102824,40 +102878,37 @@ function playTrackRealTime(track, ecd) {
102824
102878
  }
102825
102879
 
102826
102880
  /**
102827
- *
102828
- * @param {Number} t
102829
- * @param {Number} p0
102830
- * @param {Number} p1
102831
- * @param {Number} p2
102832
- * @param {Number} p3
102833
- * @return {Number}
102881
+ * 3-rd (cubic) degree bezier curve
102882
+ * @see https://www.youtube.com/watch?v=jvPPXbo87ds&t=234s
102883
+ * @see https://en.wikipedia.org/wiki/B%C3%A9zier_curve
102884
+ * @param {number} t factor value between 0..1
102885
+ * @param {number} p0 start point
102886
+ * @param {number} p1 control point
102887
+ * @param {number} p2 control point
102888
+ * @param {number} p3 end point
102889
+ * @returns {number}
102834
102890
  */
102835
- function cubicCurve(t, p0, p1, p2, p3) {
102836
- /**
102837
- *
102838
- * @type {number}
102839
- */
102840
- const cX = 3 * (p1 - p0);
102891
+ function spline_bezier3(t, p0, p1, p2, p3) {
102892
+ // first we compute necessary factors for each point
102893
+ const nt = 1 - t;
102841
102894
 
102842
- /**
102843
- *
102844
- * @type {number}
102845
- */
102846
- const bX = 3 * (p2 - p1) - cX;
102895
+ const nt_2 = nt * nt;
102847
102896
 
102848
- /**
102849
- *
102850
- * @type {number}
102851
- */
102852
- const aX = p3 - p0 - cX - bX;
102897
+ const nt_3 = nt_2 * nt;
102853
102898
 
102854
102899
  const t_2 = t * t;
102855
102900
 
102856
102901
  const t_3 = t_2 * t;
102857
102902
 
102858
- return (aX * t_3) + (bX * t_2) + (cX * t) + p0;
102903
+ // combine factors with point values to produce final result
102904
+ return nt_3 * p0 + 3 * nt_2 * t * p1 + 3 * nt * t_2 * p2 + t_3 * p3;
102859
102905
  }
102860
102906
 
102907
+ /**
102908
+ * @deprecated use {@link spline_bezier3} directly
102909
+ */
102910
+ const cubicCurve = spline_bezier3;
102911
+
102861
102912
  /**
102862
102913
  *
102863
102914
  * @param {Number} p0
@@ -107373,28 +107424,18 @@ class ReferencedTextureAtlas extends AbstractTextureAtlas {
107373
107424
  * @extends {AABB2}
107374
107425
  */
107375
107426
  class QuadTreeDatum extends AABB2 {
107427
+
107376
107428
  /**
107377
107429
  *
107378
- * @param {Number} [x0=0]
107379
- * @param {Number} [y0=0]
107380
- * @param {Number} [x1=0]
107381
- * @param {Number} [y1=0]
107430
+ * @type {D|null}
107382
107431
  */
107383
- constructor(x0, y0, x1, y1) {
107384
- super(x0, y0, x1, y1);
107385
-
107386
- /**
107387
- *
107388
- * @type {D|null}
107389
- */
107390
- this.data = null;
107432
+ data = null;
107391
107433
 
107392
- /**
107393
- *
107394
- * @type {QuadTreeNode|null}
107395
- */
107396
- this.parentNode = null;
107397
- }
107434
+ /**
107435
+ *
107436
+ * @type {QuadTreeNode|null}
107437
+ */
107438
+ parentNode = null;
107398
107439
 
107399
107440
  disconnect() {
107400
107441
  const parentNode = this.parentNode;