@react-arch/geometry 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 React Arch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,133 @@
1
+ import { EPSILON, Vec2, Vec2 as Vec2$1, Vec3 } from "@react-arch/shared";
2
+
3
+ //#region src/vec2.d.ts
4
+ declare const add: (a: Vec2$1, b: Vec2$1) => Vec2$1;
5
+ declare const sub: (a: Vec2$1, b: Vec2$1) => Vec2$1;
6
+ declare const scale: (a: Vec2$1, s: number) => Vec2$1;
7
+ declare const dot: (a: Vec2$1, b: Vec2$1) => number;
8
+ declare const cross: (a: Vec2$1, b: Vec2$1) => number;
9
+ declare const length: (a: Vec2$1) => number;
10
+ declare const distance: (a: Vec2$1, b: Vec2$1) => number;
11
+ declare function normalize(a: Vec2$1): Vec2$1;
12
+ /** Left-hand normal of a vector (rotate +90°). */
13
+ declare function normal(a: Vec2$1): Vec2$1;
14
+ declare function lerp(a: Vec2$1, b: Vec2$1, t: number): Vec2$1;
15
+ declare function angle(a: Vec2$1, b: Vec2$1): number;
16
+ declare function equals(a: Vec2$1, b: Vec2$1, eps?: number): boolean;
17
+ /** Project point p onto the segment [a,b], returning the closest point + t. */
18
+ declare function projectPointOnSegment(p: Vec2$1, a: Vec2$1, b: Vec2$1): {
19
+ point: Vec2$1;
20
+ t: number;
21
+ distance: number;
22
+ };
23
+ //#endregion
24
+ //#region src/line.d.ts
25
+ interface Segment {
26
+ a: Vec2$1;
27
+ b: Vec2$1;
28
+ }
29
+ /**
30
+ * Intersection of two infinite lines defined by segments. Returns null when
31
+ * the lines are parallel (within EPSILON).
32
+ */
33
+ declare function lineIntersection(s1: Segment, s2: Segment): Vec2$1 | null;
34
+ /**
35
+ * Intersection point of two finite segments, or null if they don't cross.
36
+ */
37
+ declare function segmentIntersection(s1: Segment, s2: Segment): Vec2$1 | null;
38
+ //#endregion
39
+ //#region src/polygon.d.ts
40
+ /** Signed area (positive = counter-clockwise winding). */
41
+ declare function signedArea(polygon: Vec2$1[]): number;
42
+ declare function polygonArea(polygon: Vec2$1[]): number;
43
+ declare function polygonCentroid(polygon: Vec2$1[]): Vec2$1;
44
+ declare function isClockwise(polygon: Vec2$1[]): boolean;
45
+ declare function ensureCounterClockwise(polygon: Vec2$1[]): Vec2$1[];
46
+ /** Axis-aligned bounding box of a set of points. */
47
+ declare function bounds(points: Vec2$1[]): {
48
+ min: Vec2$1;
49
+ max: Vec2$1;
50
+ width: number;
51
+ height: number;
52
+ };
53
+ declare function pointInPolygon(point: Vec2$1, polygon: Vec2$1[]): boolean;
54
+ //#endregion
55
+ //#region src/wall.d.ts
56
+ interface WallLike {
57
+ start: Vec2$1;
58
+ end: Vec2$1;
59
+ thickness: number;
60
+ }
61
+ declare function wallLength(wall: WallLike): number;
62
+ declare function wallDirection(wall: WallLike): Vec2$1;
63
+ /**
64
+ * Generate the visible quad polygon of a wall from its centerline and
65
+ * thickness. Vertices are ordered counter-clockwise.
66
+ */
67
+ declare function wallPolygon(wall: WallLike, extendEnds?: number): Vec2$1[];
68
+ /** World position of a point at distance `offset` along the wall centerline. */
69
+ declare function pointAlongWall(wall: WallLike, offset: number): Vec2$1;
70
+ interface OpeningLike {
71
+ offset: number;
72
+ width: number;
73
+ }
74
+ /**
75
+ * The two centerline endpoints of an opening cut, clamped to the wall length.
76
+ */
77
+ declare function openingSpan(wall: WallLike, opening: OpeningLike): {
78
+ start: Vec2$1;
79
+ end: Vec2$1;
80
+ center: Vec2$1;
81
+ };
82
+ /** Whether an opening fits entirely within the wall it is attached to. */
83
+ declare function openingFits(wall: WallLike, opening: OpeningLike): boolean;
84
+ interface WallOpeningInput extends OpeningLike {
85
+ /** Bottom of the opening above the floor. */
86
+ sillHeight: number;
87
+ /** Opening height. */
88
+ height: number;
89
+ }
90
+ /**
91
+ * A solid box of the wall expressed in wall-local coordinates: `along` runs
92
+ * from the wall start, `z` is vertical. The 3D renderer extrudes each box by
93
+ * the wall thickness. This is how openings are "cut" without CSG — the wall is
94
+ * decomposed into the solid pieces that remain around its openings.
95
+ */
96
+ interface WallBox {
97
+ along0: number;
98
+ along1: number;
99
+ z0: number;
100
+ z1: number;
101
+ }
102
+ declare function wallBoxes(wall: WallLike, openings: WallOpeningInput[], wallHeight: number,
103
+ /**
104
+ * Extend the solid wall by this much past each end. Pass `thickness / 2` so
105
+ * walls meeting at a corner overlap and fill the corner square — otherwise
106
+ * each centerline box stops short and leaves a notch.
107
+ */
108
+
109
+ extendEnds?: number): WallBox[];
110
+ //#endregion
111
+ //#region src/snap.d.ts
112
+ type SnapKind = "endpoint" | "midpoint" | "grid" | "perpendicular";
113
+ interface SnapResult {
114
+ point: Vec2$1;
115
+ kind: SnapKind;
116
+ distance: number;
117
+ }
118
+ interface SnapSegment {
119
+ a: Vec2$1;
120
+ b: Vec2$1;
121
+ }
122
+ declare function snapToGrid(point: Vec2$1, gridSize: number): Vec2$1;
123
+ /**
124
+ * Find the best snap candidate near `point`. Endpoint and midpoint snaps take
125
+ * priority over grid snaps within the given pixel/world tolerance.
126
+ */
127
+ declare function findSnap(point: Vec2$1, segments: SnapSegment[], options?: {
128
+ tolerance: number;
129
+ gridSize?: number;
130
+ }): SnapResult | null;
131
+ //#endregion
132
+ export { EPSILON, OpeningLike, Segment, SnapKind, SnapResult, SnapSegment, type Vec2, type Vec3, WallBox, WallLike, WallOpeningInput, add, angle, bounds, cross, distance, dot, ensureCounterClockwise, equals, findSnap, isClockwise, length, lerp, lineIntersection, normal, normalize, openingFits, openingSpan, pointAlongWall, pointInPolygon, polygonArea, polygonCentroid, projectPointOnSegment, scale, segmentIntersection, signedArea, snapToGrid, sub, wallBoxes, wallDirection, wallLength, wallPolygon };
133
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/vec2.ts","../src/line.ts","../src/polygon.ts","../src/wall.ts","../src/snap.ts"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,308 @@
1
+ import { EPSILON, EPSILON as EPSILON$1 } from "@react-arch/shared";
2
+ //#region src/vec2.ts
3
+ const add = (a, b) => [a[0] + b[0], a[1] + b[1]];
4
+ const sub = (a, b) => [a[0] - b[0], a[1] - b[1]];
5
+ const scale = (a, s) => [a[0] * s, a[1] * s];
6
+ const dot = (a, b) => a[0] * b[0] + a[1] * b[1];
7
+ const cross = (a, b) => a[0] * b[1] - a[1] * b[0];
8
+ const length = (a) => Math.hypot(a[0], a[1]);
9
+ const distance = (a, b) => length(sub(a, b));
10
+ function normalize(a) {
11
+ const len = length(a);
12
+ if (len < EPSILON$1) return [0, 0];
13
+ return [a[0] / len, a[1] / len];
14
+ }
15
+ /** Left-hand normal of a vector (rotate +90°). */
16
+ function normal(a) {
17
+ return [-a[1], a[0]];
18
+ }
19
+ function lerp(a, b, t) {
20
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
21
+ }
22
+ function angle(a, b) {
23
+ return Math.atan2(b[1] - a[1], b[0] - a[0]);
24
+ }
25
+ function equals(a, b, eps = EPSILON$1) {
26
+ return Math.abs(a[0] - b[0]) <= eps && Math.abs(a[1] - b[1]) <= eps;
27
+ }
28
+ /** Project point p onto the segment [a,b], returning the closest point + t. */
29
+ function projectPointOnSegment(p, a, b) {
30
+ const ab = sub(b, a);
31
+ const len2 = dot(ab, ab);
32
+ if (len2 < EPSILON$1) return {
33
+ point: a,
34
+ t: 0,
35
+ distance: distance(p, a)
36
+ };
37
+ let t = dot(sub(p, a), ab) / len2;
38
+ t = Math.max(0, Math.min(1, t));
39
+ const point = add(a, scale(ab, t));
40
+ return {
41
+ point,
42
+ t,
43
+ distance: distance(p, point)
44
+ };
45
+ }
46
+ //#endregion
47
+ //#region src/line.ts
48
+ /**
49
+ * Intersection of two infinite lines defined by segments. Returns null when
50
+ * the lines are parallel (within EPSILON).
51
+ */
52
+ function lineIntersection(s1, s2) {
53
+ const r = sub(s1.b, s1.a);
54
+ const s = sub(s2.b, s2.a);
55
+ const denom = cross(r, s);
56
+ if (Math.abs(denom) < EPSILON$1) return null;
57
+ const t = cross(sub(s2.a, s1.a), s) / denom;
58
+ return [s1.a[0] + t * r[0], s1.a[1] + t * r[1]];
59
+ }
60
+ /**
61
+ * Intersection point of two finite segments, or null if they don't cross.
62
+ */
63
+ function segmentIntersection(s1, s2) {
64
+ const r = sub(s1.b, s1.a);
65
+ const s = sub(s2.b, s2.a);
66
+ const denom = cross(r, s);
67
+ if (Math.abs(denom) < EPSILON$1) return null;
68
+ const qp = sub(s2.a, s1.a);
69
+ const t = cross(qp, s) / denom;
70
+ const u = cross(qp, r) / denom;
71
+ if (t < -EPSILON$1 || t > 1 + EPSILON$1 || u < -EPSILON$1 || u > 1 + EPSILON$1) return null;
72
+ return [s1.a[0] + t * r[0], s1.a[1] + t * r[1]];
73
+ }
74
+ //#endregion
75
+ //#region src/polygon.ts
76
+ /** Signed area (positive = counter-clockwise winding). */
77
+ function signedArea(polygon) {
78
+ let sum = 0;
79
+ for (let i = 0; i < polygon.length; i++) {
80
+ const a = polygon[i];
81
+ const b = polygon[(i + 1) % polygon.length];
82
+ sum += a[0] * b[1] - b[0] * a[1];
83
+ }
84
+ return sum / 2;
85
+ }
86
+ function polygonArea(polygon) {
87
+ return Math.abs(signedArea(polygon));
88
+ }
89
+ function polygonCentroid(polygon) {
90
+ let cx = 0;
91
+ let cy = 0;
92
+ let a = 0;
93
+ for (let i = 0; i < polygon.length; i++) {
94
+ const p0 = polygon[i];
95
+ const p1 = polygon[(i + 1) % polygon.length];
96
+ const f = p0[0] * p1[1] - p1[0] * p0[1];
97
+ cx += (p0[0] + p1[0]) * f;
98
+ cy += (p0[1] + p1[1]) * f;
99
+ a += f;
100
+ }
101
+ if (Math.abs(a) < 1e-9) {
102
+ const n = polygon.length || 1;
103
+ return [polygon.reduce((s, p) => s + p[0], 0) / n, polygon.reduce((s, p) => s + p[1], 0) / n];
104
+ }
105
+ a *= 3;
106
+ return [cx / a, cy / a];
107
+ }
108
+ function isClockwise(polygon) {
109
+ return signedArea(polygon) < 0;
110
+ }
111
+ function ensureCounterClockwise(polygon) {
112
+ return isClockwise(polygon) ? [...polygon].reverse() : polygon;
113
+ }
114
+ /** Axis-aligned bounding box of a set of points. */
115
+ function bounds(points) {
116
+ if (points.length === 0) return {
117
+ min: [0, 0],
118
+ max: [0, 0],
119
+ width: 0,
120
+ height: 0
121
+ };
122
+ let minX = Infinity;
123
+ let minY = Infinity;
124
+ let maxX = -Infinity;
125
+ let maxY = -Infinity;
126
+ for (const [x, y] of points) {
127
+ if (x < minX) minX = x;
128
+ if (y < minY) minY = y;
129
+ if (x > maxX) maxX = x;
130
+ if (y > maxY) maxY = y;
131
+ }
132
+ return {
133
+ min: [minX, minY],
134
+ max: [maxX, maxY],
135
+ width: maxX - minX,
136
+ height: maxY - minY
137
+ };
138
+ }
139
+ function pointInPolygon(point, polygon) {
140
+ let inside = false;
141
+ const [px, py] = point;
142
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
143
+ const a = polygon[i];
144
+ const b = polygon[j];
145
+ if (a[1] > py !== b[1] > py && px < (b[0] - a[0]) * (py - a[1]) / (b[1] - a[1]) + a[0]) inside = !inside;
146
+ }
147
+ return inside;
148
+ }
149
+ //#endregion
150
+ //#region src/wall.ts
151
+ function wallLength(wall) {
152
+ return distance(wall.start, wall.end);
153
+ }
154
+ function wallDirection(wall) {
155
+ return normalize(sub(wall.end, wall.start));
156
+ }
157
+ /**
158
+ * Generate the visible quad polygon of a wall from its centerline and
159
+ * thickness. Vertices are ordered counter-clockwise.
160
+ */
161
+ function wallPolygon(wall, extendEnds = 0) {
162
+ const dir = wallDirection(wall);
163
+ const n = normal(dir);
164
+ const half = wall.thickness / 2;
165
+ const offset = scale(n, half);
166
+ const negOffset = scale(n, -half);
167
+ const start = add(wall.start, scale(dir, -extendEnds));
168
+ const end = add(wall.end, scale(dir, extendEnds));
169
+ return [
170
+ add(start, negOffset),
171
+ add(end, negOffset),
172
+ add(end, offset),
173
+ add(start, offset)
174
+ ];
175
+ }
176
+ /** World position of a point at distance `offset` along the wall centerline. */
177
+ function pointAlongWall(wall, offset) {
178
+ const dir = wallDirection(wall);
179
+ return add(wall.start, scale(dir, offset));
180
+ }
181
+ /**
182
+ * The two centerline endpoints of an opening cut, clamped to the wall length.
183
+ */
184
+ function openingSpan(wall, opening) {
185
+ const len = wallLength(wall);
186
+ const half = opening.width / 2;
187
+ const start = Math.max(0, opening.offset - half);
188
+ const end = Math.min(len, opening.offset + half);
189
+ return {
190
+ start: pointAlongWall(wall, start),
191
+ end: pointAlongWall(wall, end),
192
+ center: pointAlongWall(wall, opening.offset)
193
+ };
194
+ }
195
+ /** Whether an opening fits entirely within the wall it is attached to. */
196
+ function openingFits(wall, opening) {
197
+ const len = wallLength(wall);
198
+ return opening.offset - opening.width / 2 >= -1e-6 && opening.offset + opening.width / 2 <= len + 1e-6;
199
+ }
200
+ function wallBoxes(wall, openings, wallHeight, extendEnds = 0) {
201
+ const len = wallLength(wall);
202
+ const lo = -extendEnds;
203
+ const hi = len + extendEnds;
204
+ if (openings.length === 0) return [{
205
+ along0: lo,
206
+ along1: hi,
207
+ z0: 0,
208
+ z1: wallHeight
209
+ }];
210
+ const sorted = [...openings].map((o) => ({
211
+ a0: Math.max(0, o.offset - o.width / 2),
212
+ a1: Math.min(len, o.offset + o.width / 2),
213
+ sill: Math.max(0, o.sillHeight),
214
+ top: Math.min(wallHeight, o.sillHeight + o.height)
215
+ })).sort((a, b) => a.a0 - b.a0);
216
+ const boxes = [];
217
+ let cursor = lo;
218
+ for (const o of sorted) {
219
+ if (o.a0 > cursor) boxes.push({
220
+ along0: cursor,
221
+ along1: o.a0,
222
+ z0: 0,
223
+ z1: wallHeight
224
+ });
225
+ if (o.top < wallHeight) boxes.push({
226
+ along0: o.a0,
227
+ along1: o.a1,
228
+ z0: o.top,
229
+ z1: wallHeight
230
+ });
231
+ if (o.sill > 0) boxes.push({
232
+ along0: o.a0,
233
+ along1: o.a1,
234
+ z0: 0,
235
+ z1: o.sill
236
+ });
237
+ cursor = Math.max(cursor, o.a1);
238
+ }
239
+ if (cursor < hi) boxes.push({
240
+ along0: cursor,
241
+ along1: hi,
242
+ z0: 0,
243
+ z1: wallHeight
244
+ });
245
+ return boxes;
246
+ }
247
+ //#endregion
248
+ //#region src/snap.ts
249
+ function snapToGrid(point, gridSize) {
250
+ if (gridSize <= 0) return point;
251
+ return [Math.round(point[0] / gridSize) * gridSize, Math.round(point[1] / gridSize) * gridSize];
252
+ }
253
+ /**
254
+ * Find the best snap candidate near `point`. Endpoint and midpoint snaps take
255
+ * priority over grid snaps within the given pixel/world tolerance.
256
+ */
257
+ function findSnap(point, segments, options = { tolerance: .2 }) {
258
+ const candidates = [];
259
+ for (const seg of segments) {
260
+ for (const ep of [seg.a, seg.b]) {
261
+ const d = distance(point, ep);
262
+ if (d <= options.tolerance) candidates.push({
263
+ point: ep,
264
+ kind: "endpoint",
265
+ distance: d
266
+ });
267
+ }
268
+ const mid = lerp(seg.a, seg.b, .5);
269
+ const dm = distance(point, mid);
270
+ if (dm <= options.tolerance) candidates.push({
271
+ point: mid,
272
+ kind: "midpoint",
273
+ distance: dm
274
+ });
275
+ const proj = projectPointOnSegment(point, seg.a, seg.b);
276
+ if (proj.distance <= options.tolerance) candidates.push({
277
+ point: proj.point,
278
+ kind: "perpendicular",
279
+ distance: proj.distance
280
+ });
281
+ }
282
+ if (candidates.length > 0) {
283
+ candidates.sort((a, b) => {
284
+ const rank = {
285
+ endpoint: 0,
286
+ midpoint: 1,
287
+ perpendicular: 2,
288
+ grid: 3
289
+ };
290
+ if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];
291
+ return a.distance - b.distance;
292
+ });
293
+ return candidates[0];
294
+ }
295
+ if (options.gridSize) {
296
+ const gp = snapToGrid(point, options.gridSize);
297
+ return {
298
+ point: gp,
299
+ kind: "grid",
300
+ distance: distance(point, gp)
301
+ };
302
+ }
303
+ return null;
304
+ }
305
+ //#endregion
306
+ export { EPSILON, add, angle, bounds, cross, distance, dot, ensureCounterClockwise, equals, findSnap, isClockwise, length, lerp, lineIntersection, normal, normalize, openingFits, openingSpan, pointAlongWall, pointInPolygon, polygonArea, polygonCentroid, projectPointOnSegment, scale, segmentIntersection, signedArea, snapToGrid, sub, wallBoxes, wallDirection, wallLength, wallPolygon };
307
+
308
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["EPSILON","EPSILON"],"sources":["../src/vec2.ts","../src/line.ts","../src/polygon.ts","../src/wall.ts","../src/snap.ts"],"sourcesContent":["import { EPSILON, type Vec2 } from \"@react-arch/shared\";\n\nexport const add = (a: Vec2, b: Vec2): Vec2 => [a[0] + b[0], a[1] + b[1]];\nexport const sub = (a: Vec2, b: Vec2): Vec2 => [a[0] - b[0], a[1] - b[1]];\nexport const scale = (a: Vec2, s: number): Vec2 => [a[0] * s, a[1] * s];\nexport const dot = (a: Vec2, b: Vec2): number => a[0] * b[0] + a[1] * b[1];\nexport const cross = (a: Vec2, b: Vec2): number => a[0] * b[1] - a[1] * b[0];\nexport const length = (a: Vec2): number => Math.hypot(a[0], a[1]);\nexport const distance = (a: Vec2, b: Vec2): number => length(sub(a, b));\n\nexport function normalize(a: Vec2): Vec2 {\n const len = length(a);\n if (len < EPSILON) return [0, 0];\n return [a[0] / len, a[1] / len];\n}\n\n/** Left-hand normal of a vector (rotate +90°). */\nexport function normal(a: Vec2): Vec2 {\n return [-a[1], a[0]];\n}\n\nexport function lerp(a: Vec2, b: Vec2, t: number): Vec2 {\n return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];\n}\n\nexport function angle(a: Vec2, b: Vec2): number {\n return Math.atan2(b[1] - a[1], b[0] - a[0]);\n}\n\nexport function equals(a: Vec2, b: Vec2, eps = EPSILON): boolean {\n return Math.abs(a[0] - b[0]) <= eps && Math.abs(a[1] - b[1]) <= eps;\n}\n\n/** Project point p onto the segment [a,b], returning the closest point + t. */\nexport function projectPointOnSegment(\n p: Vec2,\n a: Vec2,\n b: Vec2,\n): { point: Vec2; t: number; distance: number } {\n const ab = sub(b, a);\n const len2 = dot(ab, ab);\n if (len2 < EPSILON) return { point: a, t: 0, distance: distance(p, a) };\n let t = dot(sub(p, a), ab) / len2;\n t = Math.max(0, Math.min(1, t));\n const point = add(a, scale(ab, t));\n return { point, t, distance: distance(p, point) };\n}\n","import { EPSILON, type Vec2 } from \"@react-arch/shared\";\nimport { cross, sub } from \"./vec2.js\";\n\nexport interface Segment {\n a: Vec2;\n b: Vec2;\n}\n\n/**\n * Intersection of two infinite lines defined by segments. Returns null when\n * the lines are parallel (within EPSILON).\n */\nexport function lineIntersection(s1: Segment, s2: Segment): Vec2 | null {\n const r = sub(s1.b, s1.a);\n const s = sub(s2.b, s2.a);\n const denom = cross(r, s);\n if (Math.abs(denom) < EPSILON) return null; // parallel\n const qp = sub(s2.a, s1.a);\n const t = cross(qp, s) / denom;\n return [s1.a[0] + t * r[0], s1.a[1] + t * r[1]];\n}\n\n/**\n * Intersection point of two finite segments, or null if they don't cross.\n */\nexport function segmentIntersection(s1: Segment, s2: Segment): Vec2 | null {\n const r = sub(s1.b, s1.a);\n const s = sub(s2.b, s2.a);\n const denom = cross(r, s);\n if (Math.abs(denom) < EPSILON) return null;\n const qp = sub(s2.a, s1.a);\n const t = cross(qp, s) / denom;\n const u = cross(qp, r) / denom;\n if (t < -EPSILON || t > 1 + EPSILON || u < -EPSILON || u > 1 + EPSILON) {\n return null;\n }\n return [s1.a[0] + t * r[0], s1.a[1] + t * r[1]];\n}\n","import { type Vec2 } from \"@react-arch/shared\";\n\n/** Signed area (positive = counter-clockwise winding). */\nexport function signedArea(polygon: Vec2[]): number {\n let sum = 0;\n for (let i = 0; i < polygon.length; i++) {\n const a = polygon[i]!;\n const b = polygon[(i + 1) % polygon.length]!;\n sum += a[0] * b[1] - b[0] * a[1];\n }\n return sum / 2;\n}\n\nexport function polygonArea(polygon: Vec2[]): number {\n return Math.abs(signedArea(polygon));\n}\n\nexport function polygonCentroid(polygon: Vec2[]): Vec2 {\n let cx = 0;\n let cy = 0;\n let a = 0;\n for (let i = 0; i < polygon.length; i++) {\n const p0 = polygon[i]!;\n const p1 = polygon[(i + 1) % polygon.length]!;\n const f = p0[0] * p1[1] - p1[0] * p0[1];\n cx += (p0[0] + p1[0]) * f;\n cy += (p0[1] + p1[1]) * f;\n a += f;\n }\n if (Math.abs(a) < 1e-9) {\n // Degenerate polygon: fall back to vertex average.\n const n = polygon.length || 1;\n return [\n polygon.reduce((s, p) => s + p[0], 0) / n,\n polygon.reduce((s, p) => s + p[1], 0) / n,\n ];\n }\n a *= 3;\n return [cx / a, cy / a];\n}\n\nexport function isClockwise(polygon: Vec2[]): boolean {\n return signedArea(polygon) < 0;\n}\n\nexport function ensureCounterClockwise(polygon: Vec2[]): Vec2[] {\n return isClockwise(polygon) ? [...polygon].reverse() : polygon;\n}\n\n/** Axis-aligned bounding box of a set of points. */\nexport function bounds(points: Vec2[]): {\n min: Vec2;\n max: Vec2;\n width: number;\n height: number;\n} {\n if (points.length === 0) {\n return { min: [0, 0], max: [0, 0], width: 0, height: 0 };\n }\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [x, y] of points) {\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n }\n return {\n min: [minX, minY],\n max: [maxX, maxY],\n width: maxX - minX,\n height: maxY - minY,\n };\n}\n\nexport function pointInPolygon(point: Vec2, polygon: Vec2[]): boolean {\n let inside = false;\n const [px, py] = point;\n for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {\n const a = polygon[i]!;\n const b = polygon[j]!;\n const intersect =\n a[1] > py !== b[1] > py &&\n px < ((b[0] - a[0]) * (py - a[1])) / (b[1] - a[1]) + a[0];\n if (intersect) inside = !inside;\n }\n return inside;\n}\n","import { type Vec2 } from \"@react-arch/shared\";\nimport { add, distance, normal, normalize, scale, sub } from \"./vec2.js\";\n\nexport interface WallLike {\n start: Vec2;\n end: Vec2;\n thickness: number;\n}\n\nexport function wallLength(wall: WallLike): number {\n return distance(wall.start, wall.end);\n}\n\nexport function wallDirection(wall: WallLike): Vec2 {\n return normalize(sub(wall.end, wall.start));\n}\n\n/**\n * Generate the visible quad polygon of a wall from its centerline and\n * thickness. Vertices are ordered counter-clockwise.\n */\nexport function wallPolygon(wall: WallLike, extendEnds = 0): Vec2[] {\n const dir = wallDirection(wall);\n const n = normal(dir);\n const half = wall.thickness / 2;\n const offset = scale(n, half);\n const negOffset = scale(n, -half);\n // Push the ends out along the wall direction so corners overlap (no notch).\n const start = add(wall.start, scale(dir, -extendEnds));\n const end = add(wall.end, scale(dir, extendEnds));\n return [\n add(start, negOffset),\n add(end, negOffset),\n add(end, offset),\n add(start, offset),\n ];\n}\n\n/** World position of a point at distance `offset` along the wall centerline. */\nexport function pointAlongWall(wall: WallLike, offset: number): Vec2 {\n const dir = wallDirection(wall);\n return add(wall.start, scale(dir, offset));\n}\n\nexport interface OpeningLike {\n offset: number;\n width: number;\n}\n\n/**\n * The two centerline endpoints of an opening cut, clamped to the wall length.\n */\nexport function openingSpan(\n wall: WallLike,\n opening: OpeningLike,\n): { start: Vec2; end: Vec2; center: Vec2 } {\n const len = wallLength(wall);\n const half = opening.width / 2;\n const start = Math.max(0, opening.offset - half);\n const end = Math.min(len, opening.offset + half);\n return {\n start: pointAlongWall(wall, start),\n end: pointAlongWall(wall, end),\n center: pointAlongWall(wall, opening.offset),\n };\n}\n\n/** Whether an opening fits entirely within the wall it is attached to. */\nexport function openingFits(wall: WallLike, opening: OpeningLike): boolean {\n const len = wallLength(wall);\n return (\n opening.offset - opening.width / 2 >= -1e-6 &&\n opening.offset + opening.width / 2 <= len + 1e-6\n );\n}\n\nexport interface WallOpeningInput extends OpeningLike {\n /** Bottom of the opening above the floor. */\n sillHeight: number;\n /** Opening height. */\n height: number;\n}\n\n/**\n * A solid box of the wall expressed in wall-local coordinates: `along` runs\n * from the wall start, `z` is vertical. The 3D renderer extrudes each box by\n * the wall thickness. This is how openings are \"cut\" without CSG — the wall is\n * decomposed into the solid pieces that remain around its openings.\n */\nexport interface WallBox {\n along0: number;\n along1: number;\n z0: number;\n z1: number;\n}\n\nexport function wallBoxes(\n wall: WallLike,\n openings: WallOpeningInput[],\n wallHeight: number,\n /**\n * Extend the solid wall by this much past each end. Pass `thickness / 2` so\n * walls meeting at a corner overlap and fill the corner square — otherwise\n * each centerline box stops short and leaves a notch.\n */\n extendEnds = 0,\n): WallBox[] {\n const len = wallLength(wall);\n const lo = -extendEnds;\n const hi = len + extendEnds;\n if (openings.length === 0) {\n return [{ along0: lo, along1: hi, z0: 0, z1: wallHeight }];\n }\n const sorted = [...openings]\n .map((o) => ({\n a0: Math.max(0, o.offset - o.width / 2),\n a1: Math.min(len, o.offset + o.width / 2),\n sill: Math.max(0, o.sillHeight),\n top: Math.min(wallHeight, o.sillHeight + o.height),\n }))\n .sort((a, b) => a.a0 - b.a0);\n\n const boxes: WallBox[] = [];\n let cursor = lo;\n for (const o of sorted) {\n if (o.a0 > cursor) {\n boxes.push({ along0: cursor, along1: o.a0, z0: 0, z1: wallHeight });\n }\n // Lintel above the opening.\n if (o.top < wallHeight) {\n boxes.push({ along0: o.a0, along1: o.a1, z0: o.top, z1: wallHeight });\n }\n // Sill below the opening (windows).\n if (o.sill > 0) {\n boxes.push({ along0: o.a0, along1: o.a1, z0: 0, z1: o.sill });\n }\n cursor = Math.max(cursor, o.a1);\n }\n if (cursor < hi) {\n boxes.push({ along0: cursor, along1: hi, z0: 0, z1: wallHeight });\n }\n return boxes;\n}\n","import { type Vec2 } from \"@react-arch/shared\";\nimport { distance, lerp } from \"./vec2.js\";\nimport { projectPointOnSegment } from \"./vec2.js\";\n\nexport type SnapKind = \"endpoint\" | \"midpoint\" | \"grid\" | \"perpendicular\";\n\nexport interface SnapResult {\n point: Vec2;\n kind: SnapKind;\n distance: number;\n}\n\nexport interface SnapSegment {\n a: Vec2;\n b: Vec2;\n}\n\nexport function snapToGrid(point: Vec2, gridSize: number): Vec2 {\n if (gridSize <= 0) return point;\n return [\n Math.round(point[0] / gridSize) * gridSize,\n Math.round(point[1] / gridSize) * gridSize,\n ];\n}\n\n/**\n * Find the best snap candidate near `point`. Endpoint and midpoint snaps take\n * priority over grid snaps within the given pixel/world tolerance.\n */\nexport function findSnap(\n point: Vec2,\n segments: SnapSegment[],\n options: { tolerance: number; gridSize?: number } = { tolerance: 0.2 },\n): SnapResult | null {\n const candidates: SnapResult[] = [];\n for (const seg of segments) {\n for (const ep of [seg.a, seg.b]) {\n const d = distance(point, ep);\n if (d <= options.tolerance) {\n candidates.push({ point: ep, kind: \"endpoint\", distance: d });\n }\n }\n const mid = lerp(seg.a, seg.b, 0.5);\n const dm = distance(point, mid);\n if (dm <= options.tolerance) {\n candidates.push({ point: mid, kind: \"midpoint\", distance: dm });\n }\n const proj = projectPointOnSegment(point, seg.a, seg.b);\n if (proj.distance <= options.tolerance) {\n candidates.push({\n point: proj.point,\n kind: \"perpendicular\",\n distance: proj.distance,\n });\n }\n }\n\n if (candidates.length > 0) {\n candidates.sort((a, b) => {\n const rank: Record<SnapKind, number> = {\n endpoint: 0,\n midpoint: 1,\n perpendicular: 2,\n grid: 3,\n };\n if (rank[a.kind] !== rank[b.kind]) return rank[a.kind] - rank[b.kind];\n return a.distance - b.distance;\n });\n return candidates[0]!;\n }\n\n if (options.gridSize) {\n const gp = snapToGrid(point, options.gridSize);\n return { point: gp, kind: \"grid\", distance: distance(point, gp) };\n }\n return null;\n}\n"],"mappings":";;AAEA,MAAa,OAAO,GAAS,MAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;AACxE,MAAa,OAAO,GAAS,MAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;AACxE,MAAa,SAAS,GAAS,MAAoB,CAAC,EAAE,KAAK,GAAG,EAAE,KAAK,CAAC;AACtE,MAAa,OAAO,GAAS,MAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;AACxE,MAAa,SAAS,GAAS,MAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;AAC1E,MAAa,UAAU,MAAoB,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE;AAChE,MAAa,YAAY,GAAS,MAAoB,OAAO,IAAI,GAAG,CAAC,CAAC;AAEtE,SAAgB,UAAU,GAAe;CACvC,MAAM,MAAM,OAAO,CAAC;CACpB,IAAI,MAAMA,WAAS,OAAO,CAAC,GAAG,CAAC;CAC/B,OAAO,CAAC,EAAE,KAAK,KAAK,EAAE,KAAK,GAAG;AAChC;;AAGA,SAAgB,OAAO,GAAe;CACpC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;AACrB;AAEA,SAAgB,KAAK,GAAS,GAAS,GAAiB;CACtD,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC;AAC5D;AAEA,SAAgB,MAAM,GAAS,GAAiB;CAC9C,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;AAC5C;AAEA,SAAgB,OAAO,GAAS,GAAS,MAAMA,WAAkB;CAC/D,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK;AAClE;;AAGA,SAAgB,sBACd,GACA,GACA,GAC8C;CAC9C,MAAM,KAAK,IAAI,GAAG,CAAC;CACnB,MAAM,OAAO,IAAI,IAAI,EAAE;CACvB,IAAI,OAAOA,WAAS,OAAO;EAAE,OAAO;EAAG,GAAG;EAAG,UAAU,SAAS,GAAG,CAAC;CAAE;CACtE,IAAI,IAAI,IAAI,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI;CAC7B,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;CAC9B,MAAM,QAAQ,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC;CACjC,OAAO;EAAE;EAAO;EAAG,UAAU,SAAS,GAAG,KAAK;CAAE;AAClD;;;;;;;AClCA,SAAgB,iBAAiB,IAAa,IAA0B;CACtE,MAAM,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC;CACxB,MAAM,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC;CACxB,MAAM,QAAQ,MAAM,GAAG,CAAC;CACxB,IAAI,KAAK,IAAI,KAAK,IAAIC,WAAS,OAAO;CAEtC,MAAM,IAAI,MADC,IAAI,GAAG,GAAG,GAAG,CACP,GAAG,CAAC,IAAI;CACzB,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,IAAI,EAAE,EAAE;AAChD;;;;AAKA,SAAgB,oBAAoB,IAAa,IAA0B;CACzE,MAAM,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC;CACxB,MAAM,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC;CACxB,MAAM,QAAQ,MAAM,GAAG,CAAC;CACxB,IAAI,KAAK,IAAI,KAAK,IAAIA,WAAS,OAAO;CACtC,MAAM,KAAK,IAAI,GAAG,GAAG,GAAG,CAAC;CACzB,MAAM,IAAI,MAAM,IAAI,CAAC,IAAI;CACzB,MAAM,IAAI,MAAM,IAAI,CAAC,IAAI;CACzB,IAAI,IAAI,CAACA,aAAW,IAAI,IAAIA,aAAW,IAAI,CAACA,aAAW,IAAI,IAAIA,WAC7D,OAAO;CAET,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,KAAK,IAAI,EAAE,EAAE;AAChD;;;;AClCA,SAAgB,WAAW,SAAyB;CAClD,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,IAAI,QAAQ;EAClB,MAAM,IAAI,SAAS,IAAI,KAAK,QAAQ;EACpC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;CAChC;CACA,OAAO,MAAM;AACf;AAEA,SAAgB,YAAY,SAAyB;CACnD,OAAO,KAAK,IAAI,WAAW,OAAO,CAAC;AACrC;AAEA,SAAgB,gBAAgB,SAAuB;CACrD,IAAI,KAAK;CACT,IAAI,KAAK;CACT,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,KAAK,QAAQ;EACnB,MAAM,KAAK,SAAS,IAAI,KAAK,QAAQ;EACrC,MAAM,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG;EACrC,OAAO,GAAG,KAAK,GAAG,MAAM;EACxB,OAAO,GAAG,KAAK,GAAG,MAAM;EACxB,KAAK;CACP;CACA,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM;EAEtB,MAAM,IAAI,QAAQ,UAAU;EAC5B,OAAO,CACL,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,GACxC,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,IAAI,CAAC,IAAI,CAC1C;CACF;CACA,KAAK;CACL,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;AACxB;AAEA,SAAgB,YAAY,SAA0B;CACpD,OAAO,WAAW,OAAO,IAAI;AAC/B;AAEA,SAAgB,uBAAuB,SAAyB;CAC9D,OAAO,YAAY,OAAO,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,IAAI;AACzD;;AAGA,SAAgB,OAAO,QAKrB;CACA,IAAI,OAAO,WAAW,GACpB,OAAO;EAAE,KAAK,CAAC,GAAG,CAAC;EAAG,KAAK,CAAC,GAAG,CAAC;EAAG,OAAO;EAAG,QAAQ;CAAE;CAEzD,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CACX,IAAI,OAAO;CACX,KAAK,MAAM,CAAC,GAAG,MAAM,QAAQ;EAC3B,IAAI,IAAI,MAAM,OAAO;EACrB,IAAI,IAAI,MAAM,OAAO;EACrB,IAAI,IAAI,MAAM,OAAO;EACrB,IAAI,IAAI,MAAM,OAAO;CACvB;CACA,OAAO;EACL,KAAK,CAAC,MAAM,IAAI;EAChB,KAAK,CAAC,MAAM,IAAI;EAChB,OAAO,OAAO;EACd,QAAQ,OAAO;CACjB;AACF;AAEA,SAAgB,eAAe,OAAa,SAA0B;CACpE,IAAI,SAAS;CACb,MAAM,CAAC,IAAI,MAAM;CACjB,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,SAAS,GAAG,IAAI,QAAQ,QAAQ,IAAI,KAAK;EACnE,MAAM,IAAI,QAAQ;EAClB,MAAM,IAAI,QAAQ;EAIlB,IAFE,EAAE,KAAK,OAAO,EAAE,KAAK,MACrB,MAAO,EAAE,KAAK,EAAE,OAAO,KAAK,EAAE,OAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAC1C,SAAS,CAAC;CAC3B;CACA,OAAO;AACT;;;AChFA,SAAgB,WAAW,MAAwB;CACjD,OAAO,SAAS,KAAK,OAAO,KAAK,GAAG;AACtC;AAEA,SAAgB,cAAc,MAAsB;CAClD,OAAO,UAAU,IAAI,KAAK,KAAK,KAAK,KAAK,CAAC;AAC5C;;;;;AAMA,SAAgB,YAAY,MAAgB,aAAa,GAAW;CAClE,MAAM,MAAM,cAAc,IAAI;CAC9B,MAAM,IAAI,OAAO,GAAG;CACpB,MAAM,OAAO,KAAK,YAAY;CAC9B,MAAM,SAAS,MAAM,GAAG,IAAI;CAC5B,MAAM,YAAY,MAAM,GAAG,CAAC,IAAI;CAEhC,MAAM,QAAQ,IAAI,KAAK,OAAO,MAAM,KAAK,CAAC,UAAU,CAAC;CACrD,MAAM,MAAM,IAAI,KAAK,KAAK,MAAM,KAAK,UAAU,CAAC;CAChD,OAAO;EACL,IAAI,OAAO,SAAS;EACpB,IAAI,KAAK,SAAS;EAClB,IAAI,KAAK,MAAM;EACf,IAAI,OAAO,MAAM;CACnB;AACF;;AAGA,SAAgB,eAAe,MAAgB,QAAsB;CACnE,MAAM,MAAM,cAAc,IAAI;CAC9B,OAAO,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM,CAAC;AAC3C;;;;AAUA,SAAgB,YACd,MACA,SAC0C;CAC1C,MAAM,MAAM,WAAW,IAAI;CAC3B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,KAAK,IAAI,GAAG,QAAQ,SAAS,IAAI;CAC/C,MAAM,MAAM,KAAK,IAAI,KAAK,QAAQ,SAAS,IAAI;CAC/C,OAAO;EACL,OAAO,eAAe,MAAM,KAAK;EACjC,KAAK,eAAe,MAAM,GAAG;EAC7B,QAAQ,eAAe,MAAM,QAAQ,MAAM;CAC7C;AACF;;AAGA,SAAgB,YAAY,MAAgB,SAA+B;CACzE,MAAM,MAAM,WAAW,IAAI;CAC3B,OACE,QAAQ,SAAS,QAAQ,QAAQ,KAAK,SACtC,QAAQ,SAAS,QAAQ,QAAQ,KAAK,MAAM;AAEhD;AAsBA,SAAgB,UACd,MACA,UACA,YAMA,aAAa,GACF;CACX,MAAM,MAAM,WAAW,IAAI;CAC3B,MAAM,KAAK,CAAC;CACZ,MAAM,KAAK,MAAM;CACjB,IAAI,SAAS,WAAW,GACtB,OAAO,CAAC;EAAE,QAAQ;EAAI,QAAQ;EAAI,IAAI;EAAG,IAAI;CAAW,CAAC;CAE3D,MAAM,SAAS,CAAC,GAAG,QAAQ,CAAC,CACzB,KAAK,OAAO;EACX,IAAI,KAAK,IAAI,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC;EACtC,IAAI,KAAK,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;EACxC,MAAM,KAAK,IAAI,GAAG,EAAE,UAAU;EAC9B,KAAK,KAAK,IAAI,YAAY,EAAE,aAAa,EAAE,MAAM;CACnD,EAAE,CAAC,CACF,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;CAE7B,MAAM,QAAmB,CAAC;CAC1B,IAAI,SAAS;CACb,KAAK,MAAM,KAAK,QAAQ;EACtB,IAAI,EAAE,KAAK,QACT,MAAM,KAAK;GAAE,QAAQ;GAAQ,QAAQ,EAAE;GAAI,IAAI;GAAG,IAAI;EAAW,CAAC;EAGpE,IAAI,EAAE,MAAM,YACV,MAAM,KAAK;GAAE,QAAQ,EAAE;GAAI,QAAQ,EAAE;GAAI,IAAI,EAAE;GAAK,IAAI;EAAW,CAAC;EAGtE,IAAI,EAAE,OAAO,GACX,MAAM,KAAK;GAAE,QAAQ,EAAE;GAAI,QAAQ,EAAE;GAAI,IAAI;GAAG,IAAI,EAAE;EAAK,CAAC;EAE9D,SAAS,KAAK,IAAI,QAAQ,EAAE,EAAE;CAChC;CACA,IAAI,SAAS,IACX,MAAM,KAAK;EAAE,QAAQ;EAAQ,QAAQ;EAAI,IAAI;EAAG,IAAI;CAAW,CAAC;CAElE,OAAO;AACT;;;AC7HA,SAAgB,WAAW,OAAa,UAAwB;CAC9D,IAAI,YAAY,GAAG,OAAO;CAC1B,OAAO,CACL,KAAK,MAAM,MAAM,KAAK,QAAQ,IAAI,UAClC,KAAK,MAAM,MAAM,KAAK,QAAQ,IAAI,QACpC;AACF;;;;;AAMA,SAAgB,SACd,OACA,UACA,UAAoD,EAAE,WAAW,GAAI,GAClD;CACnB,MAAM,aAA2B,CAAC;CAClC,KAAK,MAAM,OAAO,UAAU;EAC1B,KAAK,MAAM,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG;GAC/B,MAAM,IAAI,SAAS,OAAO,EAAE;GAC5B,IAAI,KAAK,QAAQ,WACf,WAAW,KAAK;IAAE,OAAO;IAAI,MAAM;IAAY,UAAU;GAAE,CAAC;EAEhE;EACA,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI,GAAG,EAAG;EAClC,MAAM,KAAK,SAAS,OAAO,GAAG;EAC9B,IAAI,MAAM,QAAQ,WAChB,WAAW,KAAK;GAAE,OAAO;GAAK,MAAM;GAAY,UAAU;EAAG,CAAC;EAEhE,MAAM,OAAO,sBAAsB,OAAO,IAAI,GAAG,IAAI,CAAC;EACtD,IAAI,KAAK,YAAY,QAAQ,WAC3B,WAAW,KAAK;GACd,OAAO,KAAK;GACZ,MAAM;GACN,UAAU,KAAK;EACjB,CAAC;CAEL;CAEA,IAAI,WAAW,SAAS,GAAG;EACzB,WAAW,MAAM,GAAG,MAAM;GACxB,MAAM,OAAiC;IACrC,UAAU;IACV,UAAU;IACV,eAAe;IACf,MAAM;GACR;GACA,IAAI,KAAK,EAAE,UAAU,KAAK,EAAE,OAAO,OAAO,KAAK,EAAE,QAAQ,KAAK,EAAE;GAChE,OAAO,EAAE,WAAW,EAAE;EACxB,CAAC;EACD,OAAO,WAAW;CACpB;CAEA,IAAI,QAAQ,UAAU;EACpB,MAAM,KAAK,WAAW,OAAO,QAAQ,QAAQ;EAC7C,OAAO;GAAE,OAAO;GAAI,MAAM;GAAQ,UAAU,SAAS,OAAO,EAAE;EAAE;CAClE;CACA,OAAO;AACT"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@react-arch/geometry",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@react-arch/shared": "0.1.0"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.2",
19
+ "vitest": "^2.1.8"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/react-arch/react-arch.git",
30
+ "directory": "packages/geometry"
31
+ },
32
+ "license": "MIT",
33
+ "scripts": {
34
+ "typecheck": "tsc --noEmit",
35
+ "test": "vitest run --passWithNoTests",
36
+ "lint": "oxlint src",
37
+ "build": "tsdown"
38
+ }
39
+ }