react-svg-canvas 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.
Files changed (51) hide show
  1. package/LICENSE +8 -8
  2. package/README.md +579 -2
  3. package/lib/geometry/bounds.d.ts +44 -0
  4. package/lib/geometry/bounds.js +157 -0
  5. package/lib/geometry/index.d.ts +6 -0
  6. package/lib/geometry/index.js +7 -0
  7. package/lib/geometry/math.d.ts +44 -0
  8. package/lib/geometry/math.js +80 -0
  9. package/lib/geometry/transforms.d.ts +52 -0
  10. package/lib/geometry/transforms.js +122 -0
  11. package/lib/hooks/index.d.ts +5 -0
  12. package/lib/hooks/index.js +6 -0
  13. package/lib/hooks/useDraggable.d.ts +32 -0
  14. package/lib/hooks/useDraggable.js +162 -0
  15. package/lib/hooks/useResizable.d.ts +33 -0
  16. package/lib/hooks/useResizable.js +78 -0
  17. package/lib/index.d.ts +9 -0
  18. package/lib/index.js +16 -0
  19. package/lib/queries/index.d.ts +4 -0
  20. package/lib/queries/index.js +5 -0
  21. package/lib/queries/spatial.d.ts +51 -0
  22. package/lib/queries/spatial.js +107 -0
  23. package/lib/selection/ResizeHandle.d.ts +18 -0
  24. package/lib/selection/ResizeHandle.js +14 -0
  25. package/lib/selection/SelectionBox.d.ts +21 -0
  26. package/lib/selection/SelectionBox.js +21 -0
  27. package/lib/selection/index.d.ts +6 -0
  28. package/lib/selection/index.js +7 -0
  29. package/lib/selection/useSelection.d.ts +43 -0
  30. package/lib/selection/useSelection.js +101 -0
  31. package/lib/snapping/SnapDebugOverlay.d.ts +17 -0
  32. package/lib/snapping/SnapDebugOverlay.js +105 -0
  33. package/lib/snapping/SnapGuides.d.ts +20 -0
  34. package/lib/snapping/SnapGuides.js +184 -0
  35. package/lib/snapping/index.d.ts +12 -0
  36. package/lib/snapping/index.js +17 -0
  37. package/lib/snapping/rotation-utils.d.ts +67 -0
  38. package/lib/snapping/rotation-utils.js +204 -0
  39. package/lib/snapping/snap-engine.d.ts +40 -0
  40. package/lib/snapping/snap-engine.js +592 -0
  41. package/lib/snapping/snap-targets.d.ts +40 -0
  42. package/lib/snapping/snap-targets.js +272 -0
  43. package/lib/snapping/types.d.ts +178 -0
  44. package/lib/snapping/types.js +37 -0
  45. package/lib/snapping/useSnapping.d.ts +52 -0
  46. package/lib/snapping/useSnapping.js +121 -0
  47. package/lib/svgcanvas.d.ts +26 -1
  48. package/lib/svgcanvas.js +53 -18
  49. package/lib/types.d.ts +67 -0
  50. package/lib/types.js +14 -0
  51. package/package.json +9 -8
package/LICENSE CHANGED
@@ -1,18 +1,18 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Szilard Hajba <szilu@symbion.hu>
3
+ Copyright (c) 2024 Szilárd Hajba
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of
6
- this software and associated documentation files (the 'Software'), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
- of the Software, and to permit persons to whom the Software is furnished to do
10
- so, subject to the following conditions:
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
11
 
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
package/README.md CHANGED
@@ -1,4 +1,581 @@
1
- React SVG Canvas
2
- ================
1
+ # react-svg-canvas
3
2
 
3
+ A React library for building interactive SVG canvas applications with pan, zoom, selection, drag-and-drop, resize, and Figma-style snapping.
4
4
 
