@woosh/meep-engine 2.118.5 → 2.118.7

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