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