5
+ ## Features
6
+
7
+ - **Pan & Zoom** - Middle mouse/touch panning, mouse wheel/pinch-to-zoom
8
+ - **Touch Support** - Full touch event handling for mobile devices
9
+ - **Selection System** - Multi-select, rectangle selection, selection bounds
10
+ - **Drag & Drop** - Smooth dragging with window-level event handling
11
+ - **Resize Handles** - 8-point resize with min/max constraints
12
+ - **Snapping** - Figma-style snapping to edges, centers, grid, and matching sizes
13
+ - **Geometry Utilities** - Bounds operations, transforms, coordinate conversion
14
+ - **Spatial Queries** - Hit testing, rectangle selection, culling
15
+ - **TypeScript** - Full type definitions included
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install react-svg-canvas
21
+ # or
22
+ pnpm add react-svg-canvas
23
+ # or
24
+ yarn add react-svg-canvas
25
+ ```
26
+
27
+ **Peer Dependencies:** React 19.x
28
+
29
+ ## Quick Start
30
+
31
+ ```tsx
32
+ import { SvgCanvas, useSvgCanvas } from 'react-svg-canvas'
33
+
34
+ function MyCanvas() {
35
+ return (
36
+ <SvgCanvas
37
+ className="my-canvas"
38
+ style={{ width: '100%', height: '100%' }}
39
+ >
40
+ <rect x={100} y={100} width={200} height={150} fill="#3b82f6" />
41
+ <circle cx={400} cy={200} r={50} fill="#ef4444" />
42
+ </SvgCanvas>
43
+ )
44
+ }
45
+ ```
46
+
47
+ ## API Reference
48
+
49
+ ### SvgCanvas
50
+
51
+ The main canvas component that provides pan, zoom, and coordinate transformation.
52
+
53
+ ```tsx
54
+ import { SvgCanvas, SvgCanvasHandle } from 'react-svg-canvas'
55
+
56
+ function App() {
57
+ const canvasRef = useRef<SvgCanvasHandle>(null)
58
+
59
+ return (
60
+ <SvgCanvas
61
+ ref={canvasRef}
62
+ className="canvas"
63
+ style={{ width: '100vw', height: '100vh' }}
64
+ fixed={<MyToolbar />} // Renders in screen space (not transformed)
65
+ onToolStart={(e) => console.log('Tool start:', e.x, e.y)}
66
+ onToolMove={(e) => console.log('Tool move:', e.x, e.y)}
67
+ onToolEnd={() => console.log('Tool end')}
68
+ onContextReady={(ctx) => console.log('Scale:', ctx.scale)}
69
+ >
70
+ {/* Children render in canvas space (transformed) */}
71
+ <MyShapes />
72
+ </SvgCanvas>
73
+ )
74
+ }
75
+ ```
76
+
77
+ #### Props
78
+
79
+ | Prop | Type | Description |
80
+ |------|------|-------------|
81
+ | `className` | `string` | CSS class for the SVG element |
82
+ | `style` | `CSSProperties` | Inline styles for the SVG element |
83
+ | `children` | `ReactNode` | Content rendered in canvas space (pan/zoom applied) |
84
+ | `fixed` | `ReactNode` | Content rendered in screen space (UI overlays) |
85
+ | `onToolStart` | `(e: ToolEvent) => void` | Called on left mouse/touch start |
86
+ | `onToolMove` | `(e: ToolEvent) => void` | Called during drag |
87
+ | `onToolEnd` | `() => void` | Called on mouse/touch end |
88
+ | `onContextReady` | `(ctx: SvgCanvasContext) => void` | Called when context changes (zoom, pan) |
89
+
90
+ #### Imperative Handle
91
+
92
+ ```tsx
93
+ const canvasRef = useRef<SvgCanvasHandle>(null)
94
+
95
+ // Center viewport on a point
96
+ canvasRef.current?.centerOn(100, 100, 1.5) // x, y, optional zoom
97
+
98
+ // Fit a rectangle in view
99
+ canvasRef.current?.centerOnRect(0, 0, 500, 400, 50) // x, y, w, h, padding
100
+
101
+ // Get/set transform matrix
102
+ const matrix = canvasRef.current?.getMatrix()
103
+ canvasRef.current?.setMatrix([1, 0, 0, 1, 0, 0])
104
+ ```
105
+
106
+ #### Interaction Controls
107
+
108
+ | Input | Action |
109
+ |-------|--------|
110
+ | Left mouse | Tool events (onToolStart/Move/End) |
111
+ | Middle mouse | Pan canvas |
112
+ | Mouse wheel | Zoom in/out |
113
+ | Single touch | Tool events or pan |
114
+ | Two-finger pinch | Zoom |
115
+
116
+ ---
117
+
118
+ ### useSvgCanvas
119
+
120
+ Hook to access canvas context from child components.
121
+
122
+ ```tsx
123
+ function MyShape() {
124
+ const { svg, matrix, scale, translateTo, translateFrom } = useSvgCanvas()
125
+
126
+ // Convert screen coords to canvas coords
127
+ const [canvasX, canvasY] = translateTo(screenX, screenY)
128
+
129
+ // Convert canvas coords to screen coords
130
+ const [screenX, screenY] = translateFrom(canvasX, canvasY)
131
+
132
+ return <rect x={100} y={100} width={100 / scale} height={100 / scale} />
133
+ }
134
+ ```
135
+
136
+ ---
137
+
138
+ ### Selection System
139
+
140
+ #### useSelection
141
+
142
+ Manages selection state for canvas objects.
143
+
144
+ ```tsx
145
+ import { useSelection, SpatialObject } from 'react-svg-canvas'
146
+
147
+ interface MyObject extends SpatialObject {
148
+ id: string
149
+ bounds: Bounds
150
+ color: string
151
+ }
152
+
153
+ function Canvas({ objects }: { objects: MyObject[] }) {
154
+ const {
155
+ selectedIds,
156
+ selectedObjects,
157
+ selectionBounds,
158
+ hasSelection,
159
+ select,
160
+ selectMultiple,
161
+ toggle,
162
+ clear,
163
+ selectAll,
164
+ selectInRect,
165
+ isSelected
166
+ } = useSelection({ objects, onChange: (ids) => console.log('Selection:', ids) })
167
+
168
+ return (
169
+ <SvgCanvas>
170
+ {objects.map(obj => (
171
+ <rect
172
+ key={obj.id}
173
+ {...obj.bounds}
174
+ fill={isSelected(obj.id) ? 'blue' : obj.color}
175
+ onClick={(e) => select(obj.id, e.shiftKey)}
176
+ />
177
+ ))}
178
+ {selectionBounds && (
179
+ <SelectionBox bounds={selectionBounds} onResizeStart={handleResize} />
180
+ )}
181
+ </SvgCanvas>
182
+ )
183
+ }
184
+ ```
185
+
186
+ #### SelectionBox
187
+
188
+ Renders a selection rectangle with resize handles.
189
+
190
+ ```tsx
191
+ import { SelectionBox } from 'react-svg-canvas'
192
+
193
+ <SelectionBox
194
+ bounds={{ x: 100, y: 100, width: 200, height: 150 }}
195
+ rotation={45}
196
+ stroke="#0066ff"
197
+ strokeDasharray="4,4"
198
+ showHandles={true}
199
+ handleSize={8}
200
+ onResizeStart={(handle, e) => console.log('Resize:', handle)}
201
+ />
202
+ ```
203
+
204
+ ---
205
+
206
+ ### Interaction Hooks
207
+
208
+ #### useDraggable
209
+
210
+ Provides smooth drag interaction with window-level events.
211
+
212
+ ```tsx
213
+ import { useDraggable, svgTransformCoordinates } from 'react-svg-canvas'
214
+
215
+ function DraggableRect({ x, y, onMove }) {
216
+ const { isDragging, dragProps } = useDraggable({
217
+ onDragStart: (e) => console.log('Start:', e.x, e.y),
218
+ onDragMove: (e) => onMove(e.deltaX, e.deltaY),
219
+ onDragEnd: (e) => console.log('End'),
220
+ transformCoordinates: svgTransformCoordinates // For SVG coordinate space
221
+ })
222
+
223
+ return (
224
+ <rect
225
+ x={x} y={y}
226
+ width={100} height={80}
227
+ fill={isDragging ? 'orange' : 'blue'}
228
+ style={{ cursor: 'move' }}
229
+ {...dragProps}
230
+ />
231
+ )
232
+ }
233
+ ```
234
+
235
+ #### useResizable
236
+
237
+ Provides resize interaction for selected objects.
238
+
239
+ ```tsx
240
+ import { useResizable } from 'react-svg-canvas'
241
+
242
+ function ResizableRect({ bounds, onResize }) {
243
+ const { isResizing, activeHandle, handleResizeStart } = useResizable({
244
+ bounds,
245
+ minWidth: 50,
246
+ minHeight: 50,
247
+ onResize: (e) => onResize(e.bounds),
248
+ onResizeEnd: (e) => console.log('Final bounds:', e.bounds)
249
+ })
250
+
251
+ return (
252
+ <SelectionBox
253
+ bounds={bounds}
254
+ onResizeStart={handleResizeStart}
255
+ />
256
+ )
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ### Snapping System
263
+
264
+ Figma-style snapping with visual guide lines.
265
+
266
+ #### useSnapping
267
+
268
+ ```tsx
269
+ import { useSnapping, SnapGuides, DEFAULT_SNAP_CONFIG } from 'react-svg-canvas'
270
+
271
+ function Canvas({ objects }) {
272
+ const { svg, translateFrom } = useSvgCanvas()
273
+ const viewBounds = { x: 0, y: 0, width: 1000, height: 800 }
274
+
275
+ const { snapDrag, snapResize, activeSnaps, allCandidates, clearSnaps } = useSnapping({
276
+ objects,
277
+ config: DEFAULT_SNAP_CONFIG,
278
+ viewBounds
279
+ })
280
+
281
+ function handleDrag(objectId, bounds, delta, grabPoint) {
282
+ const result = snapDrag({
283
+ bounds: { ...bounds, rotation: 0 },
284
+ objectId,
285
+ delta,
286
+ grabPoint
287
+ })
288
+ // result.position contains snapped coordinates
289
+ // result.activeSnaps contains active snap info
290
+ }
291
+
292
+ return (
293
+ <SvgCanvas
294
+ fixed={
295
+ <SnapGuides
296
+ activeSnaps={activeSnaps}
297
+ config={DEFAULT_SNAP_CONFIG.guides}
298
+ viewBounds={viewBounds}
299
+ transformPoint={translateFrom}
300
+ />
301
+ }
302
+ >
303
+ {/* Your objects */}
304
+ </SvgCanvas>
305
+ )
306
+ }
307
+ ```
308
+
309
+ #### Snap Configuration
310
+
311
+ ```tsx
312
+ const config: SnapConfiguration = {
313
+ enabled: true,
314
+ snapToGrid: true,
315
+ snapToObjects: true,
316
+ snapToSizes: true, // Snap to matching widths/heights
317
+ gridSize: 10,
318
+ snapThreshold: 8, // Pixels within which snapping activates
319
+ weights: {
320
+ distance: 10, // How much distance affects snap priority
321
+ direction: 3, // Movement direction influence
322
+ velocity: 2, // Faster movement = less sticky
323
+ grabProximity: 5, // Snaps near grab point prioritized
324
+ hierarchy: 4, // Parent/sibling preference
325
+ edgePriority: 1.2,
326
+ centerPriority: 1.0,
327
+ gridPriority: 0.8,
328
+ sizePriority: 0.9
329
+ },
330
+ guides: {
331
+ color: '#ff3366',
332
+ strokeWidth: 1,
333
+ showDistanceIndicators: true
334
+ },
335
+ debug: {
336
+ enabled: false,
337
+ showTopN: 5,
338
+ showScores: true,
339
+ showScoreBreakdown: false
340
+ }
341
+ }
342
+ ```
343
+
344
+ ---
345
+
346
+ ### Geometry Utilities
347
+
348
+ #### Bounds Operations
349
+
350
+ ```tsx
351
+ import {
352
+ getBoundsCenter,
353
+ expandBounds,
354
+ unionBounds,
355
+ unionAllBounds,
356
+ boundsIntersect,
357
+ boundsContains,
358
+ pointInBounds,
359
+ boundsFromPoints,
360
+ getHandlePositions,
361
+ resizeBounds
362
+ } from 'react-svg-canvas'
363
+
364
+ // Get center point
365
+ const center = getBoundsCenter({ x: 0, y: 0, width: 100, height: 100 })
366
+ // { x: 50, y: 50 }
367
+
368
+ // Expand bounds by margin
369
+ const expanded = expandBounds(bounds, 10)
370
+
371
+ // Union of two bounds
372
+ const combined = unionBounds(boundsA, boundsB)
373
+
374
+ // Check intersection
375
+ if (boundsIntersect(selection, object.bounds)) {
376
+ // Object is selected
377
+ }
378
+
379
+ // Create bounds from drag rectangle
380
+ const selectionRect = boundsFromPoints(startPoint, endPoint)
381
+
382
+ // Resize bounds from handle drag
383
+ const newBounds = resizeBounds(originalBounds, 'se', deltaX, deltaY, minW, minH)
384
+ ```
385
+
386
+ #### Transforms
387
+
388
+ ```tsx
389
+ import {
390
+ transformPoint,
391
+ invertTransform,
392
+ composeTransforms,
393
+ matrixToTransform,
394
+ transformToMatrix,
395
+ getAbsolutePosition
396
+ } from 'react-svg-canvas'
397
+
398
+ // Apply transform to point
399
+ const worldPoint = transformPoint({ x: 10, y: 10 }, transform)
400
+
401
+ // Convert SVG matrix to transform object
402
+ const transform = matrixToTransform([1, 0, 0, 1, 100, 50])
403
+
404
+ // Get absolute position walking up hierarchy
405
+ const absPos = getAbsolutePosition(item, (item) => itemsById[item.parentId])
406
+ ```
407
+
408
+ #### Math Utilities
409
+
410
+ ```tsx
411
+ import {
412
+ rotatePoint,
413
+ scalePoint,
414
+ distance,
415
+ snapToGrid,
416
+ snapPointToGrid,
417
+ lerp,
418
+ clamp,
419
+ normalizeAngle,
420
+ degToRad,
421
+ radToDeg
422
+ } from 'react-svg-canvas'
423
+
424
+ // Rotate point around center
425
+ const rotated = rotatePoint({ x: 100, y: 0 }, { x: 0, y: 0 }, 90)
426
+
427
+ // Snap to grid
428
+ const snapped = snapPointToGrid({ x: 123, y: 456 }, 10)
429
+ // { x: 120, y: 460 }
430
+ ```
431
+
432
+ ---
433
+
434
+ ### Spatial Queries
435
+
436
+ ```tsx
437
+ import {
438
+ getObjectsAtPoint,
439
+ getTopmostAtPoint,
440
+ getObjectsIntersectingRect,
441
+ getObjectsContainedInRect,
442
+ getSelectionBounds,
443
+ getObjectsInView,
444
+ findNearestObject,
445
+ getObjectsInRadius
446
+ } from 'react-svg-canvas'
447
+
448
+ // Hit testing
449
+ const clicked = getTopmostAtPoint(objects, { x: mouseX, y: mouseY })
450
+
451
+ // Rectangle selection
452
+ const selected = getObjectsIntersectingRect(objects, selectionRect)
453
+
454
+ // Viewport culling (render only visible objects)
455
+ const visible = getObjectsInView(objects, viewBounds)
456
+
457
+ // Find nearest object
458
+ const nearest = findNearestObject(objects, cursorPos, maxDistance)
459
+ ```
460
+
461
+ ---
462
+
463
+ ## Types
464
+
465
+ ```tsx
466
+ interface Point {
467
+ x: number
468
+ y: number
469
+ }
470
+
471
+ interface Bounds {
472
+ x: number
473
+ y: number
474
+ width: number
475
+ height: number
476
+ }
477
+
478
+ interface Transform {
479
+ x: number
480
+ y: number
481
+ rotation: number
482
+ scaleX: number
483
+ scaleY: number
484
+ }
485
+
486
+ interface SpatialObject {
487
+ id: string
488
+ bounds: Bounds
489
+ }
490
+
491
+ type ResizeHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w'
492
+ ```
493
+
494
+ ---
495
+
496
+ ## Example: Complete Editor
497
+
498
+ ```tsx
499
+ import {
500
+ SvgCanvas,
501
+ SvgCanvasHandle,
502
+ useSelection,
503
+ useDraggable,
504
+ useSnapping,
505
+ SelectionBox,
506
+ SnapGuides,
507
+ DEFAULT_SNAP_CONFIG
508
+ } from 'react-svg-canvas'
509
+
510
+ function Editor() {
511
+ const canvasRef = useRef<SvgCanvasHandle>(null)
512
+ const [objects, setObjects] = useState<MyObject[]>(initialObjects)
513
+
514
+ const selection = useSelection({
515
+ objects,
516
+ onChange: (ids) => console.log('Selected:', ids)
517
+ })
518
+
519
+ const snapping = useSnapping({
520
+ objects,
521
+ config: DEFAULT_SNAP_CONFIG,
522
+ viewBounds: { x: 0, y: 0, width: 1920, height: 1080 }
523
+ })
524
+
525
+ return (
526
+ <SvgCanvas
527
+ ref={canvasRef}
528
+ style={{ width: '100%', height: '100vh' }}
529
+ onToolStart={(e) => {
530
+ const hit = getTopmostAtPoint(objects, e)
531
+ if (hit) selection.select(hit.id, false)
532
+ else selection.clear()
533
+ }}
534
+ fixed={
535
+ <SnapGuides
536
+ activeSnaps={snapping.activeSnaps}
537
+ config={DEFAULT_SNAP_CONFIG.guides}
538
+ viewBounds={viewBounds}
539
+ />
540
+ }
541
+ >
542
+ {objects.map(obj => (
543
+ <DraggableShape
544
+ key={obj.id}
545
+ object={obj}
546
+ isSelected={selection.isSelected(obj.id)}
547
+ onMove={(delta) => {
548
+ const result = snapping.snapDrag({
549
+ bounds: obj.bounds,
550
+ objectId: obj.id,
551
+ delta,
552
+ grabPoint: { x: 0.5, y: 0.5 }
553
+ })
554
+ updateObject(obj.id, result.position)
555
+ }}
556
+ />
557
+ ))}
558
+
559
+ {selection.selectionBounds && (
560
+ <SelectionBox
561
+ bounds={selection.selectionBounds}
562
+ onResizeStart={handleResize}
563
+ />
564
+ )}
565
+ </SvgCanvas>
566
+ )
567
+ }
568
+ ```
569
+
570
+ ## Browser Support
571
+
572
+ - Modern browsers with ES2020 support
573
+ - Touch devices (iOS Safari, Android Chrome)
574
+
575
+ ## License
576
+
577
+ MIT License - see [LICENSE](./LICENSE) for details.
578
+
579
+ ## Author
580
+
581
+ Szilard Hajba <szilard@cloudillo.org>
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Bounds operations for SVG canvas
3
+ */
4
+ import type { Point, Bounds, HandlePosition, ResizeHandle } from '../types';
5
+ /**
6
+ * Get center of bounds
7
+ */
8
+ export declare function getBoundsCenter(bounds: Bounds): Point;
9
+ /**
10
+ * Expand bounds by margin
11
+ */
12
+ export declare function expandBounds(bounds: Bounds, margin: number): Bounds;
13
+ /**
14
+ * Union of two bounds
15
+ */
16
+ export declare function unionBounds(a: Bounds, b: Bounds): Bounds;
17
+ /**
18
+ * Union of multiple bounds
19
+ */
20
+ export declare function unionAllBounds(boundsArray: Bounds[]): Bounds | null;
21
+ /**
22
+ * Check if two bounds intersect (AABB collision)
23
+ */
24
+ export declare function boundsIntersect(a: Bounds, b: Bounds): boolean;
25
+ /**
26
+ * Check if point is inside bounds
27
+ */
28
+ export declare function pointInBounds(point: Point, bounds: Bounds): boolean;
29
+ /**
30
+ * Check if bounds A is completely inside bounds B
31
+ */
32
+ export declare function boundsContains(outer: Bounds, inner: Bounds): boolean;
33
+ /**
34
+ * Create bounds from two points (handles negative width/height)
35
+ */
36
+ export declare function boundsFromPoints(p1: Point, p2: Point): Bounds;
37
+ /**
38
+ * Get the 8 resize handle positions for a bounds
39
+ */
40
+ export declare function getHandlePositions(bounds: Bounds): HandlePosition[];
41
+ /**
42
+ * Calculate new bounds after resizing from a handle
43
+ */
44
+ export declare function resizeBounds(originalBounds: Bounds, handle: ResizeHandle, deltaX: number, deltaY: number, minWidth?: number, minHeight?: number): Bounds;