@codexo/exojs 0.6.11 → 0.6.12

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.
package/dist/exo.esm.js CHANGED
@@ -16882,6 +16882,257 @@ class PolarVector {
16882
16882
  }
16883
16883
  }
16884
16884
 
16885
+ // ---------------------------------------------------------------------------
16886
+ // sweepRectangle — AABB vs AABB slab method
16887
+ // ---------------------------------------------------------------------------
16888
+ /**
16889
+ * Swept axis-aligned box vs. axis-aligned box.
16890
+ *
16891
+ * Uses the separating-axis slab method: for each axis we compute the entry
16892
+ * and exit times of the moving box's slab vs the static box's slab, then
16893
+ * combine. `t` is the fraction of the requested move at which first contact
16894
+ * occurs (0 = already overlapping at start, 1 = just barely reaches).
16895
+ *
16896
+ * Already-overlapping case (tEntry < 0 overall): returns `t = 0` with the
16897
+ * normal of the deepest-penetration axis, allowing callers to handle the
16898
+ * "I'm already inside" situation without a separate discrete test.
16899
+ */
16900
+ function sweepRectangle(moving, deltaX, deltaY, target) {
16901
+ const movMinX = moving.x;
16902
+ const movMaxX = moving.x + moving.width;
16903
+ const movMinY = moving.y;
16904
+ const movMaxY = moving.y + moving.height;
16905
+ const tarMinX = target.x;
16906
+ const tarMaxX = target.x + target.width;
16907
+ const tarMinY = target.y;
16908
+ const tarMaxY = target.y + target.height;
16909
+ // X axis
16910
+ let tEntryX = -Infinity;
16911
+ let tExitX = Infinity;
16912
+ if (deltaX > 0) {
16913
+ tEntryX = (tarMinX - movMaxX) / deltaX;
16914
+ tExitX = (tarMaxX - movMinX) / deltaX;
16915
+ }
16916
+ else if (deltaX < 0) {
16917
+ tEntryX = (tarMaxX - movMinX) / deltaX;
16918
+ tExitX = (tarMinX - movMaxX) / deltaX;
16919
+ }
16920
+ else if (movMaxX <= tarMinX || movMinX >= tarMaxX) {
16921
+ // No movement on X and no static overlap — can never collide
16922
+ return null;
16923
+ }
16924
+ // Y axis
16925
+ let tEntryY = -Infinity;
16926
+ let tExitY = Infinity;
16927
+ if (deltaY > 0) {
16928
+ tEntryY = (tarMinY - movMaxY) / deltaY;
16929
+ tExitY = (tarMaxY - movMinY) / deltaY;
16930
+ }
16931
+ else if (deltaY < 0) {
16932
+ tEntryY = (tarMaxY - movMinY) / deltaY;
16933
+ tExitY = (tarMinY - movMaxY) / deltaY;
16934
+ }
16935
+ else if (movMaxY <= tarMinY || movMinY >= tarMaxY) {
16936
+ // No movement on Y and no static overlap — can never collide
16937
+ return null;
16938
+ }
16939
+ const tEntry = Math.max(tEntryX, tEntryY);
16940
+ const tExit = Math.min(tExitX, tExitY);
16941
+ // No overlap window
16942
+ if (tEntry > tExit || tExit < 0 || tEntry > 1) {
16943
+ return null;
16944
+ }
16945
+ const t = Math.max(0, tEntry);
16946
+ const hitX = moving.x + deltaX * t;
16947
+ const hitY = moving.y + deltaY * t;
16948
+ // Normal is on the axis whose slab entry was latest.
16949
+ // Already-overlapping: use the deepest-penetration axis normal.
16950
+ let normalX = 0;
16951
+ let normalY = 0;
16952
+ if (tEntry <= 0) {
16953
+ // Already overlapping — pick the axis with least penetration
16954
+ const overlapX = Math.min(movMaxX - tarMinX, tarMaxX - movMinX);
16955
+ const overlapY = Math.min(movMaxY - tarMinY, tarMaxY - movMinY);
16956
+ if (overlapX < overlapY) {
16957
+ normalX = movMinX < tarMinX ? -1 : 1;
16958
+ }
16959
+ else {
16960
+ normalY = movMinY < tarMinY ? -1 : 1;
16961
+ }
16962
+ }
16963
+ else if (tEntryX > tEntryY) {
16964
+ // X axis had the latest entry
16965
+ normalX = deltaX > 0 ? -1 : 1;
16966
+ }
16967
+ else {
16968
+ // Y axis had the latest entry
16969
+ normalY = deltaY > 0 ? -1 : 1;
16970
+ }
16971
+ return { t, x: hitX, y: hitY, normalX, normalY };
16972
+ }
16973
+ // ---------------------------------------------------------------------------
16974
+ // sweepCircleVsRectangle — expanded-AABB simple fallback (V1)
16975
+ // ---------------------------------------------------------------------------
16976
+ /**
16977
+ * Swept circle vs. axis-aligned box.
16978
+ *
16979
+ * **V1 implementation** uses the simple Minkowski expansion fallback:
16980
+ * the target rectangle is expanded by `circle.radius` on all sides, then
16981
+ * `sweepRectangle` is run treating the circle centre as a zero-sized moving
16982
+ * box. This over-collides at rectangle corners (the circle collides with the
16983
+ * expanded-rect's flat face when geometrically it should curve around the
16984
+ * corner), producing slightly early hits in corner-quadrant trajectories —
16985
+ * a known and acceptable accuracy trade-off for V1.
16986
+ *
16987
+ * TODO (V2): Replace with the full Minkowski rounded-rectangle formulation
16988
+ * that handles the four corner quadrants with per-corner circle-vs-circle
16989
+ * sub-tests.
16990
+ */
16991
+ function sweepCircleVsRectangle(moving, deltaX, deltaY, target) {
16992
+ const r = moving.radius;
16993
+ // Expanded target: grow each side by the circle radius
16994
+ const expanded = new Rectangle(target.x - r, target.y - r, target.width + r * 2, target.height + r * 2);
16995
+ // Treat the circle centre as a zero-sized moving box
16996
+ const centreBox = new Rectangle(moving.x, moving.y, 0, 0);
16997
+ return sweepRectangle(centreBox, deltaX, deltaY, expanded);
16998
+ }
16999
+ // ---------------------------------------------------------------------------
17000
+ // sweepCircleVsCircle — quadratic equation
17001
+ // ---------------------------------------------------------------------------
17002
+ /**
17003
+ * Swept circle vs. stationary circle.
17004
+ *
17005
+ * Solves `|(moving.centre + delta*t) − target.centre|² = (r1+r2)²` for t,
17006
+ * yielding a quadratic. Returns the smaller root if it is in [0, 1].
17007
+ *
17008
+ * Already-overlapping case: returns `{ t: 0 }` with the normal pointing from
17009
+ * target → moving (or an arbitrary normal if both centres coincide).
17010
+ */
17011
+ function sweepCircleVsCircle(moving, deltaX, deltaY, target) {
17012
+ const dx = moving.x - target.x;
17013
+ const dy = moving.y - target.y;
17014
+ const r = moving.radius + target.radius;
17015
+ const a = deltaX * deltaX + deltaY * deltaY;
17016
+ const b = 2 * (dx * deltaX + dy * deltaY);
17017
+ const c = dx * dx + dy * dy - r * r;
17018
+ // Already overlapping at start
17019
+ if (c <= 0) {
17020
+ const dist = Math.sqrt(dx * dx + dy * dy);
17021
+ const normalX = dist > 0 ? dx / dist : 1;
17022
+ const normalY = dist > 0 ? dy / dist : 0;
17023
+ return { t: 0, x: moving.x, y: moving.y, normalX, normalY };
17024
+ }
17025
+ // No movement
17026
+ if (a === 0) {
17027
+ return null;
17028
+ }
17029
+ const disc = b * b - 4 * a * c;
17030
+ if (disc < 0) {
17031
+ return null;
17032
+ }
17033
+ const t = (-b - Math.sqrt(disc)) / (2 * a);
17034
+ if (t < 0 || t > 1) {
17035
+ return null;
17036
+ }
17037
+ const hitX = moving.x + deltaX * t;
17038
+ const hitY = moving.y + deltaY * t;
17039
+ // Normal points from target centre → hit circle centre
17040
+ const normalX = (hitX - target.x) / r;
17041
+ const normalY = (hitY - target.y) / r;
17042
+ return { t, x: hitX, y: hitY, normalX, normalY };
17043
+ }
17044
+ // ---------------------------------------------------------------------------
17045
+ // Batch helpers — sweep a shape against multiple targets
17046
+ // ---------------------------------------------------------------------------
17047
+ /**
17048
+ * Returns the earliest `SweptHit` against an array of rectangle targets, or
17049
+ * `null` if none are hit.
17050
+ *
17051
+ * Optimisation: before testing each target individually the swept AABB of the
17052
+ * moving rectangle is computed once; targets whose AABB does not overlap the
17053
+ * swept AABB are skipped.
17054
+ */
17055
+ function sweepRectangleAgainst(moving, deltaX, deltaY, targets) {
17056
+ if (targets.length === 0) {
17057
+ return null;
17058
+ }
17059
+ // Swept AABB of the moving rectangle (broad-phase skip)
17060
+ const sweptMinX = Math.min(moving.x, moving.x + deltaX);
17061
+ const sweptMaxX = Math.max(moving.x + moving.width, moving.x + moving.width + deltaX);
17062
+ const sweptMinY = Math.min(moving.y, moving.y + deltaY);
17063
+ const sweptMaxY = Math.max(moving.y + moving.height, moving.y + moving.height + deltaY);
17064
+ let earliest = null;
17065
+ for (const target of targets) {
17066
+ // Broad-phase: skip if swept AABB doesn't overlap target AABB
17067
+ if (sweptMaxX <= target.x
17068
+ || sweptMinX >= target.x + target.width
17069
+ || sweptMaxY <= target.y
17070
+ || sweptMinY >= target.y + target.height) {
17071
+ continue;
17072
+ }
17073
+ const hit = sweepRectangle(moving, deltaX, deltaY, target);
17074
+ if (hit !== null && (earliest === null || hit.t < earliest.t)) {
17075
+ earliest = hit;
17076
+ }
17077
+ }
17078
+ return earliest;
17079
+ }
17080
+ /**
17081
+ * Returns the earliest `SweptHit` against an array of circle targets, or
17082
+ * `null` if none are hit.
17083
+ *
17084
+ * Optimisation: the swept AABB of the moving circle is computed once and used
17085
+ * to skip targets that cannot possibly be reached.
17086
+ */
17087
+ function sweepCircleAgainst(moving, deltaX, deltaY, targets) {
17088
+ if (targets.length === 0) {
17089
+ return null;
17090
+ }
17091
+ // Swept AABB of the moving circle
17092
+ const sweptMinX = Math.min(moving.x, moving.x + deltaX) - moving.radius;
17093
+ const sweptMaxX = Math.max(moving.x, moving.x + deltaX) + moving.radius;
17094
+ const sweptMinY = Math.min(moving.y, moving.y + deltaY) - moving.radius;
17095
+ const sweptMaxY = Math.max(moving.y, moving.y + deltaY) + moving.radius;
17096
+ let earliest = null;
17097
+ for (const target of targets) {
17098
+ // Broad-phase: skip if swept AABB doesn't overlap target's AABB
17099
+ if (sweptMaxX <= target.x - target.radius
17100
+ || sweptMinX >= target.x + target.radius
17101
+ || sweptMaxY <= target.y - target.radius
17102
+ || sweptMinY >= target.y + target.radius) {
17103
+ continue;
17104
+ }
17105
+ const hit = sweepCircleVsCircle(moving, deltaX, deltaY, target);
17106
+ if (hit !== null && (earliest === null || hit.t < earliest.t)) {
17107
+ earliest = hit;
17108
+ }
17109
+ }
17110
+ return earliest;
17111
+ }
17112
+ // ---------------------------------------------------------------------------
17113
+ // substepSweep — generic fallback iterator
17114
+ // ---------------------------------------------------------------------------
17115
+ /**
17116
+ * Generator that yields evenly-spaced position snapshots along a movement
17117
+ * vector so the caller can run their own discrete intersection check at each
17118
+ * step. Useful for arbitrary shape pairs that lack a closed-form swept test.
17119
+ *
17120
+ * `maxStepSize` controls the step granularity — smaller values produce more
17121
+ * accurate detection but more iterations. Use the smallest dimension of the
17122
+ * smaller collider as a sensible default.
17123
+ *
17124
+ * Always yields at least 2 snapshots (t=0 and t=1), even for zero-length
17125
+ * deltas.
17126
+ */
17127
+ function* substepSweep(fromX, fromY, deltaX, deltaY, maxStepSize) {
17128
+ const length = Math.hypot(deltaX, deltaY);
17129
+ const stepCount = Math.max(1, Math.ceil(length / maxStepSize));
17130
+ for (let i = 0; i <= stepCount; i++) {
17131
+ const t = i / stepCount;
17132
+ yield { x: fromX + deltaX * t, y: fromY + deltaY * t, t };
17133
+ }
17134
+ }
17135
+
16885
17136
  class ColorAffector {
16886
17137
  _fromColor;
16887
17138
  _toColor;
@@ -18737,5 +18988,5 @@ class IndexedDbStore {
18737
18988
  }
18738
18989
  }
