infernojs 0.0.1 → 0.0.2

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/README.md CHANGED
@@ -1,25 +1,18 @@
1
- # pepperjs
1
+ # infernojs
2
2
 
3
- A spicy, high-performance library for building 2D games in JavaScript.
4
-
5
- ![Pepper - The Black Cat Mascot](./assets/pepper-logo.svg)
3
+ A simple library for building 2D games in JavaScript.
6
4
 
7
5
  ## Features
8
6
 
9
- - **Efficient Collision Detection**
7
+ - **Collision Detection**
10
8
 
11
9
  - Multiple collision detection strategies for different use cases
12
10
  - Support for circles, rectangles, polygons, and line segments
13
11
  - Spatial partitioning for optimal performance with large numbers of entities
14
12
  - Precise contact point and collision normal information
15
13
 
16
- - **Future Additions** (Coming Soon)
17
-
18
- - Efficient shape rendering
19
- - Input handling
20
- - Animation system
21
- - Simple physics
22
- - Game loop management
14
+ - **Camera System**
15
+ - Convert global coordinates to camera coordinates
23
16
 
24
17
  - **Developer-Friendly**
25
18
  - Written in TypeScript with full type safety
@@ -29,7 +22,7 @@ A spicy, high-performance library for building 2D games in JavaScript.
29
22
  ## Installation
30
23
 
31
24
  ```bash
32
- npm install pepperjs
25
+ npm install infernojs
33
26
  ```
34
27
 
35
28
  ## Usage: Collision Detection
@@ -37,7 +30,7 @@ npm install pepperjs
37
30
  ### Using Collision Detectors
38
31
 
