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