18739
18990
 
18740
- export { AbstractAssetFactory, AbstractMedia, AbstractWebGl2BatchedRenderer, AbstractWebGl2Renderer, AbstractWebGpuRenderer, AnimatedSprite, Application, ApplicationStatus, ArcadeStickGamepadMapping, AudioAnalyser, BinaryFactory, BlendModes, BlurFilter, Bounds, BufferTypes, BufferUsage, BundleLoadError, CacheFirstStrategy, CallbackRenderPass, Capabilities, ChannelOffset, ChannelSize, Circle, Clock, CollisionType, Color, ColorAffector, ColorFilter, Container, Drawable, DynamicGlyphAtlas, Ease, Ellipse, FactoryRegistry, Filter, Flags, FontFactory, ForceAffector, GameCubeGamepadMapping, Gamepad, GamepadChannel, GamepadControl, GamepadMapping, GamepadMappingFamily, GamepadPromptLayouts, GenericDualAnalogGamepadMapping, Graphics, ImageFactory, IndexedDbDatabase, IndexedDbStore, Input, InputManager, Interval, JoyConLeftGamepadMapping, JoyConRightGamepadMapping, Json, JsonFactory, Keyboard, Line, Loader, Matrix, Mesh, Music, MusicFactory, NetworkOnlyStrategy, ObservableSize, ObservableVector, Particle, ParticleOptions, ParticleSystem, PlayStationGamepadMapping, Pointer, PointerState, PointerStateFlag, PolarVector, Polygon, Quadtree, Random, Rectangle, RenderBackendType, RenderNode, RenderTarget, RenderTargetPass, RenderTexture, RendererRegistry, RenderingPrimitives, Sampler, ScaleAffector, ScaleModes, Scene, SceneManager, SceneNode, Segment, Shader, ShaderAttribute, ShaderPrimitives, ShaderUniform, Signal, Size, Sound, SoundFactory, Sprite, SpriteFlags, Spritesheet, SteamControllerGamepadMapping, SvgAsset, SvgFactory, SwitchProGamepadMapping, Text, TextAsset, TextFactory, TextStyle, Texture, TextureFactory, Time, Timer, TorqueAffector, Tween, TweenManager, TweenState, UniversalEmitter, Vector, Video, VideoFactory, View, ViewFlags, VoronoiRegion, VttAsset, VttFactory, WasmFactory, WebGl2Backend, WebGl2MeshRenderer, WebGl2ParticleRenderer, WebGl2RenderBuffer, WebGl2ShaderBlock, WebGl2SpriteRenderer, WebGl2VertexArrayObject, WebGpuBackend, WebGpuMeshRenderer, WebGpuParticleRenderer, WebGpuSpriteRenderer, WrapModes, XboxGamepadMapping, bezierCurveTo, buildCircle, buildEllipse, buildLine, buildPath, buildPolygon, buildRectangle, buildStar, builtInGamepadDefinitions, canvasSourceToDataUrl, clamp, createRenderStats, createWebGl2ShaderProgram, decodeAudioData, defineAssetManifest, degreesPerRadian, degreesToRadians, determineMimeType, emptyArrayBuffer, getAudioContext, getCanvasSourceSize, getCollisionCircleCircle, getCollisionCircleRectangle, getCollisionPolygonCircle, getCollisionRectangleRectangle, getCollisionSat, getDistance, getOfflineAudioContext, getPreciseTime, getTextureSourceSize, getVoronoiRegion$1 as getVoronoiRegion, getWebGpuBlendState, hours, inRange, intersectionCircleCircle, intersectionCircleEllipse, intersectionCirclePoly, intersectionEllipseEllipse, intersectionEllipsePoly, intersectionLineCircle, intersectionLineEllipse, intersectionLineLine, intersectionLinePoly, intersectionLineRect, intersectionPointCircle, intersectionPointEllipse, intersectionPointLine, intersectionPointPoint, intersectionPointPoly, intersectionPointRect, intersectionPolyPoly, intersectionRectCircle, intersectionRectEllipse, intersectionRectPoly, intersectionRectRect, intersectionSat, isAudioContextReady, isPowerOfTwo, layoutText, lerp, matchesIds, maxPointers, milliseconds, minutes, noop$1 as noop, normalizeIds, onAudioContextReady, parseGamepadDescriptor, pointerSlotSize, quadraticCurveTo, radiansPerDegree, radiansToDegrees, rand, removeArrayItems, resetRenderStats, resolveDefinition, resolveGamepadDefinition, seconds, sign, stopEvent, supportsCodec, supportsEventOptions, supportsIndexedDb, supportsPointerEvents, supportsTouchEvents, supportsWebAudio, tau, trimRotation, webGl2PrimitiveArrayConstructors, webGl2PrimitiveByteSizeMapping, webGl2PrimitiveTypeNames };
18991
+ export { AbstractAssetFactory, AbstractMedia, AbstractWebGl2BatchedRenderer, AbstractWebGl2Renderer, AbstractWebGpuRenderer, AnimatedSprite, Application, ApplicationStatus, ArcadeStickGamepadMapping, AudioAnalyser, BinaryFactory, BlendModes, BlurFilter, Bounds, BufferTypes, BufferUsage, BundleLoadError, CacheFirstStrategy, CallbackRenderPass, Capabilities, ChannelOffset, ChannelSize, Circle, Clock, CollisionType, Color, ColorAffector, ColorFilter, Container, Drawable, DynamicGlyphAtlas, Ease, Ellipse, FactoryRegistry, Filter, Flags, FontFactory, ForceAffector, GameCubeGamepadMapping, Gamepad, GamepadChannel, GamepadControl, GamepadMapping, GamepadMappingFamily, GamepadPromptLayouts, GenericDualAnalogGamepadMapping, Graphics, ImageFactory, IndexedDbDatabase, IndexedDbStore, Input, InputManager, Interval, JoyConLeftGamepadMapping, JoyConRightGamepadMapping, Json, JsonFactory, Keyboard, Line, Loader, Matrix, Mesh, Music, MusicFactory, NetworkOnlyStrategy, ObservableSize, ObservableVector, Particle, ParticleOptions, ParticleSystem, PlayStationGamepadMapping, Pointer, PointerState, PointerStateFlag, PolarVector, Polygon, Quadtree, Random, Rectangle, RenderBackendType, RenderNode, RenderTarget, RenderTargetPass, RenderTexture, RendererRegistry, RenderingPrimitives, Sampler, ScaleAffector, ScaleModes, Scene, SceneManager, SceneNode, Segment, Shader, ShaderAttribute, ShaderPrimitives, ShaderUniform, Signal, Size, Sound, SoundFactory, Sprite, SpriteFlags, Spritesheet, SteamControllerGamepadMapping, SvgAsset, SvgFactory, SwitchProGamepadMapping, Text, TextAsset, TextFactory, TextStyle, Texture, TextureFactory, Time, Timer, TorqueAffector, Tween, TweenManager, TweenState, UniversalEmitter, Vector, Video, VideoFactory, View, ViewFlags, VoronoiRegion, VttAsset, VttFactory, WasmFactory, WebGl2Backend, WebGl2MeshRenderer, WebGl2ParticleRenderer, WebGl2RenderBuffer, WebGl2ShaderBlock, WebGl2SpriteRenderer, WebGl2VertexArrayObject, WebGpuBackend, WebGpuMeshRenderer, WebGpuParticleRenderer, WebGpuSpriteRenderer, WrapModes, XboxGamepadMapping, bezierCurveTo, buildCircle, buildEllipse, buildLine, buildPath, buildPolygon, buildRectangle, buildStar, builtInGamepadDefinitions, canvasSourceToDataUrl, clamp, createRenderStats, createWebGl2ShaderProgram, decodeAudioData, defineAssetManifest, degreesPerRadian, degreesToRadians, determineMimeType, emptyArrayBuffer, getAudioContext, getCanvasSourceSize, getCollisionCircleCircle, getCollisionCircleRectangle, getCollisionPolygonCircle, getCollisionRectangleRectangle, getCollisionSat, getDistance, getOfflineAudioContext, getPreciseTime, getTextureSourceSize, getVoronoiRegion$1 as getVoronoiRegion, getWebGpuBlendState, hours, inRange, intersectionCircleCircle, intersectionCircleEllipse, intersectionCirclePoly, intersectionEllipseEllipse, intersectionEllipsePoly, intersectionLineCircle, intersectionLineEllipse, intersectionLineLine, intersectionLinePoly, intersectionLineRect, intersectionPointCircle, intersectionPointEllipse, intersectionPointLine, intersectionPointPoint, intersectionPointPoly, intersectionPointRect, intersectionPolyPoly, intersectionRectCircle, intersectionRectEllipse, intersectionRectPoly, intersectionRectRect, intersectionSat, isAudioContextReady, isPowerOfTwo, layoutText, lerp, matchesIds, maxPointers, milliseconds, minutes, noop$1 as noop, normalizeIds, onAudioContextReady, parseGamepadDescriptor, pointerSlotSize, quadraticCurveTo, radiansPerDegree, radiansToDegrees, rand, removeArrayItems, resetRenderStats, resolveDefinition, resolveGamepadDefinition, seconds, sign, stopEvent, substepSweep, supportsCodec, supportsEventOptions, supportsIndexedDb, supportsPointerEvents, supportsTouchEvents, supportsWebAudio, sweepCircleAgainst, sweepCircleVsCircle, sweepCircleVsRectangle, sweepRectangle, sweepRectangleAgainst, tau, trimRotation, webGl2PrimitiveArrayConstructors, webGl2PrimitiveByteSizeMapping, webGl2PrimitiveTypeNames };
18741
18992
  //# sourceMappingURL=exo.esm.js.map