39
32
  ```typescript
40
- import { BruteForceCollisionDetector, SpatialGridCollisionDetector } from "pepperjs";
33
+ import { BruteForceCollisionDetector, SpatialGridCollisionDetector } from "infernojs";
41
34
 
42
35
  // Create some entities
43
36
  const entities = [
@@ -96,7 +89,7 @@ const physics = new CollisionSystem({
96
89
  const circleId = physics.addEntity({
97
90
  type: "circle",
98
91
  shape: {
99
- center: vec2(100, 100),
92
+ center: { x: 100, y: 100 },
100
93
  radius: 50,
101
94
  },
102
95
  });
@@ -105,7 +98,7 @@ const circleId = physics.addEntity({
105
98
  const rectId = physics.addEntity({
106
99
  type: "rectangle",
107
100
  shape: {
108
- position: vec2(120, 90),
101
+ position: { x: 120, y: 90 },
109
102
  width: 80,
110
103
  height: 60,
111
104
  },
@@ -139,7 +132,7 @@ The library uses several techniques to ensure high performance:
139
132
 
140
133
  ## Choosing the Right Collision Detector
141
134
 
142
- PepperJS provides different collision detection strategies optimized for different scenarios:
135
+ InfernoJS provides different collision detection strategies optimized for different scenarios:
143
136
 
144
137
  - **BruteForceCollisionDetector**: Checks every entity against the given shape
145
138
  - Best for small number of entities (< 100)
@@ -152,47 +145,12 @@ PepperJS provides different collision detection strategies optimized for differe
152
145
  - Best when you need to check collisions frequently
153
146
  - Allows tuning the cell size for optimal performance
154
147
 
155
- ## Roadmap
156
-
157
- We're actively working on expanding pepperjs into a comprehensive 2D game development library. Here's what's coming:
158
-
159
148
  ### Version 0.1.0 (Current)
160
149
 
161
150
  - ✅ High-performance collision detection
162
151
  - ✅ Spatial partitioning for efficient collision handling
163
152
  - ✅ Support for multiple shape types (circles, rectangles, polygons, line segments)
164
153
 
165
- ### Version 0.2.0 (Planned)
166
-
167
- - 🔲 Canvas-based rendering system
168
- - 🔲 Shape drawing utilities
169
- - 🔲 Basic sprite support
170
- - 🔲 Performance optimizations for rendering
171
-
172
- ### Version 0.3.0 (Planned)
173
-
174
- - 🔲 Input handling (keyboard, mouse, touch)
175
- - 🔲 Game loop management
176
- - 🔲 Time-based animation system
177
-
178
- ### Version 0.4.0 (Planned)
179
-
180
- - 🔲 Simple physics engine (forces, gravity, etc.)
181
- - 🔲 Constraints and joints
182
- - 🔲 Integration with collision system
183
-
184
- ### Version 0.5.0 (Planned)
185
-
186
- - 🔲 Entity-component system
187
- - 🔲 Particle systems
188
- - 🔲 Camera and viewport management
189
-
190
- ### Future Additions
191
-
192
- - 🔲 WebGL renderer option
193
- - 🔲 Sound system
194
- - 🔲 Tile maps and level loading
195
- - 🔲 UI components
196
154
 
197
155
  ## Examples
198
156
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { Entity, Shape, Circle, Point, Rectangle } from './types.mjs';
2
- export { AABB, CollisionConfig, CollisionResult, ContactPoint, ShapeType, Vector2D } from './types.mjs';
1
+ import { Entity, Shape, Vector2D, Point, Circle, Rectangle, AABB } from './types.mjs';
2
+ export { CollisionConfig, CollisionResult, ContactPoint, ShapeType } from './types.mjs';
3
3
  export { aabbIntersect, add, cellToId, circleToAABB, cross, distance, distanceSquared, dot, length, lengthSquared, normalize, positionToCell, rectToAABB, scale, subtract, vec2 } from './utils.mjs';
4
4
 
5
5
  /**
@@ -18,9 +18,9 @@ declare abstract class BaseCollisionDetector {
18
18
  /**
19
19
  * Get all entities that collide with the given shape
20
20
  * @param shape The shape to check collisions against
21
- * @returns Array of entity IDs that collide with the shape
21
+ * @returns Array of entities that collide with the shape
22
22
  */
23
- abstract getCollisions(shape: Shape): (string | number)[];
23
+ abstract getCollisions(shape: Shape): Entity[];
24
24
  }
25
25
 
26
26
  /**
@@ -39,9 +39,9 @@ declare class BruteForceCollisionDetectorImpl extends BaseCollisionDetector {
39
39
  /**
40
40
  * Get all entities that collide with the given shape
41
41
  * @param shape The shape to check collisions against
42
- * @returns Array of entity IDs that collide with the shape
42
+ * @returns Array of entities that collide with the shape
43
43
  */
44
- getCollisions(shape: Shape): (string | number)[];
44
+ getCollisions(shape: Shape): Entity[];
45
45
  }
46
46
 
47
47
  /**
@@ -75,7 +75,7 @@ declare class SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {
75
75
  /**
76
76
  * Get all entities that collide with the given shape
77
77
  */
78
- getCollisions(shape: Shape): (string | number)[];
78
+ getCollisions(shape: Shape): Entity[];
79
79
  /**
80
80
  * Rebuild the spatial grid with the current entities
81
81
  */
@@ -107,90 +107,35 @@ declare class SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {
107
107
  declare function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean;
108
108
 
109
109
  /**
110
- * Style options for rendering shapes
111
- */
112
- type ShapeStyle = {
113
- /** Fill color (CSS color string) */
114
- fillColor?: string;
115
- /** Stroke color (CSS color string) */
116
- strokeColor?: string;
117
- /** Stroke width in pixels */
118
- strokeWidth?: number;
119
- /** Alpha/opacity (0-1) */
120
- alpha?: number;
121
- /** Shadow blur radius */
122
- shadowBlur?: number;
123
- /** Shadow color */
124
- shadowColor?: string;
125
- /** Shadow offset X */
126
- shadowOffsetX?: number;
127
- /** Shadow offset Y */
128
- shadowOffsetY?: number;
129
- };
130
- /**
131
- * A renderable entity with shape and style
132
- */
133
- type RenderableShape = {
134
- shape: Shape;
135
- style: ShapeStyle;
136
- };
137
- /**
138
- * Configuration options for the renderer
139
- */
140
- type RendererOptions = {
141
- /** Background color of the canvas */
142
- backgroundColor?: string;
143
- /** Default style to apply to shapes without specific styling */
144
- defaultStyle?: ShapeStyle;
145
- /** Whether to clear the canvas before each render */
146
- clearBeforeRender?: boolean;
110
+ * Configuration for the camera
111
+ */
112
+ type CameraConfig = {
113
+ /** Center position of the camera in world coordinates */
114
+ position: Vector2D;
115
+ /** Width of the viewport/canvas in pixels */
116
+ width: number;
117
+ /** Height of the viewport/canvas in pixels */
118
+ height: number;
119
+ /** Rotation in degrees (optional, defaults to 0) */
120
+ rotation?: number;
147
121
  };
148
-
149
- /**
150
- * Apply style settings to the canvas context
151
- */
152
- declare function applyStyle(ctx: CanvasRenderingContext2D, style: ShapeStyle): void;
153
- /**
154
- * Restore the canvas context to its previous state
155
- */
156
- declare function restoreContext(ctx: CanvasRenderingContext2D): void;
157
- /**
158
- * Render a circle shape
159
- */
160
- declare function renderCircle(ctx: CanvasRenderingContext2D, circle: Circle, style: ShapeStyle): void;
161
- /**
162
- * Render a point shape
163
- */
164
- declare function renderPoint(ctx: CanvasRenderingContext2D, point: Point, style: ShapeStyle): void;
165
- /**
166
- * Render a rectangle shape
167
- */
168
- declare function renderRectangle(ctx: CanvasRenderingContext2D, rectangle: Rectangle, style: ShapeStyle): void;
169
- /**
170
- * Render any shape based on its type
171
- */
172
- declare function renderShape(ctx: CanvasRenderingContext2D, shape: Shape, style: ShapeStyle): void;
173
122
  /**
174
- * Main renderer function to render multiple shapes
123
+ * Converts a global coordinate object into a local camera coordinate system.
175
124
  */
176
- declare function renderShapes(ctx: CanvasRenderingContext2D, shapes: RenderableShape[], options: RendererOptions): void;
125
+ declare function globalCoordinatesToCameraCoordinates(global: Vector2D, camera: CameraConfig): Vector2D;
126
+ declare function globalCoordinatesToCameraCoordinates(global: Point, camera: CameraConfig): Point;
127
+ declare function globalCoordinatesToCameraCoordinates(global: Circle, camera: CameraConfig): Circle;
128
+ declare function globalCoordinatesToCameraCoordinates(global: Rectangle, camera: CameraConfig): Rectangle;
129
+ declare function globalCoordinatesToCameraCoordinates(global: AABB, camera: CameraConfig): AABB;
177
130
 
178
131
  /**
179
- * pepperjs
132
+ * InfernoJS
180
133
  * A spicy, high-performance library for building 2D games in JavaScript
181
134
  *
182
135
  * @packageDocumentation
183
136
  */
184
137
 
185
138
  declare const VERSION = "0.0.1";
186
- /**
187
- * @internal
188
- * Rendering capabilities
189
- */
190
- declare const Renderer: {
191
- notImplemented: boolean;
192
- info: string;
193
- };
194
139
  /**
195
140
  * @internal
196
141
  * Future game loop management API
@@ -208,4 +153,4 @@ declare const Input: {
208
153
  info: string;
209
154
  };
210
155
 
211
- export { BaseCollisionDetector, BruteForceCollisionDetector, Circle, Entity, GameLoop, Input, Point, Rectangle, Renderer, Shape, SpatialGridCollisionDetector, type SpatialGridConfig, VERSION, applyStyle, checkShapeCollision, renderCircle, renderPoint, renderRectangle, renderShape, renderShapes, restoreContext };
156
+ export { AABB, BaseCollisionDetector, BruteForceCollisionDetector, type CameraConfig, Circle, Entity, GameLoop, Input, Point, Rectangle, Shape, SpatialGridCollisionDetector, type SpatialGridConfig, VERSION, Vector2D, checkShapeCollision, globalCoordinatesToCameraCoordinates };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Entity, Shape, Circle, Point, Rectangle } from './types.js';
2
- export { AABB, CollisionConfig, CollisionResult, ContactPoint, ShapeType, Vector2D } from './types.js';
1
+ import { Entity, Shape, Vector2D, Point, Circle, Rectangle, AABB } from './types.js';
2
+ export { CollisionConfig, CollisionResult, ContactPoint, ShapeType } from './types.js';
3
3
  export { aabbIntersect, add, cellToId, circleToAABB, cross, distance, distanceSquared, dot, length, lengthSquared, normalize, positionToCell, rectToAABB, scale, subtract, vec2 } from './utils.js';
4
4
 
5
5
  /**
@@ -18,9 +18,9 @@ declare abstract class BaseCollisionDetector {
18
18
  /**
19
19
  * Get all entities that collide with the given shape
20
20
  * @param shape The shape to check collisions against
21
- * @returns Array of entity IDs that collide with the shape
21
+ * @returns Array of entities that collide with the shape
22
22
  */
23
- abstract getCollisions(shape: Shape): (string | number)[];
23
+ abstract getCollisions(shape: Shape): Entity[];
24
24
  }
25
25
 
26
26
  /**
@@ -39,9 +39,9 @@ declare class BruteForceCollisionDetectorImpl extends BaseCollisionDetector {
39
39
  /**
40
40
  * Get all entities that collide with the given shape
41
41
  * @param shape The shape to check collisions against
42
- * @returns Array of entity IDs that collide with the shape
42
+ * @returns Array of entities that collide with the shape
43
43
  */
44
- getCollisions(shape: Shape): (string | number)[];
44
+ getCollisions(shape: Shape): Entity[];
45
45
  }
46
46
 
47
47
  /**
@@ -75,7 +75,7 @@ declare class SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {
75
75
  /**
76
76
  * Get all entities that collide with the given shape
77
77
  */
78
- getCollisions(shape: Shape): (string | number)[];
78
+ getCollisions(shape: Shape): Entity[];
79
79
  /**
80
80
  * Rebuild the spatial grid with the current entities
81
81
  */
@@ -107,90 +107,35 @@ declare class SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {
107
107
  declare function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean;
108
108
 
109
109
  /**
110
- * Style options for rendering shapes
111
- */
112
- type ShapeStyle = {
113
- /** Fill color (CSS color string) */
114
- fillColor?: string;
115
- /** Stroke color (CSS color string) */
116
- strokeColor?: string;
117
- /** Stroke width in pixels */
118
- strokeWidth?: number;
119
- /** Alpha/opacity (0-1) */
120
- alpha?: number;
121
- /** Shadow blur radius */
122
- shadowBlur?: number;
123
- /** Shadow color */
124
- shadowColor?: string;
125
- /** Shadow offset X */
126
- shadowOffsetX?: number;
127
- /** Shadow offset Y */
128
- shadowOffsetY?: number;
129
- };
130
- /**
131
- * A renderable entity with shape and style
132
- */
133
- type RenderableShape = {
134
- shape: Shape;
135
- style: ShapeStyle;
136
- };
137
- /**
138
- * Configuration options for the renderer
139
- */
140
- type RendererOptions = {
141
- /** Background color of the canvas */
142
- backgroundColor?: string;
143
- /** Default style to apply to shapes without specific styling */
144
- defaultStyle?: ShapeStyle;
145
- /** Whether to clear the canvas before each render */
146
- clearBeforeRender?: boolean;
110
+ * Configuration for the camera
111
+ */
112
+ type CameraConfig = {
113
+ /** Center position of the camera in world coordinates */
114
+ position: Vector2D;
115
+ /** Width of the viewport/canvas in pixels */
116
+ width: number;
117
+ /** Height of the viewport/canvas in pixels */
118
+ height: number;
119
+ /** Rotation in degrees (optional, defaults to 0) */
120
+ rotation?: number;
147
121
  };
148
-
149
- /**
150
- * Apply style settings to the canvas context
151
- */
152
- declare function applyStyle(ctx: CanvasRenderingContext2D, style: ShapeStyle): void;
153
- /**
154
- * Restore the canvas context to its previous state
155
- */
156
- declare function restoreContext(ctx: CanvasRenderingContext2D): void;
157
- /**
158
- * Render a circle shape
159
- */
160
- declare function renderCircle(ctx: CanvasRenderingContext2D, circle: Circle, style: ShapeStyle): void;
161
- /**
162
- * Render a point shape
163
- */
164
- declare function renderPoint(ctx: CanvasRenderingContext2D, point: Point, style: ShapeStyle): void;
165
- /**
166
- * Render a rectangle shape
167
- */
168
- declare function renderRectangle(ctx: CanvasRenderingContext2D, rectangle: Rectangle, style: ShapeStyle): void;
169
- /**
170
- * Render any shape based on its type
171
- */
172
- declare function renderShape(ctx: CanvasRenderingContext2D, shape: Shape, style: ShapeStyle): void;
173
122
  /**
174
- * Main renderer function to render multiple shapes
123
+ * Converts a global coordinate object into a local camera coordinate system.
175
124
  */
176
- declare function renderShapes(ctx: CanvasRenderingContext2D, shapes: RenderableShape[], options: RendererOptions): void;
125
+ declare function globalCoordinatesToCameraCoordinates(global: Vector2D, camera: CameraConfig): Vector2D;
126
+ declare function globalCoordinatesToCameraCoordinates(global: Point, camera: CameraConfig): Point;
127
+ declare function globalCoordinatesToCameraCoordinates(global: Circle, camera: CameraConfig): Circle;
128
+ declare function globalCoordinatesToCameraCoordinates(global: Rectangle, camera: CameraConfig): Rectangle;
129
+ declare function globalCoordinatesToCameraCoordinates(global: AABB, camera: CameraConfig): AABB;
177
130
 
178
131
  /**
179
- * pepperjs
132
+ * InfernoJS
180
133
  * A spicy, high-performance library for building 2D games in JavaScript
181
134
  *
182
135
  * @packageDocumentation
183
136
  */
184
137
 
185
138
  declare const VERSION = "0.0.1";
186
- /**
187
- * @internal
188
- * Rendering capabilities
189
- */
190
- declare const Renderer: {
191
- notImplemented: boolean;
192
- info: string;
193
- };
194
139
  /**
195
140
  * @internal
196
141
  * Future game loop management API
@@ -208,4 +153,4 @@ declare const Input: {
208
153
  info: string;
209
154
  };
210
155
 
211
- export { BaseCollisionDetector, BruteForceCollisionDetector, Circle, Entity, GameLoop, Input, Point, Rectangle, Renderer, Shape, SpatialGridCollisionDetector, type SpatialGridConfig, VERSION, applyStyle, checkShapeCollision, renderCircle, renderPoint, renderRectangle, renderShape, renderShapes, restoreContext };
156
+ export { AABB, BaseCollisionDetector, BruteForceCollisionDetector, type CameraConfig, Circle, Entity, GameLoop, Input, Point, Rectangle, Shape, SpatialGridCollisionDetector, type SpatialGridConfig, VERSION, Vector2D, checkShapeCollision, globalCoordinatesToCameraCoordinates };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var B=Object.defineProperty;var z=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var U=(t,o)=>{for(var e in o)B(t,e,{get:o[e],enumerable:!0})},L=(t,o,e,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of j(o))!H.call(t,i)&&i!==e&&B(t,i,{get:()=>o[i],enumerable:!(n=z(o,i))||n.enumerable});return t};var N=t=>L(B({},"__esModule",{value:!0}),t);var ut={};U(ut,{BaseCollisionDetector:()=>p,BruteForceCollisionDetector:()=>it,GameLoop:()=>ct,Input:()=>dt,Renderer:()=>at,SpatialGridCollisionDetector:()=>rt,VERSION:()=>lt,aabbIntersect:()=>P,add:()=>J,applyStyle:()=>b,cellToId:()=>V,checkShapeCollision:()=>g,circleToAABB:()=>A,cross:()=>Q,distance:()=>Z,distanceSquared:()=>f,dot:()=>C,length:()=>v,lengthSquared:()=>E,normalize:()=>y,positionToCell:()=>R,rectToAABB:()=>h,renderCircle:()=>X,renderPoint:()=>Y,renderRectangle:()=>G,renderShape:()=>F,renderShapes:()=>st,restoreContext:()=>w,scale:()=>K,subtract:()=>x,vec2:()=>$});module.exports=N(ut);function $(t,o){return{x:t,y:o}}function J(t,o){return{x:t.x+o.x,y:t.y+o.y}}function x(t,o){return{x:t.x-o.x,y:t.y-o.y}}function K(t,o){return{x:t.x*o,y:t.y*o}}function C(t,o){return t.x*o.x+t.y*o.y}function Q(t,o){return t.x*o.y-t.y*o.x}function E(t){return t.x*t.x+t.y*t.y}function v(t){return Math.sqrt(E(t))}function y(t){let o=v(t);return o<1e-10?{x:0,y:0}:{x:t.x/o,y:t.y/o}}function f(t,o){let e=o.x-t.x,n=o.y-t.y;return e*e+n*n}function Z(t,o){return Math.sqrt(f(t,o))}function h(t){return{min:{x:t.position.x,y:t.position.y},max:{x:t.position.x+t.width,y:t.position.y+t.height}}}function A(t){return{min:{x:t.center.x-t.radius,y:t.center.y-t.radius},max:{x:t.center.x+t.radius,y:t.center.y+t.radius}}}function P(t,o){return!(t.max.x<o.min.x||t.min.x>o.max.x||t.max.y<o.min.y||t.min.y>o.max.y)}function R(t,o){return[Math.floor(t.x/o),Math.floor(t.y/o)]}function V(t,o){return(t+o)*(t+o+1)/2+o}var p=class{constructor(o){this.entities=o}};function q(t,o){let e=f(t.center,o.center),n=t.radius+o.radius;return{colliding:e<=n*n}}function k(t,o){return{colliding:f(t.center,o.position)<=t.radius*t.radius}}function S(t,o){if(o.rotationDeg!==0)return _(t,o);let e=Math.max(o.position.x,Math.min(t.center.x,o.position.x+o.width)),n=Math.max(o.position.y,Math.min(t.center.y,o.position.y+o.height));return{colliding:f(t.center,{x:e,y:n})<=t.radius*t.radius}}function _(t,o){let e=o.rotationDeg*Math.PI/180,n=Math.cos(-e),i=Math.sin(-e),r=o.position.x+o.width/2,s=o.position.y+o.height/2,l=t.center.x-r,a=t.center.y-s,c=n*l-i*a,d=i*l+n*a,m={type:"circle",center:{x:c+r,y:d+s},radius:t.radius},u={type:"rectangle",position:o.position,width:o.width,height:o.height,rotationDeg:0};return S(m,u)}function D(t,o){return t.rotationDeg!==0?tt(t,o):{colliding:o.position.x>=t.position.x&&o.position.x<=t.position.x+t.width&&o.position.y>=t.position.y&&o.position.y<=t.position.y+t.height}}function tt(t,o){let e=t.rotationDeg*Math.PI/180,n=Math.cos(-e),i=Math.sin(-e),r=t.position.x+t.width/2,s=t.position.y+t.height/2,l=o.position.x-r,a=o.position.y-s,c=n*l-i*a,d=i*l+n*a,m={type:"point",position:{x:c+r,y:d+s}},u={type:"rectangle",position:{x:t.position.x,y:t.position.y},width:t.width,height:t.height,rotationDeg:0};return D(u,m)}function O(t,o){return t.rotationDeg===0&&o.rotationDeg===0?ot(t,o):nt(t,o)}function ot(t,o){return{colliding:P(h(t),h(o))}}function T(t){let{position:o,width:e,height:n,rotationDeg:i}=t,r=e/2,s=n/2,l={x:o.x+r,y:o.y+s},a=i*Math.PI/180,c=Math.cos(a),d=Math.sin(a);return[{x:-r,y:-s},{x:r,y:-s},{x:r,y:s},{x:-r,y:s}].map(u=>({x:l.x+(u.x*c-u.y*d),y:l.y+(u.x*d+u.y*c)}))}function et(t,o){let e=[];for(let n=0;n<t.length;n++){let i=t[n],r=t[(n+1)%t.length],s=x(r,i),l=y({x:-s.y,y:s.x});e.push(l)}for(let n=0;n<o.length;n++){let i=o[n],r=o[(n+1)%o.length],s=x(r,i),l=y({x:-s.y,y:s.x});e.push(l)}return e}function W(t,o){let e=C(t[0],o),n=e;for(let i=1;i<t.length;i++){let r=C(t[i],o);r<e&&(e=r),r>n&&(n=r)}return{min:e,max:n}}function nt(t,o){let e=T(t),n=T(o),i=et(e,n);for(let r of i){let s=W(e,r),l=W(n,r);if(s.max<l.min||l.max<s.min)return{colliding:!1}}return{colliding:!0}}function g(t,o){let e=t.type,n=o.type;return e==="circle"&&n==="circle"?q(t,o).colliding:e==="circle"&&n==="point"?k(t,o).colliding:e==="point"&&n==="circle"?k(o,t).colliding:e==="circle"&&n==="rectangle"?S(t,o).colliding:e==="rectangle"&&n==="circle"?S(o,t).colliding:e==="rectangle"&&n==="rectangle"?O(t,o).colliding:e==="rectangle"&&n==="point"?D(t,o).colliding:e==="point"&&n==="rectangle"?D(o,t).colliding:!1}function it({entities:t}){return new I(t)}var I=class extends p{getCollisions(o){let e=[];for(let n of this.entities)g(o,n.shape)&&e.push(n.id);return e}};function rt({entities:t,cellSize:o=64}){return new M(t,{cellSize:o})}var M=class extends p{constructor(o,e){super(o),this.config=e,this.grid=new Map,this.rebuildGrid()}getCollisions(o){let e=this.getAABBForShape(o),n=this.getCellsForAABB(e),i=new Set;for(let s of n){let l=this.grid.get(s);if(l)for(let a of l)i.add(a)}let r=[];for(let s of i)g(o,s.shape)&&r.push(s.id);return r}rebuildGrid(){this.grid.clear();for(let o of this.entities)this.insertEntityIntoGrid(o)}insertEntityIntoGrid(o){let e=this.getAABB(o),n=this.getCellsForAABB(e);for(let i of n)this.grid.has(i)||this.grid.set(i,[]),this.grid.get(i).push(o)}getCellsForAABB(o){let{cellSize:e}=this.config,[n,i]=R(o.min,e),[r,s]=R(o.max,e),l=new Array((r-n+1)*(s-i+1)),a=0;for(let c=n;c<=r;c++)for(let d=i;d<=s;d++)l[a++]=V(c,d);return l}getAABB(o){return this.getAABBForShape(o.shape)}getAABBForShape(o){let e=o.type;if(e==="circle")return A(o);if(e==="rectangle")return h(o);if(e==="point")return{min:{x:o.position.x,y:o.position.y},max:{x:o.position.x,y:o.position.y}};throw new Error(`Unsupported shape type: ${e}`)}};function b(t,o){t.save(),o.fillColor&&(t.fillStyle=o.fillColor),o.strokeColor&&(t.strokeStyle=o.strokeColor),o.strokeWidth!==void 0&&(t.lineWidth=o.strokeWidth),o.alpha!==void 0&&(t.globalAlpha=o.alpha),o.shadowBlur!==void 0&&(t.shadowBlur=o.shadowBlur),o.shadowColor&&(t.shadowColor=o.shadowColor),o.shadowOffsetX!==void 0&&(t.shadowOffsetX=o.shadowOffsetX),o.shadowOffsetY!==void 0&&(t.shadowOffsetY=o.shadowOffsetY)}function w(t){t.restore()}function X(t,o,e){b(t,e),t.beginPath(),t.arc(o.center.x,o.center.y,o.radius,0,Math.PI*2),e.fillColor&&t.fill(),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.stroke(),w(t)}function Y(t,o,e){let n=e.strokeWidth||4;b(t,e),t.beginPath(),t.arc(o.position.x,o.position.y,n/2,0,Math.PI*2),e.fillColor&&t.fill(),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.stroke(),w(t)}function G(t,o,e){if(b(t,e),t.save(),t.translate(o.position.x,o.position.y),o.rotationDeg!==0){let r=o.rotationDeg*Math.PI/180;t.rotate(r)}let n=o.width/2,i=o.height/2;e.fillColor&&t.fillRect(-n,-i,o.width,o.height),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.strokeRect(-n,-i,o.width,o.height),t.restore(),w(t)}function F(t,o,e){switch(o.type){case"circle":X(t,o,e);break;case"rectangle":G(t,o,e);break;case"point":Y(t,o,e);break;default:console.warn("Unknown shape type encountered");break}}function st(t,o,e){for(let{shape:n,style:i}of o){let r={...e.defaultStyle,...i};F(t,n,r)}}var lt="0.0.1",at={notImplemented:!1,info:"Basic rendering capabilities are now available"},ct={notImplemented:!0,info:"Game loop management will be added in a future version"},dt={notImplemented:!0,info:"Input handling will be added in a future version"};0&&(module.exports={BaseCollisionDetector,BruteForceCollisionDetector,GameLoop,Input,Renderer,SpatialGridCollisionDetector,VERSION,aabbIntersect,add,applyStyle,cellToId,checkShapeCollision,circleToAABB,cross,distance,distanceSquared,dot,length,lengthSquared,normalize,positionToCell,rectToAABB,renderCircle,renderPoint,renderRectangle,renderShape,renderShapes,restoreContext,scale,subtract,vec2});
1
+ "use strict";var B=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var j=(t,o)=>{for(var n in o)B(t,n,{get:o[n],enumerable:!0})},z=(t,o,n,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of v(o))!F.call(t,e)&&e!==n&&B(t,e,{get:()=>o[e],enumerable:!(i=Y(o,e))||i.enumerable});return t};var k=t=>z(B({},"__esModule",{value:!0}),t);var et={};j(et,{BaseCollisionDetector:()=>m,BruteForceCollisionDetector:()=>Z,GameLoop:()=>nt,Input:()=>it,SpatialGridCollisionDetector:()=>_,VERSION:()=>ot,aabbIntersect:()=>S,add:()=>U,cellToId:()=>P,checkShapeCollision:()=>y,circleToAABB:()=>V,cross:()=>L,distance:()=>N,distanceSquared:()=>x,dot:()=>g,globalCoordinatesToCameraCoordinates:()=>tt,length:()=>b,lengthSquared:()=>E,normalize:()=>h,positionToCell:()=>R,rectToAABB:()=>d,scale:()=>H,subtract:()=>C,vec2:()=>O});module.exports=k(et);function O(t,o){return{x:t,y:o}}function U(t,o){return{x:t.x+o.x,y:t.y+o.y}}function C(t,o){return{x:t.x-o.x,y:t.y-o.y}}function H(t,o){return{x:t.x*o,y:t.y*o}}function g(t,o){return t.x*o.x+t.y*o.y}function L(t,o){return t.x*o.y-t.y*o.x}function E(t){return t.x*t.x+t.y*t.y}function b(t){return Math.sqrt(E(t))}function h(t){let o=b(t);return o<1e-10?{x:0,y:0}:{x:t.x/o,y:t.y/o}}function x(t,o){let n=o.x-t.x,i=o.y-t.y;return n*n+i*i}function N(t,o){return Math.sqrt(x(t,o))}function d(t){return{min:{x:t.position.x,y:t.position.y},max:{x:t.position.x+t.width,y:t.position.y+t.height}}}function V(t){return{min:{x:t.center.x-t.radius,y:t.center.y-t.radius},max:{x:t.center.x+t.radius,y:t.center.y+t.radius}}}function S(t,o){return!(t.max.x<o.min.x||t.min.x>o.max.x||t.max.y<o.min.y||t.min.y>o.max.y)}function R(t,o){return[Math.floor(t.x/o),Math.floor(t.y/o)]}function P(t,o){return(t+o)*(t+o+1)/2+o}var m=class{constructor(o){this.entities=o}};function T(t,o){let n=x(t.center,o.center),i=t.radius+o.radius;return{colliding:n<=i*i}}function M(t,o){return{colliding:x(t.center,o.position)<=t.radius*t.radius}}function D(t,o){if(o.rotationDeg!==0)return W(t,o);let n=Math.max(o.position.x,Math.min(t.center.x,o.position.x+o.width)),i=Math.max(o.position.y,Math.min(t.center.y,o.position.y+o.height));return{colliding:x(t.center,{x:n,y:i})<=t.radius*t.radius}}function W(t,o){let n=o.rotationDeg*Math.PI/180,i=Math.cos(-n),e=Math.sin(-n),r=o.position.x+o.width/2,s=o.position.y+o.height/2,c=t.center.x-r,l=t.center.y-s,a=i*c-e*l,u=e*c+i*l,f={type:"circle",center:{x:a+r,y:u+s},radius:t.radius},p={type:"rectangle",position:o.position,width:o.width,height:o.height,rotationDeg:0};return D(f,p)}function A(t,o){return t.rotationDeg!==0?$(t,o):{colliding:o.position.x>=t.position.x&&o.position.x<=t.position.x+t.width&&o.position.y>=t.position.y&&o.position.y<=t.position.y+t.height}}function $(t,o){let n=t.rotationDeg*Math.PI/180,i=Math.cos(-n),e=Math.sin(-n),r=t.position.x+t.width/2,s=t.position.y+t.height/2,c=o.position.x-r,l=o.position.y-s,a=i*c-e*l,u=e*c+i*l,f={type:"point",position:{x:a+r,y:u+s}},p={type:"rectangle",position:{x:t.position.x,y:t.position.y},width:t.width,height:t.height,rotationDeg:0};return A(p,f)}function X(t,o){return t.rotationDeg===0&&o.rotationDeg===0?J(t,o):Q(t,o)}function J(t,o){return{colliding:S(d(t),d(o))}}function q(t){let{position:o,width:n,height:i,rotationDeg:e}=t,r=n/2,s=i/2,c={x:o.x+r,y:o.y+s},l=e*Math.PI/180,a=Math.cos(l),u=Math.sin(l);return[{x:-r,y:-s},{x:r,y:-s},{x:r,y:s},{x:-r,y:s}].map(p=>({x:c.x+(p.x*a-p.y*u),y:c.y+(p.x*u+p.y*a)}))}function K(t,o){let n=[];for(let i=0;i<t.length;i++){let e=t[i],r=t[(i+1)%t.length],s=C(r,e),c=h({x:-s.y,y:s.x});n.push(c)}for(let i=0;i<o.length;i++){let e=o[i],r=o[(i+1)%o.length],s=C(r,e),c=h({x:-s.y,y:s.x});n.push(c)}return n}function G(t,o){let n=g(t[0],o),i=n;for(let e=1;e<t.length;e++){let r=g(t[e],o);r<n&&(n=r),r>i&&(i=r)}return{min:n,max:i}}function Q(t,o){let n=q(t),i=q(o),e=K(n,i);for(let r of e){let s=G(n,r),c=G(i,r);if(s.max<c.min||c.max<s.min)return{colliding:!1}}return{colliding:!0}}function y(t,o){let n=t.type,i=o.type;return n==="circle"&&i==="circle"?T(t,o).colliding:n==="circle"&&i==="point"?M(t,o).colliding:n==="point"&&i==="circle"?M(o,t).colliding:n==="circle"&&i==="rectangle"?D(t,o).colliding:n==="rectangle"&&i==="circle"?D(o,t).colliding:n==="rectangle"&&i==="rectangle"?X(t,o).colliding:n==="rectangle"&&i==="point"?A(t,o).colliding:n==="point"&&i==="rectangle"?A(o,t).colliding:!1}function Z({entities:t}){return new I(t)}var I=class extends m{getCollisions(o){let n=[];for(let i of this.entities)y(o,i.shape)&&n.push(i);return n}};function _({entities:t,cellSize:o=64}){return new w(t,{cellSize:o})}var w=class extends m{constructor(o,n){super(o),this.config=n,this.grid=new Map,this.rebuildGrid()}getCollisions(o){let n=this.getAABBForShape(o),i=this.getCellsForAABB(n),e=new Set;for(let s of i){let c=this.grid.get(s);if(c)for(let l of c)e.add(l)}let r=[];for(let s of e)y(o,s.shape)&&r.push(s);return r}rebuildGrid(){this.grid.clear();for(let o of this.entities)this.insertEntityIntoGrid(o)}insertEntityIntoGrid(o){let n=this.getAABB(o),i=this.getCellsForAABB(n);for(let e of i)this.grid.has(e)||this.grid.set(e,[]),this.grid.get(e).push(o)}getCellsForAABB(o){let{cellSize:n}=this.config,[i,e]=R(o.min,n),[r,s]=R(o.max,n),c=new Array((r-i+1)*(s-e+1)),l=0;for(let a=i;a<=r;a++)for(let u=e;u<=s;u++)c[l++]=P(a,u);return c}getAABB(o){return this.getAABBForShape(o.shape)}getAABBForShape(o){let n=o.type;if(n==="circle")return V(o);if(n==="rectangle")return d(o);if(n==="point")return{min:{x:o.position.x,y:o.position.y},max:{x:o.position.x,y:o.position.y}};throw new Error(`Unsupported shape type: ${n}`)}};function tt(t,o){let n=i=>{let e=i.x-o.position.x,r=i.y-o.position.y;if(o.rotation){let c=-(o.rotation*Math.PI/180),l=Math.cos(c),a=Math.sin(c),u=e*l-r*a,f=e*a+r*l;e=u,r=f}return e+=o.width/2,r+=o.height/2,{x:e,y:r}};if("type"in t){if(t.type==="point")return{...t,position:n(t.position)};if(t.type==="circle")return{...t,center:n(t.center)};if(t.type==="rectangle")return{...t,position:n(t.position),rotationDeg:t.rotationDeg-(o.rotation??0)}}if("min"in t&&"max"in t){if(!o.rotation)return{min:n(t.min),max:n(t.max)};let e=[{x:t.min.x,y:t.min.y},{x:t.max.x,y:t.min.y},{x:t.max.x,y:t.max.y},{x:t.min.x,y:t.max.y}].map(n),r=1/0,s=1/0,c=-1/0,l=-1/0;for(let a of e)r=Math.min(r,a.x),s=Math.min(s,a.y),c=Math.max(c,a.x),l=Math.max(l,a.y);return{min:{x:r,y:s},max:{x:c,y:l}}}if("x"in t&&"y"in t)return n(t);throw new Error("Unsupported type passed to globalCoordinatesToCameraCoordinates")}var ot="0.0.1",nt={notImplemented:!0,info:"Game loop management will be added in a future version"},it={notImplemented:!0,info:"Input handling will be added in a future version"};0&&(module.exports={BaseCollisionDetector,BruteForceCollisionDetector,GameLoop,Input,SpatialGridCollisionDetector,VERSION,aabbIntersect,add,cellToId,checkShapeCollision,circleToAABB,cross,distance,distanceSquared,dot,globalCoordinatesToCameraCoordinates,length,lengthSquared,normalize,positionToCell,rectToAABB,scale,subtract,vec2});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/collision/CollisionDetector.ts","../src/collision/shapeCollisions/circleCircle/index.ts","../src/collision/shapeCollisions/circlePoint/index.ts","../src/collision/shapeCollisions/circleRect/index.ts","../src/collision/shapeCollisions/rectPoint/index.ts","../src/collision/shapeCollisions/rectRect/index.ts","../src/collision/shapeCollisions/checkShapeCollision.ts","../src/collision/BruteForceCollisionDetector.ts","../src/collision/SpatialGridCollisionDetector.ts","../src/render/index.ts"],"sourcesContent":["/**\n * pepperjs\n * A spicy, high-performance library for building 2D games in JavaScript\n *\n * @packageDocumentation\n */\n\n// ----- Collision Detection System -----\n// Export types\nexport * from \"./types\";\n\n// Export utility functions\nexport * from \"./utils\";\n\n// Export collision detection system\nexport * from \"./collision\";\n\n// Export rendering system\nexport * from \"./render\";\n\n// ----- Library Information -----\nexport const VERSION = \"0.0.1\";\n\n// ----- Future modules (placeholders) -----\n// These will be implemented in future versions\n\n/**\n * @internal\n * Rendering capabilities\n */\nexport const Renderer = {\n // Renderer is now implemented\n notImplemented: false,\n\n // Information about implementation\n info: \"Basic rendering capabilities are now available\",\n};\n\n/**\n * @internal\n * Future game loop management API\n */\nexport const GameLoop = {\n // Placeholder for future game loop module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Game loop management will be added in a future version\",\n};\n\n/**\n * @internal\n * Future input handling API\n */\nexport const Input = {\n // Placeholder for future input handling module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Input handling will be added in a future version\",\n};\n","import { Vector2D, AABB, Circle, Rectangle } from \"./types\";\n\n/**\n * Create a new vector\n */\nexport function vec2(x: number, y: number): Vector2D {\n return { x, y };\n}\n\n/**\n * Vector addition\n */\nexport function add(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x + b.x, y: a.y + b.y };\n}\n\n/**\n * Vector subtraction\n */\nexport function subtract(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x - b.x, y: a.y - b.y };\n}\n\n/**\n * Vector scaling\n */\nexport function scale(v: Vector2D, scalar: number): Vector2D {\n return { x: v.x * scalar, y: v.y * scalar };\n}\n\n/**\n * Dot product of two vectors\n */\nexport function dot(a: Vector2D, b: Vector2D): number {\n return a.x * b.x + a.y * b.y;\n}\n\n/**\n * Cross product magnitude of two 2D vectors\n */\nexport function cross(a: Vector2D, b: Vector2D): number {\n return a.x * b.y - a.y * b.x;\n}\n\n/**\n * Get squared length of a vector (avoids sqrt for performance)\n */\nexport function lengthSquared(v: Vector2D): number {\n return v.x * v.x + v.y * v.y;\n}\n\n/**\n * Get vector length\n */\nexport function length(v: Vector2D): number {\n return Math.sqrt(lengthSquared(v));\n}\n\n/**\n * Normalize a vector (make it unit length)\n */\nexport function normalize(v: Vector2D): Vector2D {\n const len = length(v);\n // Avoid division by zero\n if (len < 1e-10) return { x: 0, y: 0 };\n return { x: v.x / len, y: v.y / len };\n}\n\n/**\n * Distance squared between two points (avoids sqrt for performance)\n */\nexport function distanceSquared(a: Vector2D, b: Vector2D): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Distance between two points\n */\nexport function distance(a: Vector2D, b: Vector2D): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n\n/**\n * Create an AABB from a rectangle\n */\nexport function rectToAABB(rect: Rectangle): AABB {\n return {\n min: { x: rect.position.x, y: rect.position.y },\n max: { x: rect.position.x + rect.width, y: rect.position.y + rect.height },\n };\n}\n\n/**\n * Create an AABB from a circle\n */\nexport function circleToAABB(circle: Circle): AABB {\n return {\n min: {\n x: circle.center.x - circle.radius,\n y: circle.center.y - circle.radius,\n },\n max: {\n x: circle.center.x + circle.radius,\n y: circle.center.y + circle.radius,\n },\n };\n}\n\n/**\n * Check if two AABBs intersect\n */\nexport function aabbIntersect(a: AABB, b: AABB): boolean {\n // Exit with no intersection if separated along an axis\n if (a.max.x < b.min.x || a.min.x > b.max.x) return false;\n if (a.max.y < b.min.y || a.min.y > b.max.y) return false;\n\n // Overlapping on all axes means AABBs are intersecting\n return true;\n}\n\n/**\n * Calculate cell indices for a position in a spatial grid\n */\nexport function positionToCell(\n position: Vector2D,\n cellSize: number,\n): [number, number] {\n return [Math.floor(position.x / cellSize), Math.floor(position.y / cellSize)];\n}\n\n/**\n * Calculate a unique cell ID from grid coordinates\n * Uses a spatial hashing function to convert 2D coordinates to 1D\n */\nexport function cellToId(x: number, y: number): number {\n // Cantor pairing function - maps two non-negative integers to a unique non-negative integer\n return ((x + y) * (x + y + 1)) / 2 + y;\n}\n","import { Entity, Shape } from \"../types\";\n\n/**\n * Base class for collision detectors that implements common functionality\n */\nexport abstract class BaseCollisionDetector {\n /**\n * The entities to check collisions against\n */\n protected entities: Entity[];\n\n /**\n * Create a new collision detector\n * @param entities The entities to check collisions against\n */\n constructor(entities: Entity[]) {\n this.entities = entities;\n }\n\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entity IDs that collide with the shape\n */\n abstract getCollisions(shape: Shape): (string | number)[];\n}\n","import { Circle, CollisionResult } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between two circles\n * @param circleA First circle\n * @param circleB Second circle\n * @returns Collision result with contact information\n */\nexport function circleCircleCollision(\n circleA: Circle,\n circleB: Circle,\n): CollisionResult {\n const distSquared = distanceSquared(circleA.center, circleB.center);\n\n const radiusSum = circleA.radius + circleB.radius;\n\n const colliding = distSquared <= radiusSum * radiusSum;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Point } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a point\n * @param circle Circle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function circlePointCollision(\n circle: Circle,\n point: Point,\n): CollisionResult {\n const distSquared = distanceSquared(circle.center, point.position);\n\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Rectangle } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a rectangle\n * @param circle Circle\n * @param rect Rectangle\n * @returns Collision result with contact information\n */\nexport function circleRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return circleRotatedRectCollision(circle, rect);\n }\n\n // Find the closest point on the rectangle to the circle center\n const closestX = Math.max(\n rect.position.x,\n Math.min(circle.center.x, rect.position.x + rect.width),\n );\n const closestY = Math.max(\n rect.position.y,\n Math.min(circle.center.y, rect.position.y + rect.height),\n );\n\n const distSquared = distanceSquared(circle.center, {\n x: closestX,\n y: closestY,\n });\n\n // Check if the circle is colliding with the rectangle\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a circle and a rotated rectangle\n * @param circle Circle\n * @param rect Rotated rectangle\n * @returns Collision result with contact information\n */\nfunction circleRotatedRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Translate circle center to rectangle's local space (unrotated)\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to circle center\n const dx = circle.center.x - rectCenterX;\n const dy = circle.center.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a circle in the rectangle's local space\n const localCircle: Circle = {\n type: \"circle\",\n center: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n radius: circle.radius,\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: rect.position,\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return circleRectCollision(localCircle, localRect);\n}\n","import { CollisionResult, Point, Rectangle } from \"../../../types\";\n\n/**\n * Detects collision between a rectangle and a point\n * @param rect Rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function rectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return rotatedRectPointCollision(rect, point);\n }\n\n // Check if the point is inside the rectangle\n const colliding =\n point.position.x >= rect.position.x &&\n point.position.x <= rect.position.x + rect.width &&\n point.position.y >= rect.position.y &&\n point.position.y <= rect.position.y + rect.height;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a rotated rectangle and a point\n * @param rect Rotated rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nfunction rotatedRectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Calculate rectangle center\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to point\n const dx = point.position.x - rectCenterX;\n const dy = point.position.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a point in the rectangle's local space\n const localPoint: Point = {\n type: \"point\",\n position: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: {\n x: rect.position.x,\n y: rect.position.y,\n },\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return rectPointCollision(localRect, localPoint);\n}\n","import { Rectangle, CollisionResult, Vector2D } from \"../../../types\";\nimport {\n subtract,\n normalize,\n dot,\n rectToAABB,\n aabbIntersect,\n} from \"../../../utils\";\n\n/**\n * Detect collision between two rectangles\n * This implementation handles rotated rectangles using the Separating Axis Theorem (SAT)\n */\nexport function rectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // If both rectangles have zero rotation, use a simpler AABB check\n if (rectA.rotationDeg === 0 && rectB.rotationDeg === 0) {\n return aabbRectRectCollision(rectA, rectB);\n }\n\n // For rotated rectangles, use Separating Axis Theorem (SAT)\n return satRectRectCollision(rectA, rectB);\n}\n\n/**\n * Collision detection for axis-aligned (non-rotated) rectangles\n */\nfunction aabbRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n return {\n colliding: aabbIntersect(rectToAABB(rectA), rectToAABB(rectB)),\n };\n}\n\n/**\n * Get the corners of a rotated rectangle\n */\nfunction getRectangleCorners(rect: Rectangle): Vector2D[] {\n const { position, width, height, rotationDeg } = rect;\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n\n // Calculate center of the rectangle\n const center = {\n x: position.x + halfWidth,\n y: position.y + halfHeight,\n };\n\n // Convert rotation to radians\n const rotationRad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rotationRad);\n const sin = Math.sin(rotationRad);\n\n // Calculate corners relative to center\n const corners: Vector2D[] = [\n { x: -halfWidth, y: -halfHeight }, // Top-left\n { x: halfWidth, y: -halfHeight }, // Top-right\n { x: halfWidth, y: halfHeight }, // Bottom-right\n { x: -halfWidth, y: halfHeight }, // Bottom-left\n ];\n\n // Rotate and translate corners\n return corners.map((corner) => ({\n x: center.x + (corner.x * cos - corner.y * sin),\n y: center.y + (corner.x * sin + corner.y * cos),\n }));\n}\n\n/**\n * Get the axes to test for the SAT algorithm\n */\nfunction getAxes(cornersA: Vector2D[], cornersB: Vector2D[]): Vector2D[] {\n const axes: Vector2D[] = [];\n\n // Add the normals of each edge of the first rectangle\n for (let i = 0; i < cornersA.length; i++) {\n const p1 = cornersA[i];\n const p2 = cornersA[(i + 1) % cornersA.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n // Add the normals of each edge of the second rectangle\n for (let i = 0; i < cornersB.length; i++) {\n const p1 = cornersB[i];\n const p2 = cornersB[(i + 1) % cornersB.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n return axes;\n}\n\n/**\n * Project a shape onto an axis\n */\nfunction projectShapeOntoAxis(\n corners: Vector2D[],\n axis: Vector2D,\n): { min: number; max: number } {\n let min = dot(corners[0], axis);\n let max = min;\n\n for (let i = 1; i < corners.length; i++) {\n const projection = dot(corners[i], axis);\n if (projection < min) min = projection;\n if (projection > max) max = projection;\n }\n\n return { min, max };\n}\n\n/**\n * Collision detection for rotated rectangles using Separating Axis Theorem (SAT)\n */\nfunction satRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // Get corners of both rectangles\n const cornersA = getRectangleCorners(rectA);\n const cornersB = getRectangleCorners(rectB);\n\n // Get axes to test\n const axes = getAxes(cornersA, cornersB);\n\n // Test each axis\n for (const axis of axes) {\n const projectionA = projectShapeOntoAxis(cornersA, axis);\n const projectionB = projectShapeOntoAxis(cornersB, axis);\n\n // Check for separation\n if (\n projectionA.max < projectionB.min ||\n projectionB.max < projectionA.min\n ) {\n // Shapes are separated along this axis\n return { colliding: false };\n }\n }\n\n // If we get here, the shapes are colliding\n return { colliding: true };\n}\n","import { Circle, Point, Rectangle, Shape } from \"../../types\";\nimport { circleCircleCollision } from \"./circleCircle\";\nimport { circlePointCollision } from \"./circlePoint\";\nimport { circleRectCollision } from \"./circleRect\";\nimport { rectPointCollision } from \"./rectPoint\";\nimport { rectRectCollision } from \"./rectRect\";\n\n/**\n * Check if two entities are colliding\n * @param shapeA First shape\n * @param shapeB Second shape\n * @returns True if the shapes are colliding\n */\nexport function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean {\n const shapeAType = shapeA.type;\n const shapeBType = shapeB.type;\n\n // Circle vs Circle\n if (shapeAType === \"circle\" && shapeBType === \"circle\") {\n return circleCircleCollision(shapeA as Circle, shapeB as Circle).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"point\") {\n return circlePointCollision(shapeA as Circle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"circle\") {\n return circlePointCollision(shapeB as Circle, shapeA as Point).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"rectangle\") {\n return circleRectCollision(shapeA as Circle, shapeB as Rectangle).colliding;\n }\n if (shapeAType === \"rectangle\" && shapeBType === \"circle\") {\n return circleRectCollision(shapeB as Circle, shapeA as Rectangle).colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"rectangle\") {\n return rectRectCollision(shapeA as Rectangle, shapeB as Rectangle)\n .colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"point\") {\n return rectPointCollision(shapeA as Rectangle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"rectangle\") {\n return rectPointCollision(shapeB as Rectangle, shapeA as Point).colliding;\n }\n\n return false;\n}\n","import { Entity, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions/checkShapeCollision\";\n\n/**\n * Collision detector that uses a brute force approach\n * Checks every entity against the given shape\n *\n * Best used when:\n * - You have a small number of entities\n * - You only need to check collisions occasionally\n * - You want the simplest implementation\n */\nexport function BruteForceCollisionDetector({\n entities,\n}: {\n entities: Entity[];\n}) {\n return new BruteForceCollisionDetectorImpl(entities);\n}\n\nclass BruteForceCollisionDetectorImpl extends BaseCollisionDetector {\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entity IDs that collide with the shape\n */\n getCollisions(shape: Shape): (string | number)[] {\n const collisions: (string | number)[] = [];\n\n // Check each entity against the given shape\n for (const entity of this.entities) {\n // Check if the shapes collide\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity.id);\n }\n }\n\n return collisions;\n }\n}\n","import { AABB, Circle, Entity, Point, Rectangle, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions/checkShapeCollision\";\nimport { cellToId, circleToAABB, positionToCell, rectToAABB } from \"../utils\";\n\ntype CellId = number;\n\n/**\n * Configuration options for the spatial grid collision detector\n */\nexport interface SpatialGridConfig {\n /** Size of each grid cell */\n cellSize: number;\n}\n\n/**\n * Collision detector that uses spatial partitioning for efficient collision detection\n *\n * Best used when:\n * - You have a large number of entities\n * - Entities are distributed across the space\n * - You need to check collisions frequently\n */\nexport function SpatialGridCollisionDetector({\n entities,\n cellSize = 64,\n}: {\n entities: Entity[];\n cellSize?: number;\n}) {\n return new SpatialGridCollisionDetectorImpl(entities, { cellSize });\n}\n\nclass SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {\n /** The spatial hash grid for quick lookups */\n private grid: Map<CellId, Entity[]>;\n /** Configuration for the spatial grid */\n private config: SpatialGridConfig;\n\n /**\n * Create a new spatial grid collision detector\n */\n constructor(entities: Entity[], config: SpatialGridConfig) {\n super(entities);\n this.config = config;\n this.grid = new Map();\n\n // Initialize the grid with entities\n this.rebuildGrid();\n }\n\n /**\n * Get all entities that collide with the given shape\n */\n getCollisions(shape: Shape): (string | number)[] {\n // Get the AABB for the shape\n const aabb = this.getAABBForShape(shape);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Get all entities in those cells\n const potentialCollisions = new Set<Entity>();\n\n for (const cellId of cells) {\n const entitiesInCell = this.grid.get(cellId);\n if (entitiesInCell) {\n for (const entity of entitiesInCell) {\n potentialCollisions.add(entity);\n }\n }\n }\n\n // Check for actual collisions\n const collisions: (string | number)[] = [];\n\n for (const entity of potentialCollisions) {\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity.id);\n }\n }\n\n return collisions;\n }\n\n /**\n * Rebuild the spatial grid with the current entities\n */\n rebuildGrid(): void {\n // Clear the grid\n this.grid.clear();\n\n // Add all entities to the grid\n for (const entity of this.entities) {\n this.insertEntityIntoGrid(entity);\n }\n }\n\n /**\n * Insert an entity into the spatial grid\n */\n private insertEntityIntoGrid(entity: Entity): void {\n // Get the AABB for the entity\n const aabb = this.getAABB(entity);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Add the entity to each cell\n for (const cellId of cells) {\n if (!this.grid.has(cellId)) {\n this.grid.set(cellId, []);\n }\n\n this.grid.get(cellId)!.push(entity);\n }\n }\n\n /**\n * Get all cells that an AABB overlaps\n */\n private getCellsForAABB(aabb: AABB): CellId[] {\n const { cellSize } = this.config;\n\n // Calculate the min and max cell coordinates\n const [minCellX, minCellY] = positionToCell(aabb.min, cellSize);\n const [maxCellX, maxCellY] = positionToCell(aabb.max, cellSize);\n\n // Get all cells in the range\n const cells: CellId[] = new Array(\n (maxCellX - minCellX + 1) * (maxCellY - minCellY + 1),\n );\n let index = 0;\n\n for (let x = minCellX; x <= maxCellX; x++) {\n for (let y = minCellY; y <= maxCellY; y++) {\n cells[index++] = cellToId(x, y);\n }\n }\n\n return cells;\n }\n\n /**\n * Get the AABB for an entity\n */\n private getAABB(entity: Entity): AABB {\n return this.getAABBForShape(entity.shape);\n }\n\n /**\n * Get the AABB for a shape\n */\n private getAABBForShape(shape: Shape): AABB {\n const shapeType = shape.type;\n\n if (shapeType === \"circle\") {\n return circleToAABB(shape as Circle);\n } else if (shapeType === \"rectangle\") {\n return rectToAABB(shape as Rectangle);\n } else if (shapeType === \"point\") {\n // For a point, create a tiny AABB\n return {\n min: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n max: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n };\n }\n\n throw new Error(`Unsupported shape type: ${shapeType}`);\n }\n}\n","import { Circle, Point, Rectangle, Shape } from \"../types\";\nimport { ShapeStyle, RendererOptions, RenderableShape } from \"./types\";\n\n/**\n * Apply style settings to the canvas context\n */\nexport function applyStyle(ctx: CanvasRenderingContext2D, style: ShapeStyle) {\n // Save the current context state\n ctx.save();\n\n // Apply fill and stroke styles\n if (style.fillColor) {\n ctx.fillStyle = style.fillColor;\n }\n\n if (style.strokeColor) {\n ctx.strokeStyle = style.strokeColor;\n }\n\n if (style.strokeWidth !== undefined) {\n ctx.lineWidth = style.strokeWidth;\n }\n\n if (style.alpha !== undefined) {\n ctx.globalAlpha = style.alpha;\n }\n\n // Apply shadow settings if provided\n if (style.shadowBlur !== undefined) {\n ctx.shadowBlur = style.shadowBlur;\n }\n\n if (style.shadowColor) {\n ctx.shadowColor = style.shadowColor;\n }\n\n if (style.shadowOffsetX !== undefined) {\n ctx.shadowOffsetX = style.shadowOffsetX;\n }\n\n if (style.shadowOffsetY !== undefined) {\n ctx.shadowOffsetY = style.shadowOffsetY;\n }\n}\n\n/**\n * Restore the canvas context to its previous state\n */\nexport function restoreContext(ctx: CanvasRenderingContext2D) {\n ctx.restore();\n}\n\n/**\n * Render a circle shape\n */\nexport function renderCircle(\n ctx: CanvasRenderingContext2D,\n circle: Circle,\n style: ShapeStyle,\n) {\n applyStyle(ctx, style);\n\n ctx.beginPath();\n ctx.arc(circle.center.x, circle.center.y, circle.radius, 0, Math.PI * 2);\n\n if (style.fillColor) {\n ctx.fill();\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.stroke();\n }\n\n restoreContext(ctx);\n}\n\n/**\n * Render a point shape\n */\nexport function renderPoint(\n ctx: CanvasRenderingContext2D,\n point: Point,\n style: ShapeStyle,\n) {\n // Points are rendered as small circles\n const pointSize = style.strokeWidth || 4;\n\n applyStyle(ctx, style);\n\n ctx.beginPath();\n ctx.arc(point.position.x, point.position.y, pointSize / 2, 0, Math.PI * 2);\n\n if (style.fillColor) {\n ctx.fill();\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.stroke();\n }\n\n restoreContext(ctx);\n}\n\n/**\n * Render a rectangle shape\n */\nexport function renderRectangle(\n ctx: CanvasRenderingContext2D,\n rectangle: Rectangle,\n style: ShapeStyle,\n) {\n applyStyle(ctx, style);\n\n // Save the current transformation matrix\n ctx.save();\n\n // Translate to the rectangle's position\n ctx.translate(rectangle.position.x, rectangle.position.y);\n\n // Rotate if needed\n if (rectangle.rotationDeg !== 0) {\n const rotationRad = (rectangle.rotationDeg * Math.PI) / 180;\n ctx.rotate(rotationRad);\n }\n\n // Draw the rectangle centered at the origin\n const halfWidth = rectangle.width / 2;\n const halfHeight = rectangle.height / 2;\n\n if (style.fillColor) {\n ctx.fillRect(-halfWidth, -halfHeight, rectangle.width, rectangle.height);\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.strokeRect(-halfWidth, -halfHeight, rectangle.width, rectangle.height);\n }\n\n // Restore the transformation matrix\n ctx.restore();\n\n // Restore the style context\n restoreContext(ctx);\n}\n\n/**\n * Render any shape based on its type\n */\nexport function renderShape(\n ctx: CanvasRenderingContext2D,\n shape: Shape,\n style: ShapeStyle,\n) {\n switch (shape.type) {\n case \"circle\":\n renderCircle(ctx, shape, style);\n break;\n case \"rectangle\":\n renderRectangle(ctx, shape, style);\n break;\n case \"point\":\n renderPoint(ctx, shape, style);\n break;\n default:\n // This should never happen if all shape types are handled\n console.warn(\"Unknown shape type encountered\");\n break;\n }\n}\n\n/**\n * Main renderer function to render multiple shapes\n */\nexport function renderShapes(\n ctx: CanvasRenderingContext2D,\n shapes: RenderableShape[],\n options: RendererOptions,\n) {\n // Render each shape\n for (const { shape, style } of shapes) {\n // Merge with default style if provided\n const mergedStyle = {\n ...options.defaultStyle,\n ...style,\n };\n\n renderShape(ctx, shape, mergedStyle);\n }\n}\n"],"mappings":"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","names":["index_exports","__export","BaseCollisionDetector","BruteForceCollisionDetector","GameLoop","Input","Renderer","SpatialGridCollisionDetector","VERSION","aabbIntersect","add","applyStyle","cellToId","checkShapeCollision","circleToAABB","cross","distance","distanceSquared","dot","length","lengthSquared","normalize","positionToCell","rectToAABB","renderCircle","renderPoint","renderRectangle","renderShape","renderShapes","restoreContext","scale","subtract","vec2","__toCommonJS","vec2","x","y","add","a","b","subtract","scale","v","scalar","dot","cross","lengthSquared","length","normalize","len","distanceSquared","dx","dy","distance","rectToAABB","rect","circleToAABB","circle","aabbIntersect","positionToCell","position","cellSize","cellToId","BaseCollisionDetector","entities","circleCircleCollision","circleA","circleB","distSquared","distanceSquared","radiusSum","circlePointCollision","circle","point","distanceSquared","circleRectCollision","circle","rect","circleRotatedRectCollision","closestX","closestY","distanceSquared","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localCircle","localRect","rectPointCollision","rect","point","rotatedRectPointCollision","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localPoint","localRect","rectRectCollision","rectA","rectB","aabbRectRectCollision","satRectRectCollision","aabbIntersect","rectToAABB","getRectangleCorners","rect","position","width","height","rotationDeg","halfWidth","halfHeight","center","rotationRad","cos","sin","corner","getAxes","cornersA","cornersB","axes","i","p1","p2","edge","subtract","normal","normalize","projectShapeOntoAxis","corners","axis","min","dot","max","projection","projectionA","projectionB","checkShapeCollision","shapeA","shapeB","shapeAType","shapeBType","circleCircleCollision","circlePointCollision","circleRectCollision","rectRectCollision","rectPointCollision","BruteForceCollisionDetector","entities","BruteForceCollisionDetectorImpl","BaseCollisionDetector","shape","collisions","entity","checkShapeCollision","SpatialGridCollisionDetector","entities","cellSize","SpatialGridCollisionDetectorImpl","BaseCollisionDetector","config","shape","aabb","cells","potentialCollisions","cellId","entitiesInCell","entity","collisions","checkShapeCollision","minCellX","minCellY","positionToCell","maxCellX","maxCellY","index","x","y","cellToId","shapeType","circleToAABB","rectToAABB","applyStyle","ctx","style","restoreContext","renderCircle","circle","renderPoint","point","pointSize","renderRectangle","rectangle","rotationRad","halfWidth","halfHeight","renderShape","shape","renderShapes","shapes","options","mergedStyle","VERSION","Renderer","GameLoop","Input"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/collision/CollisionDetector.ts","../src/collision/shapeCollisions/circleCircle/index.ts","../src/collision/shapeCollisions/circlePoint/index.ts","../src/collision/shapeCollisions/circleRect/index.ts","../src/collision/shapeCollisions/rectPoint/index.ts","../src/collision/shapeCollisions/rectRect/index.ts","../src/collision/shapeCollisions/index.ts","../src/collision/BruteForceCollisionDetector.ts","../src/collision/SpatialGridCollisionDetector.ts","../src/camera/Camera.ts"],"sourcesContent":["/**\n * InfernoJS\n * A spicy, high-performance library for building 2D games in JavaScript\n *\n * @packageDocumentation\n */\n\n// ----- Collision Detection System -----\n// Export types\nexport * from \"./types\";\n\n// Export utility functions\nexport * from \"./utils\";\n\n// Export collision detection system\nexport * from \"./collision\";\n\n// Export camera system\nexport * from \"./camera\";\n\n// ----- Library Information -----\nexport const VERSION = \"0.0.1\";\n\n// ----- Future modules (placeholders) -----\n// These will be implemented in future versions\n\n/**\n * @internal\n * Future game loop management API\n */\nexport const GameLoop = {\n // Placeholder for future game loop module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Game loop management will be added in a future version\",\n};\n\n/**\n * @internal\n * Future input handling API\n */\nexport const Input = {\n // Placeholder for future input handling module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Input handling will be added in a future version\",\n};\n","import { Vector2D, AABB, Circle, Rectangle } from \"./types\";\n\n/**\n * Create a new vector\n */\nexport function vec2(x: number, y: number): Vector2D {\n return { x, y };\n}\n\n/**\n * Vector addition\n */\nexport function add(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x + b.x, y: a.y + b.y };\n}\n\n/**\n * Vector subtraction\n */\nexport function subtract(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x - b.x, y: a.y - b.y };\n}\n\n/**\n * Vector scaling\n */\nexport function scale(v: Vector2D, scalar: number): Vector2D {\n return { x: v.x * scalar, y: v.y * scalar };\n}\n\n/**\n * Dot product of two vectors\n */\nexport function dot(a: Vector2D, b: Vector2D): number {\n return a.x * b.x + a.y * b.y;\n}\n\n/**\n * Cross product magnitude of two 2D vectors\n */\nexport function cross(a: Vector2D, b: Vector2D): number {\n return a.x * b.y - a.y * b.x;\n}\n\n/**\n * Get squared length of a vector (avoids sqrt for performance)\n */\nexport function lengthSquared(v: Vector2D): number {\n return v.x * v.x + v.y * v.y;\n}\n\n/**\n * Get vector length\n */\nexport function length(v: Vector2D): number {\n return Math.sqrt(lengthSquared(v));\n}\n\n/**\n * Normalize a vector (make it unit length)\n */\nexport function normalize(v: Vector2D): Vector2D {\n const len = length(v);\n // Avoid division by zero\n if (len < 1e-10) return { x: 0, y: 0 };\n return { x: v.x / len, y: v.y / len };\n}\n\n/**\n * Distance squared between two points (avoids sqrt for performance)\n */\nexport function distanceSquared(a: Vector2D, b: Vector2D): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Distance between two points\n */\nexport function distance(a: Vector2D, b: Vector2D): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n\n/**\n * Create an AABB from a rectangle\n */\nexport function rectToAABB(rect: Rectangle): AABB {\n return {\n min: { x: rect.position.x, y: rect.position.y },\n max: { x: rect.position.x + rect.width, y: rect.position.y + rect.height },\n };\n}\n\n/**\n * Create an AABB from a circle\n */\nexport function circleToAABB(circle: Circle): AABB {\n return {\n min: {\n x: circle.center.x - circle.radius,\n y: circle.center.y - circle.radius,\n },\n max: {\n x: circle.center.x + circle.radius,\n y: circle.center.y + circle.radius,\n },\n };\n}\n\n/**\n * Check if two AABBs intersect\n */\nexport function aabbIntersect(a: AABB, b: AABB): boolean {\n // Exit with no intersection if separated along an axis\n if (a.max.x < b.min.x || a.min.x > b.max.x) return false;\n if (a.max.y < b.min.y || a.min.y > b.max.y) return false;\n\n // Overlapping on all axes means AABBs are intersecting\n return true;\n}\n\n/**\n * Calculate cell indices for a position in a spatial grid\n */\nexport function positionToCell(\n position: Vector2D,\n cellSize: number,\n): [number, number] {\n return [Math.floor(position.x / cellSize), Math.floor(position.y / cellSize)];\n}\n\n/**\n * Calculate a unique cell ID from grid coordinates\n * Uses a spatial hashing function to convert 2D coordinates to 1D\n */\nexport function cellToId(x: number, y: number): number {\n // Cantor pairing function - maps two non-negative integers to a unique non-negative integer\n return ((x + y) * (x + y + 1)) / 2 + y;\n}\n","import { Entity, Shape } from \"../types\";\n\n/**\n * Base class for collision detectors that implements common functionality\n */\nexport abstract class BaseCollisionDetector {\n /**\n * The entities to check collisions against\n */\n protected entities: Entity[];\n\n /**\n * Create a new collision detector\n * @param entities The entities to check collisions against\n */\n constructor(entities: Entity[]) {\n this.entities = entities;\n }\n\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entities that collide with the shape\n */\n abstract getCollisions(shape: Shape): Entity[];\n}\n","import { Circle, CollisionResult } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between two circles\n * @param circleA First circle\n * @param circleB Second circle\n * @returns Collision result with contact information\n */\nexport function circleCircleCollision(\n circleA: Circle,\n circleB: Circle,\n): CollisionResult {\n const distSquared = distanceSquared(circleA.center, circleB.center);\n\n const radiusSum = circleA.radius + circleB.radius;\n\n const colliding = distSquared <= radiusSum * radiusSum;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Point } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a point\n * @param circle Circle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function circlePointCollision(\n circle: Circle,\n point: Point,\n): CollisionResult {\n const distSquared = distanceSquared(circle.center, point.position);\n\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Rectangle } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a rectangle\n * @param circle Circle\n * @param rect Rectangle\n * @returns Collision result with contact information\n */\nexport function circleRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return circleRotatedRectCollision(circle, rect);\n }\n\n // Find the closest point on the rectangle to the circle center\n const closestX = Math.max(\n rect.position.x,\n Math.min(circle.center.x, rect.position.x + rect.width),\n );\n const closestY = Math.max(\n rect.position.y,\n Math.min(circle.center.y, rect.position.y + rect.height),\n );\n\n const distSquared = distanceSquared(circle.center, {\n x: closestX,\n y: closestY,\n });\n\n // Check if the circle is colliding with the rectangle\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a circle and a rotated rectangle\n * @param circle Circle\n * @param rect Rotated rectangle\n * @returns Collision result with contact information\n */\nfunction circleRotatedRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Translate circle center to rectangle's local space (unrotated)\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to circle center\n const dx = circle.center.x - rectCenterX;\n const dy = circle.center.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a circle in the rectangle's local space\n const localCircle: Circle = {\n type: \"circle\",\n center: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n radius: circle.radius,\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: rect.position,\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return circleRectCollision(localCircle, localRect);\n}\n","import { CollisionResult, Point, Rectangle } from \"../../../types\";\n\n/**\n * Detects collision between a rectangle and a point\n * @param rect Rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function rectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return rotatedRectPointCollision(rect, point);\n }\n\n // Check if the point is inside the rectangle\n const colliding =\n point.position.x >= rect.position.x &&\n point.position.x <= rect.position.x + rect.width &&\n point.position.y >= rect.position.y &&\n point.position.y <= rect.position.y + rect.height;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a rotated rectangle and a point\n * @param rect Rotated rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nfunction rotatedRectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Calculate rectangle center\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to point\n const dx = point.position.x - rectCenterX;\n const dy = point.position.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a point in the rectangle's local space\n const localPoint: Point = {\n type: \"point\",\n position: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: {\n x: rect.position.x,\n y: rect.position.y,\n },\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return rectPointCollision(localRect, localPoint);\n}\n","import { Rectangle, CollisionResult, Vector2D } from \"../../../types\";\nimport {\n subtract,\n normalize,\n dot,\n rectToAABB,\n aabbIntersect,\n} from \"../../../utils\";\n\n/**\n * Detect collision between two rectangles\n * This implementation handles rotated rectangles using the Separating Axis Theorem (SAT)\n */\nexport function rectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // If both rectangles have zero rotation, use a simpler AABB check\n if (rectA.rotationDeg === 0 && rectB.rotationDeg === 0) {\n return aabbRectRectCollision(rectA, rectB);\n }\n\n // For rotated rectangles, use Separating Axis Theorem (SAT)\n return satRectRectCollision(rectA, rectB);\n}\n\n/**\n * Collision detection for axis-aligned (non-rotated) rectangles\n */\nfunction aabbRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n return {\n colliding: aabbIntersect(rectToAABB(rectA), rectToAABB(rectB)),\n };\n}\n\n/**\n * Get the corners of a rotated rectangle\n */\nfunction getRectangleCorners(rect: Rectangle): Vector2D[] {\n const { position, width, height, rotationDeg } = rect;\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n\n // Calculate center of the rectangle\n const center = {\n x: position.x + halfWidth,\n y: position.y + halfHeight,\n };\n\n // Convert rotation to radians\n const rotationRad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rotationRad);\n const sin = Math.sin(rotationRad);\n\n // Calculate corners relative to center\n const corners: Vector2D[] = [\n { x: -halfWidth, y: -halfHeight }, // Top-left\n { x: halfWidth, y: -halfHeight }, // Top-right\n { x: halfWidth, y: halfHeight }, // Bottom-right\n { x: -halfWidth, y: halfHeight }, // Bottom-left\n ];\n\n // Rotate and translate corners\n return corners.map((corner) => ({\n x: center.x + (corner.x * cos - corner.y * sin),\n y: center.y + (corner.x * sin + corner.y * cos),\n }));\n}\n\n/**\n * Get the axes to test for the SAT algorithm\n */\nfunction getAxes(cornersA: Vector2D[], cornersB: Vector2D[]): Vector2D[] {\n const axes: Vector2D[] = [];\n\n // Add the normals of each edge of the first rectangle\n for (let i = 0; i < cornersA.length; i++) {\n const p1 = cornersA[i];\n const p2 = cornersA[(i + 1) % cornersA.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n // Add the normals of each edge of the second rectangle\n for (let i = 0; i < cornersB.length; i++) {\n const p1 = cornersB[i];\n const p2 = cornersB[(i + 1) % cornersB.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n return axes;\n}\n\n/**\n * Project a shape onto an axis\n */\nfunction projectShapeOntoAxis(\n corners: Vector2D[],\n axis: Vector2D,\n): { min: number; max: number } {\n let min = dot(corners[0], axis);\n let max = min;\n\n for (let i = 1; i < corners.length; i++) {\n const projection = dot(corners[i], axis);\n if (projection < min) min = projection;\n if (projection > max) max = projection;\n }\n\n return { min, max };\n}\n\n/**\n * Collision detection for rotated rectangles using Separating Axis Theorem (SAT)\n */\nfunction satRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // Get corners of both rectangles\n const cornersA = getRectangleCorners(rectA);\n const cornersB = getRectangleCorners(rectB);\n\n // Get axes to test\n const axes = getAxes(cornersA, cornersB);\n\n // Test each axis\n for (const axis of axes) {\n const projectionA = projectShapeOntoAxis(cornersA, axis);\n const projectionB = projectShapeOntoAxis(cornersB, axis);\n\n // Check for separation\n if (\n projectionA.max < projectionB.min ||\n projectionB.max < projectionA.min\n ) {\n // Shapes are separated along this axis\n return { colliding: false };\n }\n }\n\n // If we get here, the shapes are colliding\n return { colliding: true };\n}\n","import { Circle, Point, Rectangle, Shape } from \"../../types\";\nimport { circleCircleCollision } from \"./circleCircle\";\nimport { circlePointCollision } from \"./circlePoint\";\nimport { circleRectCollision } from \"./circleRect\";\nimport { rectPointCollision } from \"./rectPoint\";\nimport { rectRectCollision } from \"./rectRect\";\n\n/**\n * Check if two entities are colliding\n * @param shapeA First shape\n * @param shapeB Second shape\n * @returns True if the shapes are colliding\n */\nexport function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean {\n const shapeAType = shapeA.type;\n const shapeBType = shapeB.type;\n\n // Circle vs Circle\n if (shapeAType === \"circle\" && shapeBType === \"circle\") {\n return circleCircleCollision(shapeA as Circle, shapeB as Circle).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"point\") {\n return circlePointCollision(shapeA as Circle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"circle\") {\n return circlePointCollision(shapeB as Circle, shapeA as Point).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"rectangle\") {\n return circleRectCollision(shapeA as Circle, shapeB as Rectangle).colliding;\n }\n if (shapeAType === \"rectangle\" && shapeBType === \"circle\") {\n return circleRectCollision(shapeB as Circle, shapeA as Rectangle).colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"rectangle\") {\n return rectRectCollision(shapeA as Rectangle, shapeB as Rectangle)\n .colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"point\") {\n return rectPointCollision(shapeA as Rectangle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"rectangle\") {\n return rectPointCollision(shapeB as Rectangle, shapeA as Point).colliding;\n }\n\n return false;\n}\n","import { Entity, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions\";\n\n/**\n * Collision detector that uses a brute force approach\n * Checks every entity against the given shape\n *\n * Best used when:\n * - You have a small number of entities\n * - You only need to check collisions occasionally\n * - You want the simplest implementation\n */\nexport function BruteForceCollisionDetector({\n entities,\n}: {\n entities: Entity[];\n}) {\n return new BruteForceCollisionDetectorImpl(entities);\n}\n\nclass BruteForceCollisionDetectorImpl extends BaseCollisionDetector {\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entities that collide with the shape\n */\n getCollisions(shape: Shape): Entity[] {\n const collisions: Entity[] = [];\n\n // Check each entity against the given shape\n for (const entity of this.entities) {\n // Check if the shapes collide\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity);\n }\n }\n\n return collisions;\n }\n}\n","import { AABB, Circle, Entity, Point, Rectangle, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions\";\nimport { cellToId, circleToAABB, positionToCell, rectToAABB } from \"../utils\";\n\ntype CellId = number;\n\n/**\n * Configuration options for the spatial grid collision detector\n */\nexport interface SpatialGridConfig {\n /** Size of each grid cell */\n cellSize: number;\n}\n\n/**\n * Collision detector that uses spatial partitioning for efficient collision detection\n *\n * Best used when:\n * - You have a large number of entities\n * - Entities are distributed across the space\n * - You need to check collisions frequently\n */\nexport function SpatialGridCollisionDetector({\n entities,\n cellSize = 64,\n}: {\n entities: Entity[];\n cellSize?: number;\n}) {\n return new SpatialGridCollisionDetectorImpl(entities, { cellSize });\n}\n\nclass SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {\n /** The spatial hash grid for quick lookups */\n private grid: Map<CellId, Entity[]>;\n /** Configuration for the spatial grid */\n private config: SpatialGridConfig;\n\n /**\n * Create a new spatial grid collision detector\n */\n constructor(entities: Entity[], config: SpatialGridConfig) {\n super(entities);\n this.config = config;\n this.grid = new Map();\n\n // Initialize the grid with entities\n this.rebuildGrid();\n }\n\n /**\n * Get all entities that collide with the given shape\n */\n getCollisions(shape: Shape): Entity[] {\n // Get the AABB for the shape\n const aabb = this.getAABBForShape(shape);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Get all entities in those cells\n const potentialCollisions = new Set<Entity>();\n\n for (const cellId of cells) {\n const entitiesInCell = this.grid.get(cellId);\n if (entitiesInCell) {\n for (const entity of entitiesInCell) {\n potentialCollisions.add(entity);\n }\n }\n }\n\n // Check for actual collisions\n const collisions: Entity[] = [];\n\n for (const entity of potentialCollisions) {\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity);\n }\n }\n\n return collisions;\n }\n\n /**\n * Rebuild the spatial grid with the current entities\n */\n rebuildGrid(): void {\n // Clear the grid\n this.grid.clear();\n\n // Add all entities to the grid\n for (const entity of this.entities) {\n this.insertEntityIntoGrid(entity);\n }\n }\n\n /**\n * Insert an entity into the spatial grid\n */\n private insertEntityIntoGrid(entity: Entity): void {\n // Get the AABB for the entity\n const aabb = this.getAABB(entity);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Add the entity to each cell\n for (const cellId of cells) {\n if (!this.grid.has(cellId)) {\n this.grid.set(cellId, []);\n }\n\n this.grid.get(cellId)!.push(entity);\n }\n }\n\n /**\n * Get all cells that an AABB overlaps\n */\n private getCellsForAABB(aabb: AABB): CellId[] {\n const { cellSize } = this.config;\n\n // Calculate the min and max cell coordinates\n const [minCellX, minCellY] = positionToCell(aabb.min, cellSize);\n const [maxCellX, maxCellY] = positionToCell(aabb.max, cellSize);\n\n // Get all cells in the range\n const cells: CellId[] = new Array(\n (maxCellX - minCellX + 1) * (maxCellY - minCellY + 1),\n );\n let index = 0;\n\n for (let x = minCellX; x <= maxCellX; x++) {\n for (let y = minCellY; y <= maxCellY; y++) {\n cells[index++] = cellToId(x, y);\n }\n }\n\n return cells;\n }\n\n /**\n * Get the AABB for an entity\n */\n private getAABB(entity: Entity): AABB {\n return this.getAABBForShape(entity.shape);\n }\n\n /**\n * Get the AABB for a shape\n */\n private getAABBForShape(shape: Shape): AABB {\n const shapeType = shape.type;\n\n if (shapeType === \"circle\") {\n return circleToAABB(shape as Circle);\n } else if (shapeType === \"rectangle\") {\n return rectToAABB(shape as Rectangle);\n } else if (shapeType === \"point\") {\n // For a point, create a tiny AABB\n return {\n min: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n max: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n };\n }\n\n throw new Error(`Unsupported shape type: ${shapeType}`);\n }\n}\n","import { Vector2D, AABB, Point, Circle, Rectangle } from \"../types\";\n\n/**\n * Configuration for the camera\n */\nexport type CameraConfig = {\n /** Center position of the camera in world coordinates */\n position: Vector2D;\n /** Width of the viewport/canvas in pixels */\n width: number;\n /** Height of the viewport/canvas in pixels */\n height: number;\n /** Rotation in degrees (optional, defaults to 0) */\n rotation?: number;\n};\n\n/**\n * Converts a global coordinate object into a local camera coordinate system.\n */\nexport function globalCoordinatesToCameraCoordinates(\n global: Vector2D,\n camera: CameraConfig,\n): Vector2D;\nexport function globalCoordinatesToCameraCoordinates(\n global: Point,\n camera: CameraConfig,\n): Point;\nexport function globalCoordinatesToCameraCoordinates(\n global: Circle,\n camera: CameraConfig,\n): Circle;\nexport function globalCoordinatesToCameraCoordinates(\n global: Rectangle,\n camera: CameraConfig,\n): Rectangle;\nexport function globalCoordinatesToCameraCoordinates(\n global: AABB,\n camera: CameraConfig,\n): AABB;\nexport function globalCoordinatesToCameraCoordinates(\n global: Vector2D | Point | Circle | Rectangle | AABB,\n camera: CameraConfig,\n): Vector2D | Point | Circle | Rectangle | AABB {\n // Helper to transform a single point\n const transformPoint = (p: Vector2D): Vector2D => {\n // 1. Translate relative to camera position\n // If camera is at (100,100), world point (150,150) becomes (50,50)\n let x = p.x - camera.position.x;\n let y = p.y - camera.position.y;\n\n // 2. Rotate if needed\n if (camera.rotation) {\n const rad = (camera.rotation * Math.PI) / 180;\n // We rotate by -camera.rotation to bring world into camera space\n // If camera rotates +90 (clockwise), world appears to rotate -90 (counter-clockwise)\n const invRad = -rad;\n const cos = Math.cos(invRad);\n const sin = Math.sin(invRad);\n\n const rx = x * cos - y * sin;\n const ry = x * sin + y * cos;\n x = rx;\n y = ry;\n }\n\n // 3. Offset to viewport center\n // Camera position corresponds to the center of the screen\n x += camera.width / 2;\n y += camera.height / 2;\n\n return { x, y };\n };\n\n // Determine type and transform accordingly\n\n // Check for Shapes with 'type' property\n if (\"type\" in global) {\n if (global.type === \"point\") {\n return {\n ...global,\n position: transformPoint(global.position),\n };\n }\n\n if (global.type === \"circle\") {\n return {\n ...global,\n center: transformPoint(global.center),\n };\n }\n\n if (global.type === \"rectangle\") {\n return {\n ...global,\n position: transformPoint(global.position),\n // Rotation relative to camera\n rotationDeg: global.rotationDeg - (camera.rotation ?? 0),\n };\n }\n }\n\n // Check for AABB (min/max)\n if (\"min\" in global && \"max\" in global) {\n // For AABB, we must transform all corners and find the new AABB\n // because rotation might make it not axis-aligned in the original sense,\n // but the return type must be AABB.\n\n // If there is no rotation, we can just transform min/max\n if (!camera.rotation) {\n return {\n min: transformPoint(global.min),\n max: transformPoint(global.max),\n };\n }\n\n // With rotation, we need to transform all 4 corners\n const corners = [\n { x: global.min.x, y: global.min.y },\n { x: global.max.x, y: global.min.y },\n { x: global.max.x, y: global.max.y },\n { x: global.min.x, y: global.max.y },\n ];\n\n const transformed = corners.map(transformPoint);\n\n let minX = Infinity,\n minY = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity;\n\n for (const p of transformed) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n\n return {\n min: { x: minX, y: minY },\n max: { x: maxX, y: maxY },\n };\n }\n\n // Check for Vector2D (x, y)\n // We check this last or ensure the other checks are exhaustive for types that might structurally overlap\n if (\"x\" in global && \"y\" in global) {\n return transformPoint(global);\n }\n\n throw new Error(\n \"Unsupported type passed to globalCoordinatesToCameraCoordinates\",\n );\n}\n"],"mappings":"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","names":["index_exports","__export","BaseCollisionDetector","BruteForceCollisionDetector","GameLoop","Input","SpatialGridCollisionDetector","VERSION","aabbIntersect","add","cellToId","checkShapeCollision","circleToAABB","cross","distance","distanceSquared","dot","globalCoordinatesToCameraCoordinates","length","lengthSquared","normalize","positionToCell","rectToAABB","scale","subtract","vec2","__toCommonJS","vec2","x","y","add","a","b","subtract","scale","v","scalar","dot","cross","lengthSquared","length","normalize","len","distanceSquared","dx","dy","distance","rectToAABB","rect","circleToAABB","circle","aabbIntersect","positionToCell","position","cellSize","cellToId","BaseCollisionDetector","entities","circleCircleCollision","circleA","circleB","distSquared","distanceSquared","radiusSum","circlePointCollision","circle","point","distanceSquared","circleRectCollision","circle","rect","circleRotatedRectCollision","closestX","closestY","distanceSquared","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localCircle","localRect","rectPointCollision","rect","point","rotatedRectPointCollision","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localPoint","localRect","rectRectCollision","rectA","rectB","aabbRectRectCollision","satRectRectCollision","aabbIntersect","rectToAABB","getRectangleCorners","rect","position","width","height","rotationDeg","halfWidth","halfHeight","center","rotationRad","cos","sin","corner","getAxes","cornersA","cornersB","axes","p1","p2","edge","subtract","normal","normalize","projectShapeOntoAxis","corners","axis","min","dot","max","i","projection","projectionA","projectionB","checkShapeCollision","shapeA","shapeB","shapeAType","shapeBType","circleCircleCollision","circlePointCollision","circleRectCollision","rectRectCollision","rectPointCollision","BruteForceCollisionDetector","entities","BruteForceCollisionDetectorImpl","BaseCollisionDetector","shape","collisions","entity","checkShapeCollision","SpatialGridCollisionDetector","entities","cellSize","SpatialGridCollisionDetectorImpl","BaseCollisionDetector","config","shape","aabb","cells","potentialCollisions","cellId","entitiesInCell","entity","collisions","checkShapeCollision","minCellX","minCellY","positionToCell","maxCellX","maxCellY","index","x","y","cellToId","shapeType","circleToAABB","rectToAABB","globalCoordinatesToCameraCoordinates","global","camera","transformPoint","p","x","y","invRad","cos","sin","rx","ry","transformed","minX","minY","maxX","maxY","VERSION","GameLoop","Input"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- function L(t,o){return{x:t,y:o}}function N(t,o){return{x:t.x+o.x,y:t.y+o.y}}function y(t,o){return{x:t.x-o.x,y:t.y-o.y}}function $(t,o){return{x:t.x*o,y:t.y*o}}function R(t,o){return t.x*o.x+t.y*o.y}function J(t,o){return t.x*o.y-t.y*o.x}function T(t){return t.x*t.x+t.y*t.y}function W(t){return Math.sqrt(T(t))}function S(t){let o=W(t);return o<1e-10?{x:0,y:0}:{x:t.x/o,y:t.y/o}}function f(t,o){let e=o.x-t.x,n=o.y-t.y;return e*e+n*n}function K(t,o){return Math.sqrt(f(t,o))}function h(t){return{min:{x:t.position.x,y:t.position.y},max:{x:t.position.x+t.width,y:t.position.y+t.height}}}function V(t){return{min:{x:t.center.x-t.radius,y:t.center.y-t.radius},max:{x:t.center.x+t.radius,y:t.center.y+t.radius}}}function k(t,o){return!(t.max.x<o.min.x||t.min.x>o.max.x||t.max.y<o.min.y||t.min.y>o.max.y)}function D(t,o){return[Math.floor(t.x/o),Math.floor(t.y/o)]}function I(t,o){return(t+o)*(t+o+1)/2+o}var p=class{constructor(o){this.entities=o}};function M(t,o){let e=f(t.center,o.center),n=t.radius+o.radius;return{colliding:e<=n*n}}function b(t,o){return{colliding:f(t.center,o.position)<=t.radius*t.radius}}function m(t,o){if(o.rotationDeg!==0)return O(t,o);let e=Math.max(o.position.x,Math.min(t.center.x,o.position.x+o.width)),n=Math.max(o.position.y,Math.min(t.center.y,o.position.y+o.height));return{colliding:f(t.center,{x:e,y:n})<=t.radius*t.radius}}function O(t,o){let e=o.rotationDeg*Math.PI/180,n=Math.cos(-e),i=Math.sin(-e),r=o.position.x+o.width/2,s=o.position.y+o.height/2,l=t.center.x-r,a=t.center.y-s,c=n*l-i*a,d=i*l+n*a,g={type:"circle",center:{x:c+r,y:d+s},radius:t.radius},u={type:"rectangle",position:o.position,width:o.width,height:o.height,rotationDeg:0};return m(g,u)}function x(t,o){return t.rotationDeg!==0?X(t,o):{colliding:o.position.x>=t.position.x&&o.position.x<=t.position.x+t.width&&o.position.y>=t.position.y&&o.position.y<=t.position.y+t.height}}function X(t,o){let e=t.rotationDeg*Math.PI/180,n=Math.cos(-e),i=Math.sin(-e),r=t.position.x+t.width/2,s=t.position.y+t.height/2,l=o.position.x-r,a=o.position.y-s,c=n*l-i*a,d=i*l+n*a,g={type:"point",position:{x:c+r,y:d+s}},u={type:"rectangle",position:{x:t.position.x,y:t.position.y},width:t.width,height:t.height,rotationDeg:0};return x(u,g)}function q(t,o){return t.rotationDeg===0&&o.rotationDeg===0?Y(t,o):F(t,o)}function Y(t,o){return{colliding:k(h(t),h(o))}}function E(t){let{position:o,width:e,height:n,rotationDeg:i}=t,r=e/2,s=n/2,l={x:o.x+r,y:o.y+s},a=i*Math.PI/180,c=Math.cos(a),d=Math.sin(a);return[{x:-r,y:-s},{x:r,y:-s},{x:r,y:s},{x:-r,y:s}].map(u=>({x:l.x+(u.x*c-u.y*d),y:l.y+(u.x*d+u.y*c)}))}function G(t,o){let e=[];for(let n=0;n<t.length;n++){let i=t[n],r=t[(n+1)%t.length],s=y(r,i),l=S({x:-s.y,y:s.x});e.push(l)}for(let n=0;n<o.length;n++){let i=o[n],r=o[(n+1)%o.length],s=y(r,i),l=S({x:-s.y,y:s.x});e.push(l)}return e}function v(t,o){let e=R(t[0],o),n=e;for(let i=1;i<t.length;i++){let r=R(t[i],o);r<e&&(e=r),r>n&&(n=r)}return{min:e,max:n}}function F(t,o){let e=E(t),n=E(o),i=G(e,n);for(let r of i){let s=v(e,r),l=v(n,r);if(s.max<l.min||l.max<s.min)return{colliding:!1}}return{colliding:!0}}function C(t,o){let e=t.type,n=o.type;return e==="circle"&&n==="circle"?M(t,o).colliding:e==="circle"&&n==="point"?b(t,o).colliding:e==="point"&&n==="circle"?b(o,t).colliding:e==="circle"&&n==="rectangle"?m(t,o).colliding:e==="rectangle"&&n==="circle"?m(o,t).colliding:e==="rectangle"&&n==="rectangle"?q(t,o).colliding:e==="rectangle"&&n==="point"?x(t,o).colliding:e==="point"&&n==="rectangle"?x(o,t).colliding:!1}function mt({entities:t}){return new w(t)}var w=class extends p{getCollisions(o){let e=[];for(let n of this.entities)C(o,n.shape)&&e.push(n.id);return e}};function St({entities:t,cellSize:o=64}){return new B(t,{cellSize:o})}var B=class extends p{constructor(o,e){super(o),this.config=e,this.grid=new Map,this.rebuildGrid()}getCollisions(o){let e=this.getAABBForShape(o),n=this.getCellsForAABB(e),i=new Set;for(let s of n){let l=this.grid.get(s);if(l)for(let a of l)i.add(a)}let r=[];for(let s of i)C(o,s.shape)&&r.push(s.id);return r}rebuildGrid(){this.grid.clear();for(let o of this.entities)this.insertEntityIntoGrid(o)}insertEntityIntoGrid(o){let e=this.getAABB(o),n=this.getCellsForAABB(e);for(let i of n)this.grid.has(i)||this.grid.set(i,[]),this.grid.get(i).push(o)}getCellsForAABB(o){let{cellSize:e}=this.config,[n,i]=D(o.min,e),[r,s]=D(o.max,e),l=new Array((r-n+1)*(s-i+1)),a=0;for(let c=n;c<=r;c++)for(let d=i;d<=s;d++)l[a++]=I(c,d);return l}getAABB(o){return this.getAABBForShape(o.shape)}getAABBForShape(o){let e=o.type;if(e==="circle")return V(o);if(e==="rectangle")return h(o);if(e==="point")return{min:{x:o.position.x,y:o.position.y},max:{x:o.position.x,y:o.position.y}};throw new Error(`Unsupported shape type: ${e}`)}};function A(t,o){t.save(),o.fillColor&&(t.fillStyle=o.fillColor),o.strokeColor&&(t.strokeStyle=o.strokeColor),o.strokeWidth!==void 0&&(t.lineWidth=o.strokeWidth),o.alpha!==void 0&&(t.globalAlpha=o.alpha),o.shadowBlur!==void 0&&(t.shadowBlur=o.shadowBlur),o.shadowColor&&(t.shadowColor=o.shadowColor),o.shadowOffsetX!==void 0&&(t.shadowOffsetX=o.shadowOffsetX),o.shadowOffsetY!==void 0&&(t.shadowOffsetY=o.shadowOffsetY)}function P(t){t.restore()}function z(t,o,e){A(t,e),t.beginPath(),t.arc(o.center.x,o.center.y,o.radius,0,Math.PI*2),e.fillColor&&t.fill(),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.stroke(),P(t)}function j(t,o,e){let n=e.strokeWidth||4;A(t,e),t.beginPath(),t.arc(o.position.x,o.position.y,n/2,0,Math.PI*2),e.fillColor&&t.fill(),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.stroke(),P(t)}function H(t,o,e){if(A(t,e),t.save(),t.translate(o.position.x,o.position.y),o.rotationDeg!==0){let r=o.rotationDeg*Math.PI/180;t.rotate(r)}let n=o.width/2,i=o.height/2;e.fillColor&&t.fillRect(-n,-i,o.width,o.height),e.strokeColor&&e.strokeWidth&&e.strokeWidth>0&&t.strokeRect(-n,-i,o.width,o.height),t.restore(),P(t)}function U(t,o,e){switch(o.type){case"circle":z(t,o,e);break;case"rectangle":H(t,o,e);break;case"point":j(t,o,e);break;default:console.warn("Unknown shape type encountered");break}}function Vt(t,o,e){for(let{shape:n,style:i}of o){let r={...e.defaultStyle,...i};U(t,n,r)}}var It="0.0.1",Mt={notImplemented:!1,info:"Basic rendering capabilities are now available"},Et={notImplemented:!0,info:"Game loop management will be added in a future version"},vt={notImplemented:!0,info:"Input handling will be added in a future version"};export{p as BaseCollisionDetector,mt as BruteForceCollisionDetector,Et as GameLoop,vt as Input,Mt as Renderer,St as SpatialGridCollisionDetector,It as VERSION,k as aabbIntersect,N as add,A as applyStyle,I as cellToId,C as checkShapeCollision,V as circleToAABB,J as cross,K as distance,f as distanceSquared,R as dot,W as length,T as lengthSquared,S as normalize,D as positionToCell,h as rectToAABB,z as renderCircle,j as renderPoint,H as renderRectangle,U as renderShape,Vt as renderShapes,P as restoreContext,$ as scale,y as subtract,L as vec2};
1
+ function z(t,o){return{x:t,y:o}}function k(t,o){return{x:t.x+o.x,y:t.y+o.y}}function h(t,o){return{x:t.x-o.x,y:t.y-o.y}}function O(t,o){return{x:t.x*o,y:t.y*o}}function R(t,o){return t.x*o.x+t.y*o.y}function U(t,o){return t.x*o.y-t.y*o.x}function q(t){return t.x*t.x+t.y*t.y}function G(t){return Math.sqrt(q(t))}function D(t){let o=G(t);return o<1e-10?{x:0,y:0}:{x:t.x/o,y:t.y/o}}function x(t,o){let n=o.x-t.x,i=o.y-t.y;return n*n+i*i}function H(t,o){return Math.sqrt(x(t,o))}function d(t){return{min:{x:t.position.x,y:t.position.y},max:{x:t.position.x+t.width,y:t.position.y+t.height}}}function P(t){return{min:{x:t.center.x-t.radius,y:t.center.y-t.radius},max:{x:t.center.x+t.radius,y:t.center.y+t.radius}}}function M(t,o){return!(t.max.x<o.min.x||t.min.x>o.max.x||t.max.y<o.min.y||t.min.y>o.max.y)}function A(t,o){return[Math.floor(t.x/o),Math.floor(t.y/o)]}function I(t,o){return(t+o)*(t+o+1)/2+o}var f=class{constructor(o){this.entities=o}};function w(t,o){let n=x(t.center,o.center),i=t.radius+o.radius;return{colliding:n<=i*i}}function B(t,o){return{colliding:x(t.center,o.position)<=t.radius*t.radius}}function y(t,o){if(o.rotationDeg!==0)return X(t,o);let n=Math.max(o.position.x,Math.min(t.center.x,o.position.x+o.width)),i=Math.max(o.position.y,Math.min(t.center.y,o.position.y+o.height));return{colliding:x(t.center,{x:n,y:i})<=t.radius*t.radius}}function X(t,o){let n=o.rotationDeg*Math.PI/180,i=Math.cos(-n),r=Math.sin(-n),e=o.position.x+o.width/2,s=o.position.y+o.height/2,c=t.center.x-e,l=t.center.y-s,a=i*c-r*l,u=r*c+i*l,m={type:"circle",center:{x:a+e,y:u+s},radius:t.radius},p={type:"rectangle",position:o.position,width:o.width,height:o.height,rotationDeg:0};return y(m,p)}function C(t,o){return t.rotationDeg!==0?Y(t,o):{colliding:o.position.x>=t.position.x&&o.position.x<=t.position.x+t.width&&o.position.y>=t.position.y&&o.position.y<=t.position.y+t.height}}function Y(t,o){let n=t.rotationDeg*Math.PI/180,i=Math.cos(-n),r=Math.sin(-n),e=t.position.x+t.width/2,s=t.position.y+t.height/2,c=o.position.x-e,l=o.position.y-s,a=i*c-r*l,u=r*c+i*l,m={type:"point",position:{x:a+e,y:u+s}},p={type:"rectangle",position:{x:t.position.x,y:t.position.y},width:t.width,height:t.height,rotationDeg:0};return C(p,m)}function T(t,o){return t.rotationDeg===0&&o.rotationDeg===0?v(t,o):j(t,o)}function v(t,o){return{colliding:M(d(t),d(o))}}function E(t){let{position:o,width:n,height:i,rotationDeg:r}=t,e=n/2,s=i/2,c={x:o.x+e,y:o.y+s},l=r*Math.PI/180,a=Math.cos(l),u=Math.sin(l);return[{x:-e,y:-s},{x:e,y:-s},{x:e,y:s},{x:-e,y:s}].map(p=>({x:c.x+(p.x*a-p.y*u),y:c.y+(p.x*u+p.y*a)}))}function F(t,o){let n=[];for(let i=0;i<t.length;i++){let r=t[i],e=t[(i+1)%t.length],s=h(e,r),c=D({x:-s.y,y:s.x});n.push(c)}for(let i=0;i<o.length;i++){let r=o[i],e=o[(i+1)%o.length],s=h(e,r),c=D({x:-s.y,y:s.x});n.push(c)}return n}function b(t,o){let n=R(t[0],o),i=n;for(let r=1;r<t.length;r++){let e=R(t[r],o);e<n&&(n=e),e>i&&(i=e)}return{min:n,max:i}}function j(t,o){let n=E(t),i=E(o),r=F(n,i);for(let e of r){let s=b(n,e),c=b(i,e);if(s.max<c.min||c.max<s.min)return{colliding:!1}}return{colliding:!0}}function g(t,o){let n=t.type,i=o.type;return n==="circle"&&i==="circle"?w(t,o).colliding:n==="circle"&&i==="point"?B(t,o).colliding:n==="point"&&i==="circle"?B(o,t).colliding:n==="circle"&&i==="rectangle"?y(t,o).colliding:n==="rectangle"&&i==="circle"?y(o,t).colliding:n==="rectangle"&&i==="rectangle"?T(t,o).colliding:n==="rectangle"&&i==="point"?C(t,o).colliding:n==="point"&&i==="rectangle"?C(o,t).colliding:!1}function ut({entities:t}){return new V(t)}var V=class extends f{getCollisions(o){let n=[];for(let i of this.entities)g(o,i.shape)&&n.push(i);return n}};function dt({entities:t,cellSize:o=64}){return new S(t,{cellSize:o})}var S=class extends f{constructor(o,n){super(o),this.config=n,this.grid=new Map,this.rebuildGrid()}getCollisions(o){let n=this.getAABBForShape(o),i=this.getCellsForAABB(n),r=new Set;for(let s of i){let c=this.grid.get(s);if(c)for(let l of c)r.add(l)}let e=[];for(let s of r)g(o,s.shape)&&e.push(s);return e}rebuildGrid(){this.grid.clear();for(let o of this.entities)this.insertEntityIntoGrid(o)}insertEntityIntoGrid(o){let n=this.getAABB(o),i=this.getCellsForAABB(n);for(let r of i)this.grid.has(r)||this.grid.set(r,[]),this.grid.get(r).push(o)}getCellsForAABB(o){let{cellSize:n}=this.config,[i,r]=A(o.min,n),[e,s]=A(o.max,n),c=new Array((e-i+1)*(s-r+1)),l=0;for(let a=i;a<=e;a++)for(let u=r;u<=s;u++)c[l++]=I(a,u);return c}getAABB(o){return this.getAABBForShape(o.shape)}getAABBForShape(o){let n=o.type;if(n==="circle")return P(o);if(n==="rectangle")return d(o);if(n==="point")return{min:{x:o.position.x,y:o.position.y},max:{x:o.position.x,y:o.position.y}};throw new Error(`Unsupported shape type: ${n}`)}};function At(t,o){let n=i=>{let r=i.x-o.position.x,e=i.y-o.position.y;if(o.rotation){let c=-(o.rotation*Math.PI/180),l=Math.cos(c),a=Math.sin(c),u=r*l-e*a,m=r*a+e*l;r=u,e=m}return r+=o.width/2,e+=o.height/2,{x:r,y:e}};if("type"in t){if(t.type==="point")return{...t,position:n(t.position)};if(t.type==="circle")return{...t,center:n(t.center)};if(t.type==="rectangle")return{...t,position:n(t.position),rotationDeg:t.rotationDeg-(o.rotation??0)}}if("min"in t&&"max"in t){if(!o.rotation)return{min:n(t.min),max:n(t.max)};let r=[{x:t.min.x,y:t.min.y},{x:t.max.x,y:t.min.y},{x:t.max.x,y:t.max.y},{x:t.min.x,y:t.max.y}].map(n),e=1/0,s=1/0,c=-1/0,l=-1/0;for(let a of r)e=Math.min(e,a.x),s=Math.min(s,a.y),c=Math.max(c,a.x),l=Math.max(l,a.y);return{min:{x:e,y:s},max:{x:c,y:l}}}if("x"in t&&"y"in t)return n(t);throw new Error("Unsupported type passed to globalCoordinatesToCameraCoordinates")}var Pt="0.0.1",Mt={notImplemented:!0,info:"Game loop management will be added in a future version"},It={notImplemented:!0,info:"Input handling will be added in a future version"};export{f as BaseCollisionDetector,ut as BruteForceCollisionDetector,Mt as GameLoop,It as Input,dt as SpatialGridCollisionDetector,Pt as VERSION,M as aabbIntersect,k as add,I as cellToId,g as checkShapeCollision,P as circleToAABB,U as cross,H as distance,x as distanceSquared,R as dot,At as globalCoordinatesToCameraCoordinates,G as length,q as lengthSquared,D as normalize,A as positionToCell,d as rectToAABB,O as scale,h as subtract,z as vec2};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/collision/CollisionDetector.ts","../src/collision/shapeCollisions/circleCircle/index.ts","../src/collision/shapeCollisions/circlePoint/index.ts","../src/collision/shapeCollisions/circleRect/index.ts","../src/collision/shapeCollisions/rectPoint/index.ts","../src/collision/shapeCollisions/rectRect/index.ts","../src/collision/shapeCollisions/checkShapeCollision.ts","../src/collision/BruteForceCollisionDetector.ts","../src/collision/SpatialGridCollisionDetector.ts","../src/render/index.ts","../src/index.ts"],"sourcesContent":["import { Vector2D, AABB, Circle, Rectangle } from \"./types\";\n\n/**\n * Create a new vector\n */\nexport function vec2(x: number, y: number): Vector2D {\n return { x, y };\n}\n\n/**\n * Vector addition\n */\nexport function add(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x + b.x, y: a.y + b.y };\n}\n\n/**\n * Vector subtraction\n */\nexport function subtract(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x - b.x, y: a.y - b.y };\n}\n\n/**\n * Vector scaling\n */\nexport function scale(v: Vector2D, scalar: number): Vector2D {\n return { x: v.x * scalar, y: v.y * scalar };\n}\n\n/**\n * Dot product of two vectors\n */\nexport function dot(a: Vector2D, b: Vector2D): number {\n return a.x * b.x + a.y * b.y;\n}\n\n/**\n * Cross product magnitude of two 2D vectors\n */\nexport function cross(a: Vector2D, b: Vector2D): number {\n return a.x * b.y - a.y * b.x;\n}\n\n/**\n * Get squared length of a vector (avoids sqrt for performance)\n */\nexport function lengthSquared(v: Vector2D): number {\n return v.x * v.x + v.y * v.y;\n}\n\n/**\n * Get vector length\n */\nexport function length(v: Vector2D): number {\n return Math.sqrt(lengthSquared(v));\n}\n\n/**\n * Normalize a vector (make it unit length)\n */\nexport function normalize(v: Vector2D): Vector2D {\n const len = length(v);\n // Avoid division by zero\n if (len < 1e-10) return { x: 0, y: 0 };\n return { x: v.x / len, y: v.y / len };\n}\n\n/**\n * Distance squared between two points (avoids sqrt for performance)\n */\nexport function distanceSquared(a: Vector2D, b: Vector2D): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Distance between two points\n */\nexport function distance(a: Vector2D, b: Vector2D): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n\n/**\n * Create an AABB from a rectangle\n */\nexport function rectToAABB(rect: Rectangle): AABB {\n return {\n min: { x: rect.position.x, y: rect.position.y },\n max: { x: rect.position.x + rect.width, y: rect.position.y + rect.height },\n };\n}\n\n/**\n * Create an AABB from a circle\n */\nexport function circleToAABB(circle: Circle): AABB {\n return {\n min: {\n x: circle.center.x - circle.radius,\n y: circle.center.y - circle.radius,\n },\n max: {\n x: circle.center.x + circle.radius,\n y: circle.center.y + circle.radius,\n },\n };\n}\n\n/**\n * Check if two AABBs intersect\n */\nexport function aabbIntersect(a: AABB, b: AABB): boolean {\n // Exit with no intersection if separated along an axis\n if (a.max.x < b.min.x || a.min.x > b.max.x) return false;\n if (a.max.y < b.min.y || a.min.y > b.max.y) return false;\n\n // Overlapping on all axes means AABBs are intersecting\n return true;\n}\n\n/**\n * Calculate cell indices for a position in a spatial grid\n */\nexport function positionToCell(\n position: Vector2D,\n cellSize: number,\n): [number, number] {\n return [Math.floor(position.x / cellSize), Math.floor(position.y / cellSize)];\n}\n\n/**\n * Calculate a unique cell ID from grid coordinates\n * Uses a spatial hashing function to convert 2D coordinates to 1D\n */\nexport function cellToId(x: number, y: number): number {\n // Cantor pairing function - maps two non-negative integers to a unique non-negative integer\n return ((x + y) * (x + y + 1)) / 2 + y;\n}\n","import { Entity, Shape } from \"../types\";\n\n/**\n * Base class for collision detectors that implements common functionality\n */\nexport abstract class BaseCollisionDetector {\n /**\n * The entities to check collisions against\n */\n protected entities: Entity[];\n\n /**\n * Create a new collision detector\n * @param entities The entities to check collisions against\n */\n constructor(entities: Entity[]) {\n this.entities = entities;\n }\n\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entity IDs that collide with the shape\n */\n abstract getCollisions(shape: Shape): (string | number)[];\n}\n","import { Circle, CollisionResult } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between two circles\n * @param circleA First circle\n * @param circleB Second circle\n * @returns Collision result with contact information\n */\nexport function circleCircleCollision(\n circleA: Circle,\n circleB: Circle,\n): CollisionResult {\n const distSquared = distanceSquared(circleA.center, circleB.center);\n\n const radiusSum = circleA.radius + circleB.radius;\n\n const colliding = distSquared <= radiusSum * radiusSum;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Point } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a point\n * @param circle Circle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function circlePointCollision(\n circle: Circle,\n point: Point,\n): CollisionResult {\n const distSquared = distanceSquared(circle.center, point.position);\n\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Rectangle } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a rectangle\n * @param circle Circle\n * @param rect Rectangle\n * @returns Collision result with contact information\n */\nexport function circleRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return circleRotatedRectCollision(circle, rect);\n }\n\n // Find the closest point on the rectangle to the circle center\n const closestX = Math.max(\n rect.position.x,\n Math.min(circle.center.x, rect.position.x + rect.width),\n );\n const closestY = Math.max(\n rect.position.y,\n Math.min(circle.center.y, rect.position.y + rect.height),\n );\n\n const distSquared = distanceSquared(circle.center, {\n x: closestX,\n y: closestY,\n });\n\n // Check if the circle is colliding with the rectangle\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a circle and a rotated rectangle\n * @param circle Circle\n * @param rect Rotated rectangle\n * @returns Collision result with contact information\n */\nfunction circleRotatedRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Translate circle center to rectangle's local space (unrotated)\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to circle center\n const dx = circle.center.x - rectCenterX;\n const dy = circle.center.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a circle in the rectangle's local space\n const localCircle: Circle = {\n type: \"circle\",\n center: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n radius: circle.radius,\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: rect.position,\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return circleRectCollision(localCircle, localRect);\n}\n","import { CollisionResult, Point, Rectangle } from \"../../../types\";\n\n/**\n * Detects collision between a rectangle and a point\n * @param rect Rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function rectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return rotatedRectPointCollision(rect, point);\n }\n\n // Check if the point is inside the rectangle\n const colliding =\n point.position.x >= rect.position.x &&\n point.position.x <= rect.position.x + rect.width &&\n point.position.y >= rect.position.y &&\n point.position.y <= rect.position.y + rect.height;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a rotated rectangle and a point\n * @param rect Rotated rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nfunction rotatedRectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Calculate rectangle center\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to point\n const dx = point.position.x - rectCenterX;\n const dy = point.position.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a point in the rectangle's local space\n const localPoint: Point = {\n type: \"point\",\n position: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: {\n x: rect.position.x,\n y: rect.position.y,\n },\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return rectPointCollision(localRect, localPoint);\n}\n","import { Rectangle, CollisionResult, Vector2D } from \"../../../types\";\nimport {\n subtract,\n normalize,\n dot,\n rectToAABB,\n aabbIntersect,\n} from \"../../../utils\";\n\n/**\n * Detect collision between two rectangles\n * This implementation handles rotated rectangles using the Separating Axis Theorem (SAT)\n */\nexport function rectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // If both rectangles have zero rotation, use a simpler AABB check\n if (rectA.rotationDeg === 0 && rectB.rotationDeg === 0) {\n return aabbRectRectCollision(rectA, rectB);\n }\n\n // For rotated rectangles, use Separating Axis Theorem (SAT)\n return satRectRectCollision(rectA, rectB);\n}\n\n/**\n * Collision detection for axis-aligned (non-rotated) rectangles\n */\nfunction aabbRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n return {\n colliding: aabbIntersect(rectToAABB(rectA), rectToAABB(rectB)),\n };\n}\n\n/**\n * Get the corners of a rotated rectangle\n */\nfunction getRectangleCorners(rect: Rectangle): Vector2D[] {\n const { position, width, height, rotationDeg } = rect;\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n\n // Calculate center of the rectangle\n const center = {\n x: position.x + halfWidth,\n y: position.y + halfHeight,\n };\n\n // Convert rotation to radians\n const rotationRad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rotationRad);\n const sin = Math.sin(rotationRad);\n\n // Calculate corners relative to center\n const corners: Vector2D[] = [\n { x: -halfWidth, y: -halfHeight }, // Top-left\n { x: halfWidth, y: -halfHeight }, // Top-right\n { x: halfWidth, y: halfHeight }, // Bottom-right\n { x: -halfWidth, y: halfHeight }, // Bottom-left\n ];\n\n // Rotate and translate corners\n return corners.map((corner) => ({\n x: center.x + (corner.x * cos - corner.y * sin),\n y: center.y + (corner.x * sin + corner.y * cos),\n }));\n}\n\n/**\n * Get the axes to test for the SAT algorithm\n */\nfunction getAxes(cornersA: Vector2D[], cornersB: Vector2D[]): Vector2D[] {\n const axes: Vector2D[] = [];\n\n // Add the normals of each edge of the first rectangle\n for (let i = 0; i < cornersA.length; i++) {\n const p1 = cornersA[i];\n const p2 = cornersA[(i + 1) % cornersA.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n // Add the normals of each edge of the second rectangle\n for (let i = 0; i < cornersB.length; i++) {\n const p1 = cornersB[i];\n const p2 = cornersB[(i + 1) % cornersB.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n return axes;\n}\n\n/**\n * Project a shape onto an axis\n */\nfunction projectShapeOntoAxis(\n corners: Vector2D[],\n axis: Vector2D,\n): { min: number; max: number } {\n let min = dot(corners[0], axis);\n let max = min;\n\n for (let i = 1; i < corners.length; i++) {\n const projection = dot(corners[i], axis);\n if (projection < min) min = projection;\n if (projection > max) max = projection;\n }\n\n return { min, max };\n}\n\n/**\n * Collision detection for rotated rectangles using Separating Axis Theorem (SAT)\n */\nfunction satRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // Get corners of both rectangles\n const cornersA = getRectangleCorners(rectA);\n const cornersB = getRectangleCorners(rectB);\n\n // Get axes to test\n const axes = getAxes(cornersA, cornersB);\n\n // Test each axis\n for (const axis of axes) {\n const projectionA = projectShapeOntoAxis(cornersA, axis);\n const projectionB = projectShapeOntoAxis(cornersB, axis);\n\n // Check for separation\n if (\n projectionA.max < projectionB.min ||\n projectionB.max < projectionA.min\n ) {\n // Shapes are separated along this axis\n return { colliding: false };\n }\n }\n\n // If we get here, the shapes are colliding\n return { colliding: true };\n}\n","import { Circle, Point, Rectangle, Shape } from \"../../types\";\nimport { circleCircleCollision } from \"./circleCircle\";\nimport { circlePointCollision } from \"./circlePoint\";\nimport { circleRectCollision } from \"./circleRect\";\nimport { rectPointCollision } from \"./rectPoint\";\nimport { rectRectCollision } from \"./rectRect\";\n\n/**\n * Check if two entities are colliding\n * @param shapeA First shape\n * @param shapeB Second shape\n * @returns True if the shapes are colliding\n */\nexport function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean {\n const shapeAType = shapeA.type;\n const shapeBType = shapeB.type;\n\n // Circle vs Circle\n if (shapeAType === \"circle\" && shapeBType === \"circle\") {\n return circleCircleCollision(shapeA as Circle, shapeB as Circle).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"point\") {\n return circlePointCollision(shapeA as Circle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"circle\") {\n return circlePointCollision(shapeB as Circle, shapeA as Point).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"rectangle\") {\n return circleRectCollision(shapeA as Circle, shapeB as Rectangle).colliding;\n }\n if (shapeAType === \"rectangle\" && shapeBType === \"circle\") {\n return circleRectCollision(shapeB as Circle, shapeA as Rectangle).colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"rectangle\") {\n return rectRectCollision(shapeA as Rectangle, shapeB as Rectangle)\n .colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"point\") {\n return rectPointCollision(shapeA as Rectangle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"rectangle\") {\n return rectPointCollision(shapeB as Rectangle, shapeA as Point).colliding;\n }\n\n return false;\n}\n","import { Entity, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions/checkShapeCollision\";\n\n/**\n * Collision detector that uses a brute force approach\n * Checks every entity against the given shape\n *\n * Best used when:\n * - You have a small number of entities\n * - You only need to check collisions occasionally\n * - You want the simplest implementation\n */\nexport function BruteForceCollisionDetector({\n entities,\n}: {\n entities: Entity[];\n}) {\n return new BruteForceCollisionDetectorImpl(entities);\n}\n\nclass BruteForceCollisionDetectorImpl extends BaseCollisionDetector {\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entity IDs that collide with the shape\n */\n getCollisions(shape: Shape): (string | number)[] {\n const collisions: (string | number)[] = [];\n\n // Check each entity against the given shape\n for (const entity of this.entities) {\n // Check if the shapes collide\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity.id);\n }\n }\n\n return collisions;\n }\n}\n","import { AABB, Circle, Entity, Point, Rectangle, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions/checkShapeCollision\";\nimport { cellToId, circleToAABB, positionToCell, rectToAABB } from \"../utils\";\n\ntype CellId = number;\n\n/**\n * Configuration options for the spatial grid collision detector\n */\nexport interface SpatialGridConfig {\n /** Size of each grid cell */\n cellSize: number;\n}\n\n/**\n * Collision detector that uses spatial partitioning for efficient collision detection\n *\n * Best used when:\n * - You have a large number of entities\n * - Entities are distributed across the space\n * - You need to check collisions frequently\n */\nexport function SpatialGridCollisionDetector({\n entities,\n cellSize = 64,\n}: {\n entities: Entity[];\n cellSize?: number;\n}) {\n return new SpatialGridCollisionDetectorImpl(entities, { cellSize });\n}\n\nclass SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {\n /** The spatial hash grid for quick lookups */\n private grid: Map<CellId, Entity[]>;\n /** Configuration for the spatial grid */\n private config: SpatialGridConfig;\n\n /**\n * Create a new spatial grid collision detector\n */\n constructor(entities: Entity[], config: SpatialGridConfig) {\n super(entities);\n this.config = config;\n this.grid = new Map();\n\n // Initialize the grid with entities\n this.rebuildGrid();\n }\n\n /**\n * Get all entities that collide with the given shape\n */\n getCollisions(shape: Shape): (string | number)[] {\n // Get the AABB for the shape\n const aabb = this.getAABBForShape(shape);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Get all entities in those cells\n const potentialCollisions = new Set<Entity>();\n\n for (const cellId of cells) {\n const entitiesInCell = this.grid.get(cellId);\n if (entitiesInCell) {\n for (const entity of entitiesInCell) {\n potentialCollisions.add(entity);\n }\n }\n }\n\n // Check for actual collisions\n const collisions: (string | number)[] = [];\n\n for (const entity of potentialCollisions) {\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity.id);\n }\n }\n\n return collisions;\n }\n\n /**\n * Rebuild the spatial grid with the current entities\n */\n rebuildGrid(): void {\n // Clear the grid\n this.grid.clear();\n\n // Add all entities to the grid\n for (const entity of this.entities) {\n this.insertEntityIntoGrid(entity);\n }\n }\n\n /**\n * Insert an entity into the spatial grid\n */\n private insertEntityIntoGrid(entity: Entity): void {\n // Get the AABB for the entity\n const aabb = this.getAABB(entity);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Add the entity to each cell\n for (const cellId of cells) {\n if (!this.grid.has(cellId)) {\n this.grid.set(cellId, []);\n }\n\n this.grid.get(cellId)!.push(entity);\n }\n }\n\n /**\n * Get all cells that an AABB overlaps\n */\n private getCellsForAABB(aabb: AABB): CellId[] {\n const { cellSize } = this.config;\n\n // Calculate the min and max cell coordinates\n const [minCellX, minCellY] = positionToCell(aabb.min, cellSize);\n const [maxCellX, maxCellY] = positionToCell(aabb.max, cellSize);\n\n // Get all cells in the range\n const cells: CellId[] = new Array(\n (maxCellX - minCellX + 1) * (maxCellY - minCellY + 1),\n );\n let index = 0;\n\n for (let x = minCellX; x <= maxCellX; x++) {\n for (let y = minCellY; y <= maxCellY; y++) {\n cells[index++] = cellToId(x, y);\n }\n }\n\n return cells;\n }\n\n /**\n * Get the AABB for an entity\n */\n private getAABB(entity: Entity): AABB {\n return this.getAABBForShape(entity.shape);\n }\n\n /**\n * Get the AABB for a shape\n */\n private getAABBForShape(shape: Shape): AABB {\n const shapeType = shape.type;\n\n if (shapeType === \"circle\") {\n return circleToAABB(shape as Circle);\n } else if (shapeType === \"rectangle\") {\n return rectToAABB(shape as Rectangle);\n } else if (shapeType === \"point\") {\n // For a point, create a tiny AABB\n return {\n min: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n max: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n };\n }\n\n throw new Error(`Unsupported shape type: ${shapeType}`);\n }\n}\n","import { Circle, Point, Rectangle, Shape } from \"../types\";\nimport { ShapeStyle, RendererOptions, RenderableShape } from \"./types\";\n\n/**\n * Apply style settings to the canvas context\n */\nexport function applyStyle(ctx: CanvasRenderingContext2D, style: ShapeStyle) {\n // Save the current context state\n ctx.save();\n\n // Apply fill and stroke styles\n if (style.fillColor) {\n ctx.fillStyle = style.fillColor;\n }\n\n if (style.strokeColor) {\n ctx.strokeStyle = style.strokeColor;\n }\n\n if (style.strokeWidth !== undefined) {\n ctx.lineWidth = style.strokeWidth;\n }\n\n if (style.alpha !== undefined) {\n ctx.globalAlpha = style.alpha;\n }\n\n // Apply shadow settings if provided\n if (style.shadowBlur !== undefined) {\n ctx.shadowBlur = style.shadowBlur;\n }\n\n if (style.shadowColor) {\n ctx.shadowColor = style.shadowColor;\n }\n\n if (style.shadowOffsetX !== undefined) {\n ctx.shadowOffsetX = style.shadowOffsetX;\n }\n\n if (style.shadowOffsetY !== undefined) {\n ctx.shadowOffsetY = style.shadowOffsetY;\n }\n}\n\n/**\n * Restore the canvas context to its previous state\n */\nexport function restoreContext(ctx: CanvasRenderingContext2D) {\n ctx.restore();\n}\n\n/**\n * Render a circle shape\n */\nexport function renderCircle(\n ctx: CanvasRenderingContext2D,\n circle: Circle,\n style: ShapeStyle,\n) {\n applyStyle(ctx, style);\n\n ctx.beginPath();\n ctx.arc(circle.center.x, circle.center.y, circle.radius, 0, Math.PI * 2);\n\n if (style.fillColor) {\n ctx.fill();\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.stroke();\n }\n\n restoreContext(ctx);\n}\n\n/**\n * Render a point shape\n */\nexport function renderPoint(\n ctx: CanvasRenderingContext2D,\n point: Point,\n style: ShapeStyle,\n) {\n // Points are rendered as small circles\n const pointSize = style.strokeWidth || 4;\n\n applyStyle(ctx, style);\n\n ctx.beginPath();\n ctx.arc(point.position.x, point.position.y, pointSize / 2, 0, Math.PI * 2);\n\n if (style.fillColor) {\n ctx.fill();\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.stroke();\n }\n\n restoreContext(ctx);\n}\n\n/**\n * Render a rectangle shape\n */\nexport function renderRectangle(\n ctx: CanvasRenderingContext2D,\n rectangle: Rectangle,\n style: ShapeStyle,\n) {\n applyStyle(ctx, style);\n\n // Save the current transformation matrix\n ctx.save();\n\n // Translate to the rectangle's position\n ctx.translate(rectangle.position.x, rectangle.position.y);\n\n // Rotate if needed\n if (rectangle.rotationDeg !== 0) {\n const rotationRad = (rectangle.rotationDeg * Math.PI) / 180;\n ctx.rotate(rotationRad);\n }\n\n // Draw the rectangle centered at the origin\n const halfWidth = rectangle.width / 2;\n const halfHeight = rectangle.height / 2;\n\n if (style.fillColor) {\n ctx.fillRect(-halfWidth, -halfHeight, rectangle.width, rectangle.height);\n }\n\n if (style.strokeColor && style.strokeWidth && style.strokeWidth > 0) {\n ctx.strokeRect(-halfWidth, -halfHeight, rectangle.width, rectangle.height);\n }\n\n // Restore the transformation matrix\n ctx.restore();\n\n // Restore the style context\n restoreContext(ctx);\n}\n\n/**\n * Render any shape based on its type\n */\nexport function renderShape(\n ctx: CanvasRenderingContext2D,\n shape: Shape,\n style: ShapeStyle,\n) {\n switch (shape.type) {\n case \"circle\":\n renderCircle(ctx, shape, style);\n break;\n case \"rectangle\":\n renderRectangle(ctx, shape, style);\n break;\n case \"point\":\n renderPoint(ctx, shape, style);\n break;\n default:\n // This should never happen if all shape types are handled\n console.warn(\"Unknown shape type encountered\");\n break;\n }\n}\n\n/**\n * Main renderer function to render multiple shapes\n */\nexport function renderShapes(\n ctx: CanvasRenderingContext2D,\n shapes: RenderableShape[],\n options: RendererOptions,\n) {\n // Render each shape\n for (const { shape, style } of shapes) {\n // Merge with default style if provided\n const mergedStyle = {\n ...options.defaultStyle,\n ...style,\n };\n\n renderShape(ctx, shape, mergedStyle);\n }\n}\n","/**\n * pepperjs\n * A spicy, high-performance library for building 2D games in JavaScript\n *\n * @packageDocumentation\n */\n\n// ----- Collision Detection System -----\n// Export types\nexport * from \"./types\";\n\n// Export utility functions\nexport * from \"./utils\";\n\n// Export collision detection system\nexport * from \"./collision\";\n\n// Export rendering system\nexport * from \"./render\";\n\n// ----- Library Information -----\nexport const VERSION = \"0.0.1\";\n\n// ----- Future modules (placeholders) -----\n// These will be implemented in future versions\n\n/**\n * @internal\n * Rendering capabilities\n */\nexport const Renderer = {\n // Renderer is now implemented\n notImplemented: false,\n\n // Information about implementation\n info: \"Basic rendering capabilities are now available\",\n};\n\n/**\n * @internal\n * Future game loop management API\n */\nexport const GameLoop = {\n // Placeholder for future game loop module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Game loop management will be added in a future version\",\n};\n\n/**\n * @internal\n * Future input handling API\n */\nexport const Input = {\n // Placeholder for future input handling module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Input handling will be added in a future version\",\n};\n"],"mappings":"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","names":["vec2","x","y","add","a","b","subtract","scale","v","scalar","dot","cross","lengthSquared","length","normalize","len","distanceSquared","dx","dy","distance","rectToAABB","rect","circleToAABB","circle","aabbIntersect","positionToCell","position","cellSize","cellToId","BaseCollisionDetector","entities","circleCircleCollision","circleA","circleB","distSquared","distanceSquared","radiusSum","circlePointCollision","circle","point","distanceSquared","circleRectCollision","circle","rect","circleRotatedRectCollision","closestX","closestY","distanceSquared","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localCircle","localRect","rectPointCollision","rect","point","rotatedRectPointCollision","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localPoint","localRect","rectRectCollision","rectA","rectB","aabbRectRectCollision","satRectRectCollision","aabbIntersect","rectToAABB","getRectangleCorners","rect","position","width","height","rotationDeg","halfWidth","halfHeight","center","rotationRad","cos","sin","corner","getAxes","cornersA","cornersB","axes","i","p1","p2","edge","subtract","normal","normalize","projectShapeOntoAxis","corners","axis","min","dot","max","projection","projectionA","projectionB","checkShapeCollision","shapeA","shapeB","shapeAType","shapeBType","circleCircleCollision","circlePointCollision","circleRectCollision","rectRectCollision","rectPointCollision","BruteForceCollisionDetector","entities","BruteForceCollisionDetectorImpl","BaseCollisionDetector","shape","collisions","entity","checkShapeCollision","SpatialGridCollisionDetector","entities","cellSize","SpatialGridCollisionDetectorImpl","BaseCollisionDetector","config","shape","aabb","cells","potentialCollisions","cellId","entitiesInCell","entity","collisions","checkShapeCollision","minCellX","minCellY","positionToCell","maxCellX","maxCellY","index","x","y","cellToId","shapeType","circleToAABB","rectToAABB","applyStyle","ctx","style","restoreContext","renderCircle","circle","renderPoint","point","pointSize","renderRectangle","rectangle","rotationRad","halfWidth","halfHeight","renderShape","shape","renderShapes","shapes","options","mergedStyle","VERSION","Renderer","GameLoop","Input"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/collision/CollisionDetector.ts","../src/collision/shapeCollisions/circleCircle/index.ts","../src/collision/shapeCollisions/circlePoint/index.ts","../src/collision/shapeCollisions/circleRect/index.ts","../src/collision/shapeCollisions/rectPoint/index.ts","../src/collision/shapeCollisions/rectRect/index.ts","../src/collision/shapeCollisions/index.ts","../src/collision/BruteForceCollisionDetector.ts","../src/collision/SpatialGridCollisionDetector.ts","../src/camera/Camera.ts","../src/index.ts"],"sourcesContent":["import { Vector2D, AABB, Circle, Rectangle } from \"./types\";\n\n/**\n * Create a new vector\n */\nexport function vec2(x: number, y: number): Vector2D {\n return { x, y };\n}\n\n/**\n * Vector addition\n */\nexport function add(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x + b.x, y: a.y + b.y };\n}\n\n/**\n * Vector subtraction\n */\nexport function subtract(a: Vector2D, b: Vector2D): Vector2D {\n return { x: a.x - b.x, y: a.y - b.y };\n}\n\n/**\n * Vector scaling\n */\nexport function scale(v: Vector2D, scalar: number): Vector2D {\n return { x: v.x * scalar, y: v.y * scalar };\n}\n\n/**\n * Dot product of two vectors\n */\nexport function dot(a: Vector2D, b: Vector2D): number {\n return a.x * b.x + a.y * b.y;\n}\n\n/**\n * Cross product magnitude of two 2D vectors\n */\nexport function cross(a: Vector2D, b: Vector2D): number {\n return a.x * b.y - a.y * b.x;\n}\n\n/**\n * Get squared length of a vector (avoids sqrt for performance)\n */\nexport function lengthSquared(v: Vector2D): number {\n return v.x * v.x + v.y * v.y;\n}\n\n/**\n * Get vector length\n */\nexport function length(v: Vector2D): number {\n return Math.sqrt(lengthSquared(v));\n}\n\n/**\n * Normalize a vector (make it unit length)\n */\nexport function normalize(v: Vector2D): Vector2D {\n const len = length(v);\n // Avoid division by zero\n if (len < 1e-10) return { x: 0, y: 0 };\n return { x: v.x / len, y: v.y / len };\n}\n\n/**\n * Distance squared between two points (avoids sqrt for performance)\n */\nexport function distanceSquared(a: Vector2D, b: Vector2D): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n return dx * dx + dy * dy;\n}\n\n/**\n * Distance between two points\n */\nexport function distance(a: Vector2D, b: Vector2D): number {\n return Math.sqrt(distanceSquared(a, b));\n}\n\n/**\n * Create an AABB from a rectangle\n */\nexport function rectToAABB(rect: Rectangle): AABB {\n return {\n min: { x: rect.position.x, y: rect.position.y },\n max: { x: rect.position.x + rect.width, y: rect.position.y + rect.height },\n };\n}\n\n/**\n * Create an AABB from a circle\n */\nexport function circleToAABB(circle: Circle): AABB {\n return {\n min: {\n x: circle.center.x - circle.radius,\n y: circle.center.y - circle.radius,\n },\n max: {\n x: circle.center.x + circle.radius,\n y: circle.center.y + circle.radius,\n },\n };\n}\n\n/**\n * Check if two AABBs intersect\n */\nexport function aabbIntersect(a: AABB, b: AABB): boolean {\n // Exit with no intersection if separated along an axis\n if (a.max.x < b.min.x || a.min.x > b.max.x) return false;\n if (a.max.y < b.min.y || a.min.y > b.max.y) return false;\n\n // Overlapping on all axes means AABBs are intersecting\n return true;\n}\n\n/**\n * Calculate cell indices for a position in a spatial grid\n */\nexport function positionToCell(\n position: Vector2D,\n cellSize: number,\n): [number, number] {\n return [Math.floor(position.x / cellSize), Math.floor(position.y / cellSize)];\n}\n\n/**\n * Calculate a unique cell ID from grid coordinates\n * Uses a spatial hashing function to convert 2D coordinates to 1D\n */\nexport function cellToId(x: number, y: number): number {\n // Cantor pairing function - maps two non-negative integers to a unique non-negative integer\n return ((x + y) * (x + y + 1)) / 2 + y;\n}\n","import { Entity, Shape } from \"../types\";\n\n/**\n * Base class for collision detectors that implements common functionality\n */\nexport abstract class BaseCollisionDetector {\n /**\n * The entities to check collisions against\n */\n protected entities: Entity[];\n\n /**\n * Create a new collision detector\n * @param entities The entities to check collisions against\n */\n constructor(entities: Entity[]) {\n this.entities = entities;\n }\n\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entities that collide with the shape\n */\n abstract getCollisions(shape: Shape): Entity[];\n}\n","import { Circle, CollisionResult } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between two circles\n * @param circleA First circle\n * @param circleB Second circle\n * @returns Collision result with contact information\n */\nexport function circleCircleCollision(\n circleA: Circle,\n circleB: Circle,\n): CollisionResult {\n const distSquared = distanceSquared(circleA.center, circleB.center);\n\n const radiusSum = circleA.radius + circleB.radius;\n\n const colliding = distSquared <= radiusSum * radiusSum;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Point } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a point\n * @param circle Circle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function circlePointCollision(\n circle: Circle,\n point: Point,\n): CollisionResult {\n const distSquared = distanceSquared(circle.center, point.position);\n\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n","import { Circle, CollisionResult, Rectangle } from \"../../../types\";\nimport { distanceSquared } from \"../../../utils\";\n\n/**\n * Detects collision between a circle and a rectangle\n * @param circle Circle\n * @param rect Rectangle\n * @returns Collision result with contact information\n */\nexport function circleRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return circleRotatedRectCollision(circle, rect);\n }\n\n // Find the closest point on the rectangle to the circle center\n const closestX = Math.max(\n rect.position.x,\n Math.min(circle.center.x, rect.position.x + rect.width),\n );\n const closestY = Math.max(\n rect.position.y,\n Math.min(circle.center.y, rect.position.y + rect.height),\n );\n\n const distSquared = distanceSquared(circle.center, {\n x: closestX,\n y: closestY,\n });\n\n // Check if the circle is colliding with the rectangle\n const colliding = distSquared <= circle.radius * circle.radius;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a circle and a rotated rectangle\n * @param circle Circle\n * @param rect Rotated rectangle\n * @returns Collision result with contact information\n */\nfunction circleRotatedRectCollision(\n circle: Circle,\n rect: Rectangle,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Translate circle center to rectangle's local space (unrotated)\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to circle center\n const dx = circle.center.x - rectCenterX;\n const dy = circle.center.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a circle in the rectangle's local space\n const localCircle: Circle = {\n type: \"circle\",\n center: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n radius: circle.radius,\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: rect.position,\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return circleRectCollision(localCircle, localRect);\n}\n","import { CollisionResult, Point, Rectangle } from \"../../../types\";\n\n/**\n * Detects collision between a rectangle and a point\n * @param rect Rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nexport function rectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Handle rotation if present\n if (rect.rotationDeg !== 0) {\n return rotatedRectPointCollision(rect, point);\n }\n\n // Check if the point is inside the rectangle\n const colliding =\n point.position.x >= rect.position.x &&\n point.position.x <= rect.position.x + rect.width &&\n point.position.y >= rect.position.y &&\n point.position.y <= rect.position.y + rect.height;\n\n return {\n colliding,\n };\n}\n\n/**\n * Detects collision between a rotated rectangle and a point\n * @param rect Rotated rectangle\n * @param point Point\n * @returns Collision result with contact information\n */\nfunction rotatedRectPointCollision(\n rect: Rectangle,\n point: Point,\n): CollisionResult {\n // Convert rotation from degrees to radians\n const rotationRad = (rect.rotationDeg * Math.PI) / 180;\n const cos = Math.cos(-rotationRad);\n const sin = Math.sin(-rotationRad);\n\n // Calculate rectangle center\n const rectCenterX = rect.position.x + rect.width / 2;\n const rectCenterY = rect.position.y + rect.height / 2;\n\n // Vector from rectangle center to point\n const dx = point.position.x - rectCenterX;\n const dy = point.position.y - rectCenterY;\n\n // Rotate the vector to align with the rectangle's local axes\n const rotatedX = cos * dx - sin * dy;\n const rotatedY = sin * dx + cos * dy;\n\n // Create a point in the rectangle's local space\n const localPoint: Point = {\n type: \"point\",\n position: {\n x: rotatedX + rectCenterX,\n y: rotatedY + rectCenterY,\n },\n };\n\n // Create an unrotated rectangle\n const localRect: Rectangle = {\n type: \"rectangle\",\n position: {\n x: rect.position.x,\n y: rect.position.y,\n },\n width: rect.width,\n height: rect.height,\n rotationDeg: 0,\n };\n\n // Check collision with the unrotated rectangle\n return rectPointCollision(localRect, localPoint);\n}\n","import { Rectangle, CollisionResult, Vector2D } from \"../../../types\";\nimport {\n subtract,\n normalize,\n dot,\n rectToAABB,\n aabbIntersect,\n} from \"../../../utils\";\n\n/**\n * Detect collision between two rectangles\n * This implementation handles rotated rectangles using the Separating Axis Theorem (SAT)\n */\nexport function rectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // If both rectangles have zero rotation, use a simpler AABB check\n if (rectA.rotationDeg === 0 && rectB.rotationDeg === 0) {\n return aabbRectRectCollision(rectA, rectB);\n }\n\n // For rotated rectangles, use Separating Axis Theorem (SAT)\n return satRectRectCollision(rectA, rectB);\n}\n\n/**\n * Collision detection for axis-aligned (non-rotated) rectangles\n */\nfunction aabbRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n return {\n colliding: aabbIntersect(rectToAABB(rectA), rectToAABB(rectB)),\n };\n}\n\n/**\n * Get the corners of a rotated rectangle\n */\nfunction getRectangleCorners(rect: Rectangle): Vector2D[] {\n const { position, width, height, rotationDeg } = rect;\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n\n // Calculate center of the rectangle\n const center = {\n x: position.x + halfWidth,\n y: position.y + halfHeight,\n };\n\n // Convert rotation to radians\n const rotationRad = (rotationDeg * Math.PI) / 180;\n const cos = Math.cos(rotationRad);\n const sin = Math.sin(rotationRad);\n\n // Calculate corners relative to center\n const corners: Vector2D[] = [\n { x: -halfWidth, y: -halfHeight }, // Top-left\n { x: halfWidth, y: -halfHeight }, // Top-right\n { x: halfWidth, y: halfHeight }, // Bottom-right\n { x: -halfWidth, y: halfHeight }, // Bottom-left\n ];\n\n // Rotate and translate corners\n return corners.map((corner) => ({\n x: center.x + (corner.x * cos - corner.y * sin),\n y: center.y + (corner.x * sin + corner.y * cos),\n }));\n}\n\n/**\n * Get the axes to test for the SAT algorithm\n */\nfunction getAxes(cornersA: Vector2D[], cornersB: Vector2D[]): Vector2D[] {\n const axes: Vector2D[] = [];\n\n // Add the normals of each edge of the first rectangle\n for (let i = 0; i < cornersA.length; i++) {\n const p1 = cornersA[i];\n const p2 = cornersA[(i + 1) % cornersA.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n // Add the normals of each edge of the second rectangle\n for (let i = 0; i < cornersB.length; i++) {\n const p1 = cornersB[i];\n const p2 = cornersB[(i + 1) % cornersB.length];\n const edge = subtract(p2, p1);\n // The normal is perpendicular to the edge\n const normal = normalize({ x: -edge.y, y: edge.x });\n axes.push(normal);\n }\n\n return axes;\n}\n\n/**\n * Project a shape onto an axis\n */\nfunction projectShapeOntoAxis(\n corners: Vector2D[],\n axis: Vector2D,\n): { min: number; max: number } {\n let min = dot(corners[0], axis);\n let max = min;\n\n for (let i = 1; i < corners.length; i++) {\n const projection = dot(corners[i], axis);\n if (projection < min) min = projection;\n if (projection > max) max = projection;\n }\n\n return { min, max };\n}\n\n/**\n * Collision detection for rotated rectangles using Separating Axis Theorem (SAT)\n */\nfunction satRectRectCollision(\n rectA: Rectangle,\n rectB: Rectangle,\n): CollisionResult {\n // Get corners of both rectangles\n const cornersA = getRectangleCorners(rectA);\n const cornersB = getRectangleCorners(rectB);\n\n // Get axes to test\n const axes = getAxes(cornersA, cornersB);\n\n // Test each axis\n for (const axis of axes) {\n const projectionA = projectShapeOntoAxis(cornersA, axis);\n const projectionB = projectShapeOntoAxis(cornersB, axis);\n\n // Check for separation\n if (\n projectionA.max < projectionB.min ||\n projectionB.max < projectionA.min\n ) {\n // Shapes are separated along this axis\n return { colliding: false };\n }\n }\n\n // If we get here, the shapes are colliding\n return { colliding: true };\n}\n","import { Circle, Point, Rectangle, Shape } from \"../../types\";\nimport { circleCircleCollision } from \"./circleCircle\";\nimport { circlePointCollision } from \"./circlePoint\";\nimport { circleRectCollision } from \"./circleRect\";\nimport { rectPointCollision } from \"./rectPoint\";\nimport { rectRectCollision } from \"./rectRect\";\n\n/**\n * Check if two entities are colliding\n * @param shapeA First shape\n * @param shapeB Second shape\n * @returns True if the shapes are colliding\n */\nexport function checkShapeCollision(shapeA: Shape, shapeB: Shape): boolean {\n const shapeAType = shapeA.type;\n const shapeBType = shapeB.type;\n\n // Circle vs Circle\n if (shapeAType === \"circle\" && shapeBType === \"circle\") {\n return circleCircleCollision(shapeA as Circle, shapeB as Circle).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"point\") {\n return circlePointCollision(shapeA as Circle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"circle\") {\n return circlePointCollision(shapeB as Circle, shapeA as Point).colliding;\n }\n\n if (shapeAType === \"circle\" && shapeBType === \"rectangle\") {\n return circleRectCollision(shapeA as Circle, shapeB as Rectangle).colliding;\n }\n if (shapeAType === \"rectangle\" && shapeBType === \"circle\") {\n return circleRectCollision(shapeB as Circle, shapeA as Rectangle).colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"rectangle\") {\n return rectRectCollision(shapeA as Rectangle, shapeB as Rectangle)\n .colliding;\n }\n\n if (shapeAType === \"rectangle\" && shapeBType === \"point\") {\n return rectPointCollision(shapeA as Rectangle, shapeB as Point).colliding;\n }\n if (shapeAType === \"point\" && shapeBType === \"rectangle\") {\n return rectPointCollision(shapeB as Rectangle, shapeA as Point).colliding;\n }\n\n return false;\n}\n","import { Entity, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions\";\n\n/**\n * Collision detector that uses a brute force approach\n * Checks every entity against the given shape\n *\n * Best used when:\n * - You have a small number of entities\n * - You only need to check collisions occasionally\n * - You want the simplest implementation\n */\nexport function BruteForceCollisionDetector({\n entities,\n}: {\n entities: Entity[];\n}) {\n return new BruteForceCollisionDetectorImpl(entities);\n}\n\nclass BruteForceCollisionDetectorImpl extends BaseCollisionDetector {\n /**\n * Get all entities that collide with the given shape\n * @param shape The shape to check collisions against\n * @returns Array of entities that collide with the shape\n */\n getCollisions(shape: Shape): Entity[] {\n const collisions: Entity[] = [];\n\n // Check each entity against the given shape\n for (const entity of this.entities) {\n // Check if the shapes collide\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity);\n }\n }\n\n return collisions;\n }\n}\n","import { AABB, Circle, Entity, Point, Rectangle, Shape } from \"../types\";\nimport { BaseCollisionDetector } from \"./CollisionDetector\";\nimport { checkShapeCollision } from \"./shapeCollisions\";\nimport { cellToId, circleToAABB, positionToCell, rectToAABB } from \"../utils\";\n\ntype CellId = number;\n\n/**\n * Configuration options for the spatial grid collision detector\n */\nexport interface SpatialGridConfig {\n /** Size of each grid cell */\n cellSize: number;\n}\n\n/**\n * Collision detector that uses spatial partitioning for efficient collision detection\n *\n * Best used when:\n * - You have a large number of entities\n * - Entities are distributed across the space\n * - You need to check collisions frequently\n */\nexport function SpatialGridCollisionDetector({\n entities,\n cellSize = 64,\n}: {\n entities: Entity[];\n cellSize?: number;\n}) {\n return new SpatialGridCollisionDetectorImpl(entities, { cellSize });\n}\n\nclass SpatialGridCollisionDetectorImpl extends BaseCollisionDetector {\n /** The spatial hash grid for quick lookups */\n private grid: Map<CellId, Entity[]>;\n /** Configuration for the spatial grid */\n private config: SpatialGridConfig;\n\n /**\n * Create a new spatial grid collision detector\n */\n constructor(entities: Entity[], config: SpatialGridConfig) {\n super(entities);\n this.config = config;\n this.grid = new Map();\n\n // Initialize the grid with entities\n this.rebuildGrid();\n }\n\n /**\n * Get all entities that collide with the given shape\n */\n getCollisions(shape: Shape): Entity[] {\n // Get the AABB for the shape\n const aabb = this.getAABBForShape(shape);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Get all entities in those cells\n const potentialCollisions = new Set<Entity>();\n\n for (const cellId of cells) {\n const entitiesInCell = this.grid.get(cellId);\n if (entitiesInCell) {\n for (const entity of entitiesInCell) {\n potentialCollisions.add(entity);\n }\n }\n }\n\n // Check for actual collisions\n const collisions: Entity[] = [];\n\n for (const entity of potentialCollisions) {\n if (checkShapeCollision(shape, entity.shape)) {\n collisions.push(entity);\n }\n }\n\n return collisions;\n }\n\n /**\n * Rebuild the spatial grid with the current entities\n */\n rebuildGrid(): void {\n // Clear the grid\n this.grid.clear();\n\n // Add all entities to the grid\n for (const entity of this.entities) {\n this.insertEntityIntoGrid(entity);\n }\n }\n\n /**\n * Insert an entity into the spatial grid\n */\n private insertEntityIntoGrid(entity: Entity): void {\n // Get the AABB for the entity\n const aabb = this.getAABB(entity);\n\n // Get all cells that the AABB overlaps\n const cells = this.getCellsForAABB(aabb);\n\n // Add the entity to each cell\n for (const cellId of cells) {\n if (!this.grid.has(cellId)) {\n this.grid.set(cellId, []);\n }\n\n this.grid.get(cellId)!.push(entity);\n }\n }\n\n /**\n * Get all cells that an AABB overlaps\n */\n private getCellsForAABB(aabb: AABB): CellId[] {\n const { cellSize } = this.config;\n\n // Calculate the min and max cell coordinates\n const [minCellX, minCellY] = positionToCell(aabb.min, cellSize);\n const [maxCellX, maxCellY] = positionToCell(aabb.max, cellSize);\n\n // Get all cells in the range\n const cells: CellId[] = new Array(\n (maxCellX - minCellX + 1) * (maxCellY - minCellY + 1),\n );\n let index = 0;\n\n for (let x = minCellX; x <= maxCellX; x++) {\n for (let y = minCellY; y <= maxCellY; y++) {\n cells[index++] = cellToId(x, y);\n }\n }\n\n return cells;\n }\n\n /**\n * Get the AABB for an entity\n */\n private getAABB(entity: Entity): AABB {\n return this.getAABBForShape(entity.shape);\n }\n\n /**\n * Get the AABB for a shape\n */\n private getAABBForShape(shape: Shape): AABB {\n const shapeType = shape.type;\n\n if (shapeType === \"circle\") {\n return circleToAABB(shape as Circle);\n } else if (shapeType === \"rectangle\") {\n return rectToAABB(shape as Rectangle);\n } else if (shapeType === \"point\") {\n // For a point, create a tiny AABB\n return {\n min: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n max: { x: (shape as Point).position.x, y: (shape as Point).position.y },\n };\n }\n\n throw new Error(`Unsupported shape type: ${shapeType}`);\n }\n}\n","import { Vector2D, AABB, Point, Circle, Rectangle } from \"../types\";\n\n/**\n * Configuration for the camera\n */\nexport type CameraConfig = {\n /** Center position of the camera in world coordinates */\n position: Vector2D;\n /** Width of the viewport/canvas in pixels */\n width: number;\n /** Height of the viewport/canvas in pixels */\n height: number;\n /** Rotation in degrees (optional, defaults to 0) */\n rotation?: number;\n};\n\n/**\n * Converts a global coordinate object into a local camera coordinate system.\n */\nexport function globalCoordinatesToCameraCoordinates(\n global: Vector2D,\n camera: CameraConfig,\n): Vector2D;\nexport function globalCoordinatesToCameraCoordinates(\n global: Point,\n camera: CameraConfig,\n): Point;\nexport function globalCoordinatesToCameraCoordinates(\n global: Circle,\n camera: CameraConfig,\n): Circle;\nexport function globalCoordinatesToCameraCoordinates(\n global: Rectangle,\n camera: CameraConfig,\n): Rectangle;\nexport function globalCoordinatesToCameraCoordinates(\n global: AABB,\n camera: CameraConfig,\n): AABB;\nexport function globalCoordinatesToCameraCoordinates(\n global: Vector2D | Point | Circle | Rectangle | AABB,\n camera: CameraConfig,\n): Vector2D | Point | Circle | Rectangle | AABB {\n // Helper to transform a single point\n const transformPoint = (p: Vector2D): Vector2D => {\n // 1. Translate relative to camera position\n // If camera is at (100,100), world point (150,150) becomes (50,50)\n let x = p.x - camera.position.x;\n let y = p.y - camera.position.y;\n\n // 2. Rotate if needed\n if (camera.rotation) {\n const rad = (camera.rotation * Math.PI) / 180;\n // We rotate by -camera.rotation to bring world into camera space\n // If camera rotates +90 (clockwise), world appears to rotate -90 (counter-clockwise)\n const invRad = -rad;\n const cos = Math.cos(invRad);\n const sin = Math.sin(invRad);\n\n const rx = x * cos - y * sin;\n const ry = x * sin + y * cos;\n x = rx;\n y = ry;\n }\n\n // 3. Offset to viewport center\n // Camera position corresponds to the center of the screen\n x += camera.width / 2;\n y += camera.height / 2;\n\n return { x, y };\n };\n\n // Determine type and transform accordingly\n\n // Check for Shapes with 'type' property\n if (\"type\" in global) {\n if (global.type === \"point\") {\n return {\n ...global,\n position: transformPoint(global.position),\n };\n }\n\n if (global.type === \"circle\") {\n return {\n ...global,\n center: transformPoint(global.center),\n };\n }\n\n if (global.type === \"rectangle\") {\n return {\n ...global,\n position: transformPoint(global.position),\n // Rotation relative to camera\n rotationDeg: global.rotationDeg - (camera.rotation ?? 0),\n };\n }\n }\n\n // Check for AABB (min/max)\n if (\"min\" in global && \"max\" in global) {\n // For AABB, we must transform all corners and find the new AABB\n // because rotation might make it not axis-aligned in the original sense,\n // but the return type must be AABB.\n\n // If there is no rotation, we can just transform min/max\n if (!camera.rotation) {\n return {\n min: transformPoint(global.min),\n max: transformPoint(global.max),\n };\n }\n\n // With rotation, we need to transform all 4 corners\n const corners = [\n { x: global.min.x, y: global.min.y },\n { x: global.max.x, y: global.min.y },\n { x: global.max.x, y: global.max.y },\n { x: global.min.x, y: global.max.y },\n ];\n\n const transformed = corners.map(transformPoint);\n\n let minX = Infinity,\n minY = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity;\n\n for (const p of transformed) {\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n }\n\n return {\n min: { x: minX, y: minY },\n max: { x: maxX, y: maxY },\n };\n }\n\n // Check for Vector2D (x, y)\n // We check this last or ensure the other checks are exhaustive for types that might structurally overlap\n if (\"x\" in global && \"y\" in global) {\n return transformPoint(global);\n }\n\n throw new Error(\n \"Unsupported type passed to globalCoordinatesToCameraCoordinates\",\n );\n}\n","/**\n * InfernoJS\n * A spicy, high-performance library for building 2D games in JavaScript\n *\n * @packageDocumentation\n */\n\n// ----- Collision Detection System -----\n// Export types\nexport * from \"./types\";\n\n// Export utility functions\nexport * from \"./utils\";\n\n// Export collision detection system\nexport * from \"./collision\";\n\n// Export camera system\nexport * from \"./camera\";\n\n// ----- Library Information -----\nexport const VERSION = \"0.0.1\";\n\n// ----- Future modules (placeholders) -----\n// These will be implemented in future versions\n\n/**\n * @internal\n * Future game loop management API\n */\nexport const GameLoop = {\n // Placeholder for future game loop module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Game loop management will be added in a future version\",\n};\n\n/**\n * @internal\n * Future input handling API\n */\nexport const Input = {\n // Placeholder for future input handling module\n notImplemented: true,\n\n // Information about planned implementation\n info: \"Input handling will be added in a future version\",\n};\n"],"mappings":"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","names":["vec2","x","y","add","a","b","subtract","scale","v","scalar","dot","cross","lengthSquared","length","normalize","len","distanceSquared","dx","dy","distance","rectToAABB","rect","circleToAABB","circle","aabbIntersect","positionToCell","position","cellSize","cellToId","BaseCollisionDetector","entities","circleCircleCollision","circleA","circleB","distSquared","distanceSquared","radiusSum","circlePointCollision","circle","point","distanceSquared","circleRectCollision","circle","rect","circleRotatedRectCollision","closestX","closestY","distanceSquared","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localCircle","localRect","rectPointCollision","rect","point","rotatedRectPointCollision","rotationRad","cos","sin","rectCenterX","rectCenterY","dx","dy","rotatedX","rotatedY","localPoint","localRect","rectRectCollision","rectA","rectB","aabbRectRectCollision","satRectRectCollision","aabbIntersect","rectToAABB","getRectangleCorners","rect","position","width","height","rotationDeg","halfWidth","halfHeight","center","rotationRad","cos","sin","corner","getAxes","cornersA","cornersB","axes","p1","p2","edge","subtract","normal","normalize","projectShapeOntoAxis","corners","axis","min","dot","max","i","projection","projectionA","projectionB","checkShapeCollision","shapeA","shapeB","shapeAType","shapeBType","circleCircleCollision","circlePointCollision","circleRectCollision","rectRectCollision","rectPointCollision","BruteForceCollisionDetector","entities","BruteForceCollisionDetectorImpl","BaseCollisionDetector","shape","collisions","entity","checkShapeCollision","SpatialGridCollisionDetector","entities","cellSize","SpatialGridCollisionDetectorImpl","BaseCollisionDetector","config","shape","aabb","cells","potentialCollisions","cellId","entitiesInCell","entity","collisions","checkShapeCollision","minCellX","minCellY","positionToCell","maxCellX","maxCellY","index","x","y","cellToId","shapeType","circleToAABB","rectToAABB","globalCoordinatesToCameraCoordinates","global","camera","transformPoint","p","x","y","invRad","cos","sin","rx","ry","transformed","minX","minY","maxX","maxY","VERSION","GameLoop","Input"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "infernojs",
3
- "version": "0.0.1",
4
- "description": "A spicy, high-performance library for building 2D games in JavaScript",
3
+ "version": "0.0.2",
4
+ "description": "A library for building 2D games in TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",