@pixerate/editor 0.36.0 → 0.38.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/THIRD_PARTY_NOTICES.md +17 -0
- package/dist/canvas/index.cjs +37 -25
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +3 -217
- package/dist/canvas/index.d.ts +3 -217
- package/dist/canvas/index.js +13 -1
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-BGNPT5PE.cjs → chunk-7EDLVBOO.cjs} +671 -19
- package/dist/chunk-7EDLVBOO.cjs.map +1 -0
- package/dist/{chunk-EGPIVUEF.js → chunk-ZCCU3VNI.js} +668 -16
- package/dist/chunk-ZCCU3VNI.js.map +1 -0
- package/dist/pathfinder-Bd87_lsQ.d.cts +994 -0
- package/dist/pathfinder-fLmmFOJw.d.ts +994 -0
- package/dist/vector/index.cjs +1900 -297
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +620 -8
- package/dist/vector/index.d.ts +620 -8
- package/dist/vector/index.js +1696 -93
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/bezier-BvqVmWMg.d.ts +0 -547
- package/dist/bezier-D8bsO-L0.d.cts +0 -547
- package/dist/chunk-BGNPT5PE.cjs.map +0 -1
- package/dist/chunk-EGPIVUEF.js.map +0 -1
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
import { P as Point } from './types-BFkYA4eF.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Polygon boolean operations (union, intersection, difference, xor and divide) on
|
|
5
|
+
* multi-polygons with holes.
|
|
6
|
+
*
|
|
7
|
+
* Algorithm (overlay + winding classification):
|
|
8
|
+
* 1. Every input edge is collected into one segment set. Vertices are merged through a
|
|
9
|
+
* tolerance-aware point pool, so points closer than `epsilon` become one vertex.
|
|
10
|
+
* 2. Segments are split at every crossing, T-junction and collinear overlap until no two
|
|
11
|
+
* segments cross (a planar arrangement). Splitting is repeated because snapping a new
|
|
12
|
+
* vertex can move a segment by up to `epsilon`.
|
|
13
|
+
* 3. Coincident segments (shared edges) are merged, keeping for every operand the signed
|
|
14
|
+
* number of its edges that run along the segment.
|
|
15
|
+
* 4. For every segment the winding number of each operand on its right side is computed
|
|
16
|
+
* with a ray cast from the segment midpoint (the segment itself excluded), and the left
|
|
17
|
+
* side follows from the signed multiplicities. The fill rule turns windings into
|
|
18
|
+
* inside / outside flags and the operation decides whether each side is in the result.
|
|
19
|
+
* 5. Segments with the result on exactly one side are kept, oriented with the result on
|
|
20
|
+
* their left, and traced into rings (taking the sharpest left turn at shared vertices,
|
|
21
|
+
* so rings that touch at a vertex stay separate simple rings). Rings with a positive
|
|
22
|
+
* signed area are outer rings; negative ones are holes, assigned to the smallest outer
|
|
23
|
+
* ring that contains them.
|
|
24
|
+
*
|
|
25
|
+
* Handles shared edges, touching vertices, collinear overlaps, holes, nested and disjoint
|
|
26
|
+
* inputs and self-intersecting input rings (resolved by the operand's fill rule).
|
|
27
|
+
*
|
|
28
|
+
* Limits: the overlay is O(n²) in the number of edges (pruned by a sweep on x), which is
|
|
29
|
+
* comfortable for canvas shapes (thousands of edges) but not for GIS-scale data. Features
|
|
30
|
+
* narrower than `epsilon` collapse. Coordinates must be finite.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** A closed ring of points. The closing point may be repeated or omitted. */
|
|
34
|
+
type PolygonRing = Point[];
|
|
35
|
+
/** A polygon: the outer ring followed by its holes. */
|
|
36
|
+
type PolygonWithHoles = PolygonRing[];
|
|
37
|
+
/** A set of polygons. */
|
|
38
|
+
type MultiPolygon = PolygonWithHoles[];
|
|
39
|
+
/** SVG fill rule used to decide which areas of a ring set are inside. */
|
|
40
|
+
type PolygonFillRule = 'nonzero' | 'evenodd';
|
|
41
|
+
/** Boolean operations. `xor` is Freeform's Exclude. */
|
|
42
|
+
type PolygonBooleanOperation = 'union' | 'intersection' | 'difference' | 'xor';
|
|
43
|
+
/**
|
|
44
|
+
* A raw ring set with a fill rule (e.g. flattened SVG path data). Ring orientation is
|
|
45
|
+
* significant for `nonzero`.
|
|
46
|
+
*/
|
|
47
|
+
interface PolygonOperand {
|
|
48
|
+
rings: PolygonRing[];
|
|
49
|
+
fillRule?: PolygonFillRule;
|
|
50
|
+
}
|
|
51
|
+
type PolygonInput = MultiPolygon | PolygonOperand;
|
|
52
|
+
interface PolygonBooleanOptions {
|
|
53
|
+
/**
|
|
54
|
+
* Distance below which points are treated as equal. Defaults to `1e-9` times the
|
|
55
|
+
* largest coordinate magnitude of the inputs (at least `1e-9`).
|
|
56
|
+
*/
|
|
57
|
+
epsilon?: number;
|
|
58
|
+
}
|
|
59
|
+
/** One region of `dividePolygons`, with the indices of the operands that cover it. */
|
|
60
|
+
interface PolygonPiece {
|
|
61
|
+
polygon: PolygonWithHoles;
|
|
62
|
+
operands: number[];
|
|
63
|
+
}
|
|
64
|
+
/** Signed shoelace area of a ring (positive when counter-clockwise in a y-up frame). */
|
|
65
|
+
declare function ringSignedArea(ring: readonly Point[]): number;
|
|
66
|
+
/** Area of a polygon (outer ring minus holes). */
|
|
67
|
+
declare function polygonArea(polygon: PolygonWithHoles): number;
|
|
68
|
+
/** Total area of a multi-polygon (holes subtracted). */
|
|
69
|
+
declare function multiPolygonArea(multi: MultiPolygon): number;
|
|
70
|
+
/** Axis-aligned bounds of a multi-polygon (or ring list), or `null` when empty. */
|
|
71
|
+
declare function multiPolygonBounds(input: MultiPolygon | PolygonRing[]): {
|
|
72
|
+
minX: number;
|
|
73
|
+
minY: number;
|
|
74
|
+
maxX: number;
|
|
75
|
+
maxY: number;
|
|
76
|
+
} | null;
|
|
77
|
+
/**
|
|
78
|
+
* Whether a point is inside a ring set under a fill rule. Points exactly on an edge may
|
|
79
|
+
* report either side.
|
|
80
|
+
*/
|
|
81
|
+
declare function pointInRings(point: Point, rings: readonly (readonly Point[])[], fillRule?: PolygonFillRule): boolean;
|
|
82
|
+
/** Whether a point is inside a multi-polygon (holes excluded). */
|
|
83
|
+
declare function pointInMultiPolygon(point: Point, multi: MultiPolygon): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Applies a boolean operation to any number of operands.
|
|
86
|
+
* - `union`: covered by any operand.
|
|
87
|
+
* - `intersection`: covered by every operand.
|
|
88
|
+
* - `difference`: the first operand minus all others.
|
|
89
|
+
* - `xor`: covered by an odd number of operands (Exclude).
|
|
90
|
+
*
|
|
91
|
+
* Returns simple rings: outer rings have a positive signed area, holes a negative one.
|
|
92
|
+
* Rings may touch at vertices but never cross.
|
|
93
|
+
*/
|
|
94
|
+
declare function polygonBoolean(operation: PolygonBooleanOperation, operands: PolygonInput[], options?: PolygonBooleanOptions): MultiPolygon;
|
|
95
|
+
declare const unionPolygons: (a: PolygonInput, b: PolygonInput, options?: PolygonBooleanOptions) => MultiPolygon;
|
|
96
|
+
declare const intersectPolygons: (a: PolygonInput, b: PolygonInput, options?: PolygonBooleanOptions) => MultiPolygon;
|
|
97
|
+
declare const subtractPolygons: (a: PolygonInput, b: PolygonInput, options?: PolygonBooleanOptions) => MultiPolygon;
|
|
98
|
+
declare const xorPolygons: (a: PolygonInput, b: PolygonInput, options?: PolygonBooleanOptions) => MultiPolygon;
|
|
99
|
+
/**
|
|
100
|
+
* Splits the overlay of the operands into its distinct regions (Freeform's Divide): every
|
|
101
|
+
* connected area covered by the same set of operands becomes one piece. Pieces are ordered
|
|
102
|
+
* by their operand set, then by size (largest first).
|
|
103
|
+
*/
|
|
104
|
+
declare function dividePolygons(operands: PolygonInput[], options?: PolygonBooleanOptions): PolygonPiece[];
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Canvas gradient models, parsing, presets, and SVG defs generation.
|
|
108
|
+
* Supports linear and radial gradients for shape fills, strokes, and freehand drawing.
|
|
109
|
+
*/
|
|
110
|
+
interface GradientStop {
|
|
111
|
+
offset: number;
|
|
112
|
+
color: string;
|
|
113
|
+
opacity?: number;
|
|
114
|
+
}
|
|
115
|
+
interface CanvasLinearGradient {
|
|
116
|
+
type: 'linear';
|
|
117
|
+
angle?: number;
|
|
118
|
+
stops: GradientStop[];
|
|
119
|
+
}
|
|
120
|
+
interface CanvasRadialGradient {
|
|
121
|
+
type: 'radial';
|
|
122
|
+
cx?: number;
|
|
123
|
+
cy?: number;
|
|
124
|
+
r?: number;
|
|
125
|
+
stops: GradientStop[];
|
|
126
|
+
}
|
|
127
|
+
type CanvasGradientDef = CanvasLinearGradient | CanvasRadialGradient;
|
|
128
|
+
interface SvgGradientStop {
|
|
129
|
+
offset: string;
|
|
130
|
+
stopColor: string;
|
|
131
|
+
stopOpacity?: number;
|
|
132
|
+
}
|
|
133
|
+
interface SvgGradientDef {
|
|
134
|
+
id: string;
|
|
135
|
+
type: 'linear' | 'radial';
|
|
136
|
+
attrs: Record<string, string | number>;
|
|
137
|
+
stops: SvgGradientStop[];
|
|
138
|
+
}
|
|
139
|
+
declare const CANVAS_GRADIENT_PRESETS: Record<string, CanvasLinearGradient>;
|
|
140
|
+
/** Whether an arbitrary value matches a CanvasGradientDef object. */
|
|
141
|
+
declare function isCanvasGradient(value: unknown): value is CanvasGradientDef;
|
|
142
|
+
/**
|
|
143
|
+
* Parses CSS linear-gradient or radial-gradient strings into CanvasGradientDef.
|
|
144
|
+
* Supports forms like:
|
|
145
|
+
* - linear-gradient(90deg, #ff0000 0%, #0000ff 100%)
|
|
146
|
+
* - linear-gradient(to right, red, blue)
|
|
147
|
+
* - radial-gradient(circle, red 0%, blue 100%)
|
|
148
|
+
*/
|
|
149
|
+
declare function parseCssGradient(css: string): CanvasGradientDef | null;
|
|
150
|
+
/**
|
|
151
|
+
* Resolves a paint value (which could be a CSS color, preset name, CSS gradient string, or CanvasGradientDef)
|
|
152
|
+
* into either a CanvasGradientDef (if gradient) or null (if solid color or invalid).
|
|
153
|
+
*/
|
|
154
|
+
declare function resolveCanvasGradient(value: unknown): CanvasGradientDef | null;
|
|
155
|
+
/**
|
|
156
|
+
* Converts degrees clockwise (CSS angle: 0 = top, 90 = right, 180 = bottom, 270 = left)
|
|
157
|
+
* to SVG linearGradient vector coordinates (x1, y1, x2, y2) normalized [0, 1].
|
|
158
|
+
*/
|
|
159
|
+
declare function angleToSvgLinearCoords(angleDeg?: number): {
|
|
160
|
+
x1: number;
|
|
161
|
+
y1: number;
|
|
162
|
+
x2: number;
|
|
163
|
+
y2: number;
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Converts a CanvasGradientDef to an SvgGradientDef descriptor ready for rendering `<linearGradient>` / `<radialGradient>`.
|
|
167
|
+
*/
|
|
168
|
+
declare function gradientToSvgDef(gradient: CanvasGradientDef, id: string): SvgGradientDef;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Shape geometry: shape descriptions (rect, rounded rect, ellipse, regular polygon, star and
|
|
172
|
+
* SVG path data) to polygons, SVG path parsing / flattening, and polygons back to path data.
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
/** Paint shared by every shape kind. Colours are any CSS colour, preset name, or gradient definition. */
|
|
176
|
+
interface ShapePaint {
|
|
177
|
+
fill?: string | CanvasGradientDef;
|
|
178
|
+
stroke?: string | CanvasGradientDef;
|
|
179
|
+
strokeWidth?: number;
|
|
180
|
+
opacity?: number;
|
|
181
|
+
strokeLinecap?: 'round' | 'butt' | 'square';
|
|
182
|
+
strokeLinejoin?: 'round' | 'bevel' | 'miter';
|
|
183
|
+
fillGradient?: CanvasGradientDef;
|
|
184
|
+
strokeGradient?: CanvasGradientDef;
|
|
185
|
+
}
|
|
186
|
+
interface ShapeBase extends ShapePaint {
|
|
187
|
+
/** Clockwise rotation in degrees (on a y-down canvas) about the centre of the node box. */
|
|
188
|
+
rotation?: number;
|
|
189
|
+
}
|
|
190
|
+
/** Rectangle filling the node box, optionally with rounded corners. */
|
|
191
|
+
interface RectShape extends ShapeBase {
|
|
192
|
+
kind: 'rect';
|
|
193
|
+
/** One radius, or `[topLeft, topRight, bottomRight, bottomLeft]`. Clamped to fit. */
|
|
194
|
+
cornerRadius?: number | readonly [number, number, number, number];
|
|
195
|
+
}
|
|
196
|
+
/** Ellipse (circle when the node is square) inscribed in the node box. */
|
|
197
|
+
interface EllipseShape extends ShapeBase {
|
|
198
|
+
kind: 'ellipse';
|
|
199
|
+
}
|
|
200
|
+
/** Regular polygon, stretched so its bounding box fills the node box. */
|
|
201
|
+
interface RegularPolygonShape extends ShapeBase {
|
|
202
|
+
kind: 'polygon';
|
|
203
|
+
/** Number of sides (at least 3, default 6). The first vertex points up. */
|
|
204
|
+
sides?: number;
|
|
205
|
+
}
|
|
206
|
+
/** Star, stretched so its bounding box fills the node box. */
|
|
207
|
+
interface StarShape extends ShapeBase {
|
|
208
|
+
kind: 'star';
|
|
209
|
+
/** Number of points (at least 3, default 5). The first point points up. */
|
|
210
|
+
points?: number;
|
|
211
|
+
/** Inner radius as a fraction of the outer radius (default 0.5). */
|
|
212
|
+
innerRadius?: number;
|
|
213
|
+
}
|
|
214
|
+
/** Arbitrary SVG path data. */
|
|
215
|
+
interface PathShape extends ShapeBase {
|
|
216
|
+
kind: 'path';
|
|
217
|
+
d: string;
|
|
218
|
+
/**
|
|
219
|
+
* `[minX, minY, width, height]` of the path's coordinate space, scaled to the node box.
|
|
220
|
+
* Without it the path coordinates are node-local pixels.
|
|
221
|
+
*/
|
|
222
|
+
viewBox?: readonly [number, number, number, number];
|
|
223
|
+
/** Fill rule (default `nonzero`, as in SVG). */
|
|
224
|
+
fillRule?: PolygonFillRule;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Shape description stored on a node as `data.shape`. Geometry is relative to the node
|
|
228
|
+
* box (`0,0` to `width,height`), so resizing the node resizes the shape.
|
|
229
|
+
*/
|
|
230
|
+
type ShapeDescription = RectShape | EllipseShape | RegularPolygonShape | StarShape | PathShape;
|
|
231
|
+
type ShapeKind = ShapeDescription['kind'];
|
|
232
|
+
declare const SHAPE_KINDS: readonly ShapeKind[];
|
|
233
|
+
/** Whether a value is a supported shape description. */
|
|
234
|
+
declare function isShapeDescription(value: unknown): value is ShapeDescription;
|
|
235
|
+
interface ShapeSize {
|
|
236
|
+
width: number;
|
|
237
|
+
height: number;
|
|
238
|
+
}
|
|
239
|
+
interface FlattenOptions {
|
|
240
|
+
/** Maximum distance between a curve and its polyline, in output units (default 0.25). */
|
|
241
|
+
tolerance?: number;
|
|
242
|
+
/** Upper bound on the segments one curve is split into (default 512). */
|
|
243
|
+
maxSegments?: number;
|
|
244
|
+
}
|
|
245
|
+
/** Normalised absolute path command produced by `parseSvgPath`. */
|
|
246
|
+
type PathCommand = {
|
|
247
|
+
type: 'M';
|
|
248
|
+
x: number;
|
|
249
|
+
y: number;
|
|
250
|
+
} | {
|
|
251
|
+
type: 'L';
|
|
252
|
+
x: number;
|
|
253
|
+
y: number;
|
|
254
|
+
} | {
|
|
255
|
+
type: 'C';
|
|
256
|
+
x1: number;
|
|
257
|
+
y1: number;
|
|
258
|
+
x2: number;
|
|
259
|
+
y2: number;
|
|
260
|
+
x: number;
|
|
261
|
+
y: number;
|
|
262
|
+
} | {
|
|
263
|
+
type: 'Q';
|
|
264
|
+
x1: number;
|
|
265
|
+
y1: number;
|
|
266
|
+
x: number;
|
|
267
|
+
y: number;
|
|
268
|
+
} | {
|
|
269
|
+
type: 'A';
|
|
270
|
+
rx: number;
|
|
271
|
+
ry: number;
|
|
272
|
+
rotation: number;
|
|
273
|
+
largeArc: boolean;
|
|
274
|
+
sweep: boolean;
|
|
275
|
+
x: number;
|
|
276
|
+
y: number;
|
|
277
|
+
} | {
|
|
278
|
+
type: 'Z';
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Parses SVG path data into absolute `M`, `L`, `C`, `Q`, `A` and `Z` commands. Relative
|
|
282
|
+
* commands, `H` / `V`, smooth curves (`S` / `T`) and implicit repeated commands are
|
|
283
|
+
* normalised. Parsing stops at the first malformed command (as browsers do).
|
|
284
|
+
*/
|
|
285
|
+
declare function parseSvgPath(d: string): PathCommand[];
|
|
286
|
+
/**
|
|
287
|
+
* Flattens SVG path data into closed rings (one per subpath; open subpaths are closed the
|
|
288
|
+
* way SVG fills them). Curves are approximated within `tolerance`.
|
|
289
|
+
*/
|
|
290
|
+
declare function flattenSvgPath(d: string | PathCommand[], options?: FlattenOptions): PolygonRing[];
|
|
291
|
+
/**
|
|
292
|
+
* SVG path data for a shape in node-local pixels (exact curves: arcs for rounded corners
|
|
293
|
+
* and ellipses), plus the fill rule and a `transform` for rotation / path view boxes.
|
|
294
|
+
* Use it to render a shape; use `shapeToPolygon` for geometry.
|
|
295
|
+
*/
|
|
296
|
+
declare function getShapeSvg(shape: ShapeDescription, size: ShapeSize): {
|
|
297
|
+
d: string;
|
|
298
|
+
transform?: string;
|
|
299
|
+
fillRule: PolygonFillRule;
|
|
300
|
+
};
|
|
301
|
+
/** Everything a renderer needs to draw a shape as one SVG `<path>`. */
|
|
302
|
+
interface ShapeRenderData {
|
|
303
|
+
/** SVG `viewBox` (`0 0 width height`): node-local pixels. */
|
|
304
|
+
viewBox: string;
|
|
305
|
+
width: number;
|
|
306
|
+
height: number;
|
|
307
|
+
d: string;
|
|
308
|
+
transform?: string;
|
|
309
|
+
fillRule: PolygonFillRule;
|
|
310
|
+
fill: string;
|
|
311
|
+
stroke: string;
|
|
312
|
+
strokeWidth: number;
|
|
313
|
+
opacity?: number;
|
|
314
|
+
strokeLinecap?: 'round' | 'butt' | 'square';
|
|
315
|
+
strokeLinejoin?: 'round' | 'bevel' | 'miter';
|
|
316
|
+
/** Gradient def for the fill, when fill uses a gradient. */
|
|
317
|
+
fillGradient?: SvgGradientDef;
|
|
318
|
+
/** Gradient def for the stroke, when stroke uses a gradient. */
|
|
319
|
+
strokeGradient?: SvgGradientDef;
|
|
320
|
+
}
|
|
321
|
+
interface GetShapeRenderDataOptions {
|
|
322
|
+
/** Unique ID suffix or base ID used when generating SVG gradient IDs (default: auto random). */
|
|
323
|
+
id?: string;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Resolves the SVG drawing of a shape for a node size. Without a size, a path's view box
|
|
327
|
+
* size (or 100x100) is used. Paint defaults: `fill: currentColor`, `stroke: none`,
|
|
328
|
+
* `strokeWidth: 1`. Supports solid colors, CSS gradients, presets, and CanvasGradientDef.
|
|
329
|
+
*/
|
|
330
|
+
declare function getShapeRenderData(shape: ShapeDescription, size?: Partial<ShapeSize>, options?: GetShapeRenderDataOptions): ShapeRenderData;
|
|
331
|
+
/**
|
|
332
|
+
* Converts a shape to rings in node-local pixels (rotation applied), flattening curves at
|
|
333
|
+
* `tolerance`. Returns the rings with the fill rule they must be read with.
|
|
334
|
+
*/
|
|
335
|
+
declare function shapeToPolygon(shape: ShapeDescription, size: ShapeSize, options?: FlattenOptions): {
|
|
336
|
+
rings: PolygonRing[];
|
|
337
|
+
fillRule: PolygonFillRule;
|
|
338
|
+
};
|
|
339
|
+
/** Formats a number with at most `precision` decimals, without trailing zeros. */
|
|
340
|
+
declare function formatNumber(value: number, precision?: number): string;
|
|
341
|
+
/**
|
|
342
|
+
* Simplifies a closed ring with Ramer–Douglas–Peucker at `tolerance`. Returns an empty
|
|
343
|
+
* ring when fewer than three points survive.
|
|
344
|
+
*/
|
|
345
|
+
declare function simplifyRing(ring: readonly Point[], tolerance: number): Point[];
|
|
346
|
+
interface PolygonToPathOptions {
|
|
347
|
+
/** Decimal places in the output (default 3). */
|
|
348
|
+
precision?: number;
|
|
349
|
+
/** Ramer–Douglas–Peucker tolerance; `0` (default) keeps every vertex. */
|
|
350
|
+
simplify?: number;
|
|
351
|
+
/** Added to every point before formatting (e.g. to make coordinates node-local). */
|
|
352
|
+
offset?: {
|
|
353
|
+
x: number;
|
|
354
|
+
y: number;
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* `'lines'` (default) writes every ring as `M`/`L`/`Z`. `'fit'` fits cubic Béziers to each
|
|
358
|
+
* ring (`fitPathData`), so flattened curves come back as smooth `C` curves while sharp
|
|
359
|
+
* corners (turning more than `cornerAngleDeg`) and straight edges stay as they are.
|
|
360
|
+
*/
|
|
361
|
+
curves?: 'lines' | 'fit';
|
|
362
|
+
/** Curve-fitting tolerance in path units for `curves: 'fit'` (default 0.25). */
|
|
363
|
+
curveTolerance?: number;
|
|
364
|
+
/** Corner threshold in degrees for `curves: 'fit'` (default 30). */
|
|
365
|
+
cornerAngleDeg?: number;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Converts a multi-polygon (or a list of rings) to SVG path data using `M`/`L`/`Z`. Holes
|
|
369
|
+
* keep their opposite orientation, so the path fills correctly with either fill rule.
|
|
370
|
+
*/
|
|
371
|
+
declare function polygonToSvgPath(input: MultiPolygon | PolygonRing[], options?: PolygonToPathOptions): string;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Bézier path model shared by the curve-preserving path operations: absolute `M` / `L` /
|
|
375
|
+
* `Q` / `C` / `Z` commands, conversion from SVG path data (arcs become cubics), segment
|
|
376
|
+
* math (evaluation, splitting, extrema, nearest point), exact areas, winding numbers and
|
|
377
|
+
* arc-length utilities.
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
/** One absolute path command. Every subpath starts with `M`; `Z` closes it. */
|
|
381
|
+
type BezierCommand = {
|
|
382
|
+
type: 'M';
|
|
383
|
+
x: number;
|
|
384
|
+
y: number;
|
|
385
|
+
} | {
|
|
386
|
+
type: 'L';
|
|
387
|
+
x: number;
|
|
388
|
+
y: number;
|
|
389
|
+
} | {
|
|
390
|
+
type: 'Q';
|
|
391
|
+
x1: number;
|
|
392
|
+
y1: number;
|
|
393
|
+
x: number;
|
|
394
|
+
y: number;
|
|
395
|
+
} | {
|
|
396
|
+
type: 'C';
|
|
397
|
+
x1: number;
|
|
398
|
+
y1: number;
|
|
399
|
+
x2: number;
|
|
400
|
+
y2: number;
|
|
401
|
+
x: number;
|
|
402
|
+
y: number;
|
|
403
|
+
} | {
|
|
404
|
+
type: 'Z';
|
|
405
|
+
};
|
|
406
|
+
/** A path as a list of absolute move / line / quadratic / cubic / close commands. */
|
|
407
|
+
type BezierPath = BezierCommand[];
|
|
408
|
+
/** Anything the path operations accept as a path: SVG path data, parsed commands or a `BezierPath`. */
|
|
409
|
+
type BezierPathInput = string | readonly BezierCommand[] | readonly PathCommand[];
|
|
410
|
+
/**
|
|
411
|
+
* One segment as its control points: 2 points for a line, 3 for a quadratic and 4 for a
|
|
412
|
+
* cubic Bézier.
|
|
413
|
+
*/
|
|
414
|
+
type BezierSegment = readonly Point[];
|
|
415
|
+
/** A subpath as a chain of segments (each starts where the previous one ends). */
|
|
416
|
+
interface BezierContour {
|
|
417
|
+
segments: BezierSegment[];
|
|
418
|
+
closed: boolean;
|
|
419
|
+
}
|
|
420
|
+
/** Point at parameter `t` (de Casteljau, works for any degree). */
|
|
421
|
+
declare function segmentPoint(seg: BezierSegment, t: number): Point;
|
|
422
|
+
/** First derivative at `t`. */
|
|
423
|
+
declare function segmentDerivative(seg: BezierSegment, t: number): Point;
|
|
424
|
+
/**
|
|
425
|
+
* Unit tangent at `t`. Where the derivative vanishes (a handle on its anchor) the direction
|
|
426
|
+
* a hair further along is used, and the chord as the last resort.
|
|
427
|
+
*/
|
|
428
|
+
declare function segmentTangent(seg: BezierSegment, t: number): Point;
|
|
429
|
+
/** Signed curvature at `t` (positive when turning counter-clockwise in a y-up frame). */
|
|
430
|
+
declare function segmentCurvature(seg: BezierSegment, t: number): number;
|
|
431
|
+
/** Splits a segment at `t` (de Casteljau). */
|
|
432
|
+
declare function splitSegment(seg: BezierSegment, t: number): [Point[], Point[]];
|
|
433
|
+
/** The part of a segment between parameters `t0` and `t1` (reversed when `t1 < t0`). */
|
|
434
|
+
declare function subSegment(seg: BezierSegment, t0: number, t1: number): Point[];
|
|
435
|
+
/** Parameters in (0, 1) where the segment's `axis` coordinate has a local extremum. */
|
|
436
|
+
declare function segmentExtrema(seg: BezierSegment, axis: 0 | 1): number[];
|
|
437
|
+
/** Exact axis-aligned bounds of a segment. */
|
|
438
|
+
declare function segmentBounds(seg: BezierSegment): {
|
|
439
|
+
minX: number;
|
|
440
|
+
minY: number;
|
|
441
|
+
maxX: number;
|
|
442
|
+
maxY: number;
|
|
443
|
+
};
|
|
444
|
+
/** Arc length of the segment between `t0` and `t1` (default the whole segment). */
|
|
445
|
+
declare function segmentLength(seg: BezierSegment, t0?: number, t1?: number, tolerance?: number): number;
|
|
446
|
+
/** Parameter at arc length `length` from the segment start (clamped to [0, 1]). */
|
|
447
|
+
declare function segmentParamAtLength(seg: BezierSegment, length: number, total?: number): number;
|
|
448
|
+
/**
|
|
449
|
+
* Nearest point of the segment to `p`: the parameter and the distance. Coarse sampling
|
|
450
|
+
* followed by Newton refinement around the best samples.
|
|
451
|
+
*/
|
|
452
|
+
declare function segmentNearest(seg: BezierSegment, p: Point): {
|
|
453
|
+
t: number;
|
|
454
|
+
distance: number;
|
|
455
|
+
};
|
|
456
|
+
/** Converts an SVG elliptical arc (SVG 1.1 F.6.5) to cubic segments, at most 90° each. */
|
|
457
|
+
declare function arcToCubics(from: Point, cmd: {
|
|
458
|
+
rx: number;
|
|
459
|
+
ry: number;
|
|
460
|
+
rotation: number;
|
|
461
|
+
largeArc: boolean;
|
|
462
|
+
sweep: boolean;
|
|
463
|
+
x: number;
|
|
464
|
+
y: number;
|
|
465
|
+
}): Point[][];
|
|
466
|
+
/**
|
|
467
|
+
* Converts SVG path data or parsed commands to a `BezierPath` (absolute `M` / `L` / `Q` /
|
|
468
|
+
* `C` / `Z`); elliptical arcs become cubic Béziers (at most 90° each).
|
|
469
|
+
*/
|
|
470
|
+
declare function toBezierPath(input: BezierPathInput): BezierPath;
|
|
471
|
+
/** Splits a path into contours of segments. Zero-length segments are dropped. */
|
|
472
|
+
declare function pathToContours(input: BezierPathInput): BezierContour[];
|
|
473
|
+
/** Builds a path from contours (a closed contour's closing line is left to `Z`). */
|
|
474
|
+
declare function contoursToPath(contours: readonly BezierContour[]): BezierPath;
|
|
475
|
+
interface BezierPathToSvgOptions {
|
|
476
|
+
/** Decimal places (default 3). */
|
|
477
|
+
precision?: number;
|
|
478
|
+
/** Added to every point before formatting. */
|
|
479
|
+
offset?: {
|
|
480
|
+
x: number;
|
|
481
|
+
y: number;
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/** Writes a path as compact SVG path data. */
|
|
485
|
+
declare function bezierPathToSvg(input: BezierPathInput, options?: BezierPathToSvgOptions): string;
|
|
486
|
+
/** 2D affine matrix `[a, b, c, d, e, f]` mapping `(x, y)` to `(a x + c y + e, b x + d y + f)`. */
|
|
487
|
+
type AffineMatrix = readonly [number, number, number, number, number, number];
|
|
488
|
+
/** Applies an affine transform to every point (exact for Bézier curves). */
|
|
489
|
+
declare function transformBezierPath(input: BezierPathInput, m: AffineMatrix): BezierPath;
|
|
490
|
+
/** Number of drawing segments (`L` / `Q` / `C`, plus the closing line of `Z` when it has length). */
|
|
491
|
+
declare function countPathSegments(input: BezierPathInput): {
|
|
492
|
+
lines: number;
|
|
493
|
+
quads: number;
|
|
494
|
+
cubics: number;
|
|
495
|
+
};
|
|
496
|
+
/** The contour's segments, with the closing line added when the contour is closed (or filled). */
|
|
497
|
+
declare function closeContour(contour: BezierContour, force?: boolean): BezierSegment[];
|
|
498
|
+
/** Exact bounds of a path, or `null` when it has no points. */
|
|
499
|
+
declare function bezierPathBounds(input: BezierPathInput): {
|
|
500
|
+
minX: number;
|
|
501
|
+
minY: number;
|
|
502
|
+
maxX: number;
|
|
503
|
+
maxY: number;
|
|
504
|
+
} | null;
|
|
505
|
+
/**
|
|
506
|
+
* Signed area of the path as drawn: every subpath closed, contributions summed (positive
|
|
507
|
+
* for counter-clockwise subpaths in a y-up frame). Exact for curves. Overlapping subpaths
|
|
508
|
+
* count with their multiplicity; use `bezierPathArea` for the filled area.
|
|
509
|
+
*/
|
|
510
|
+
declare function bezierPathSignedArea(input: BezierPathInput): number;
|
|
511
|
+
/** Signed area of a closed chain of segments. */
|
|
512
|
+
declare function contourSignedArea(segs: readonly BezierSegment[]): number;
|
|
513
|
+
/** Arc length of a path (closing lines of closed subpaths included). */
|
|
514
|
+
declare function bezierPathLength(input: BezierPathInput): number;
|
|
515
|
+
/** Point, unit tangent and angle (radians) at an arc length along the path. */
|
|
516
|
+
interface PathPointAtLength {
|
|
517
|
+
point: Point;
|
|
518
|
+
tangent: Point;
|
|
519
|
+
angle: number;
|
|
520
|
+
/** Subpath index. */
|
|
521
|
+
contour: number;
|
|
522
|
+
/** Segment index within the subpath (closing line last). */
|
|
523
|
+
segment: number;
|
|
524
|
+
/** Parameter within the segment. */
|
|
525
|
+
t: number;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The point at arc length `length` from the start of the path (clamped to the path). Arc
|
|
529
|
+
* lengths run through the subpaths in order, as `bezierPathLength` counts them.
|
|
530
|
+
*/
|
|
531
|
+
declare function bezierPathPointAtLength(input: BezierPathInput, length: number): PathPointAtLength | null;
|
|
532
|
+
/**
|
|
533
|
+
* Splits a segment chain by arc length: the part between `from` and `to` (measured from the
|
|
534
|
+
* chain's start) as new segments.
|
|
535
|
+
*/
|
|
536
|
+
declare function trimSegments(segs: readonly BezierSegment[], from: number, to: number, lengths?: number[]): Point[][];
|
|
537
|
+
/**
|
|
538
|
+
* Winding number of a set of closed segment chains around `point` (ray to +x, half-open
|
|
539
|
+
* crossing rule). Points exactly on the outline may report either side.
|
|
540
|
+
*/
|
|
541
|
+
declare function windingNumber(chains: readonly (readonly BezierSegment[])[], point: Point): number;
|
|
542
|
+
/** Whether `point` is inside the filled path (every subpath closed for filling). */
|
|
543
|
+
declare function pointInBezierPath(input: BezierPathInput, point: Point, fillRule?: PolygonFillRule): boolean;
|
|
544
|
+
/** Distance from `point` to the path outline (closing lines of closed subpaths included). */
|
|
545
|
+
declare function distanceToBezierPath(input: BezierPathInput | readonly BezierContour[], point: Point): number;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Curve-preserving boolean operations (union, intersection, difference, xor and divide) on
|
|
549
|
+
* Bézier paths.
|
|
550
|
+
*
|
|
551
|
+
* Algorithm (planar arrangement of curve pieces + winding classification):
|
|
552
|
+
* 1. Every segment of every operand (open subpaths closed for filling) is a *source*; it is
|
|
553
|
+
* cut into pieces monotone in x and y. Each piece remembers its source and its parameter
|
|
554
|
+
* range there, so results can be rebuilt from exact sub-curves of the inputs.
|
|
555
|
+
* 2. Pieces are intersected pairwise (pruned by a sweep on x): lines analytically, curves by
|
|
556
|
+
* recursive subdivision down to flat sub-curves, chord intersection and Newton polishing.
|
|
557
|
+
* Endpoints lying on another piece (T-junctions, tangent touches), collinear / coincident
|
|
558
|
+
* overlaps and clusters of hits along a tangency are resolved first. Points closer than
|
|
559
|
+
* `epsilon` share one vertex. Splitting repeats until no piece crosses another.
|
|
560
|
+
* 3. Coincident pieces (shared edges, identical curves) merge into one edge carrying every
|
|
561
|
+
* operand's signed multiplicity.
|
|
562
|
+
* 4. Each edge is classified by casting a horizontal or vertical ray from its midpoint (the
|
|
563
|
+
* pieces are monotone, so a ray crosses each at most once); the operands' fill rules turn
|
|
564
|
+
* the winding numbers on both sides into inside flags and the operation picks the edges
|
|
565
|
+
* with the result on exactly one side.
|
|
566
|
+
* 5. The kept edges are traced into rings, taking the sharpest left turn at each vertex
|
|
567
|
+
* (ties between tangent curves broken by a short chord along each curve), and consecutive
|
|
568
|
+
* pieces of one source are merged back into one exact sub-curve, so circles stay four
|
|
569
|
+
* cubics and rectangles four lines.
|
|
570
|
+
*
|
|
571
|
+
* When the curve engine fails (non-converging intersections on near-coincident curves,
|
|
572
|
+
* an inconsistent arrangement) the operation falls back to the polygon engine
|
|
573
|
+
* (`polygonBoolean`) on flattened input and reports it (`engine: 'polygon'`).
|
|
574
|
+
*
|
|
575
|
+
* Portions ported from vectorcraft-main/crates/pathops/src/boolean.rs (MIT OR Apache-2.0,
|
|
576
|
+
* © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.
|
|
577
|
+
*/
|
|
578
|
+
|
|
579
|
+
/** Boolean operations (same names as `polygonBoolean`). `xor` is Exclude. */
|
|
580
|
+
type PathBooleanOperation = PolygonBooleanOperation;
|
|
581
|
+
/** A path with the fill rule it is read with (default `nonzero`). */
|
|
582
|
+
interface PathOperand {
|
|
583
|
+
path: BezierPathInput;
|
|
584
|
+
fillRule?: PolygonFillRule;
|
|
585
|
+
}
|
|
586
|
+
type PathOperandInput = BezierPathInput | PathOperand;
|
|
587
|
+
interface PathOpsOptions {
|
|
588
|
+
/**
|
|
589
|
+
* Distance below which points are treated as equal. Defaults to `1e-9` times the
|
|
590
|
+
* largest coordinate magnitude (at least `1e-9`).
|
|
591
|
+
*/
|
|
592
|
+
epsilon?: number;
|
|
593
|
+
/** Fall back to the polygon engine when the curve engine fails (default `true`). */
|
|
594
|
+
fallback?: boolean;
|
|
595
|
+
/** Flattening tolerance used by the polygon fallback (default 0.05). */
|
|
596
|
+
fallbackTolerance?: number;
|
|
597
|
+
/** Called with the reason whenever the polygon fallback is used. */
|
|
598
|
+
onFallback?: (reason: string) => void;
|
|
599
|
+
/**
|
|
600
|
+
* Work budget of the curve–curve intersection search (flat sub-curve pairs per pair of
|
|
601
|
+
* pieces, default 20,000). Exceeding it (nearly coincident curves) falls back.
|
|
602
|
+
*/
|
|
603
|
+
subdivisionBudget?: number;
|
|
604
|
+
}
|
|
605
|
+
/** Which engine produced a result, and why the curve engine was not used. */
|
|
606
|
+
interface PathOpsReport {
|
|
607
|
+
engine: 'curve' | 'polygon';
|
|
608
|
+
fallbackReason?: string;
|
|
609
|
+
/** Set when the input cannot be processed at all (non-finite coordinates); the result is empty. */
|
|
610
|
+
error?: string;
|
|
611
|
+
}
|
|
612
|
+
interface PathBooleanResult extends PathOpsReport {
|
|
613
|
+
path: BezierPath;
|
|
614
|
+
}
|
|
615
|
+
/** One region of `dividePaths`, with the indices of the operands that cover it. */
|
|
616
|
+
interface PathPiece {
|
|
617
|
+
path: BezierPath;
|
|
618
|
+
operands: number[];
|
|
619
|
+
}
|
|
620
|
+
interface DividePathsResult extends PathOpsReport {
|
|
621
|
+
pieces: PathPiece[];
|
|
622
|
+
}
|
|
623
|
+
/** Raised by the curve engine when it cannot produce a reliable arrangement. */
|
|
624
|
+
declare class PathOpsError extends Error {
|
|
625
|
+
constructor(message: string);
|
|
626
|
+
}
|
|
627
|
+
/** A crossing or touch of two segments: the parameter on each and the point. */
|
|
628
|
+
interface SegmentIntersection {
|
|
629
|
+
ta: number;
|
|
630
|
+
tb: number;
|
|
631
|
+
point: Point;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Every point where two segments (lines, quadratics or cubics, as control points) cross or
|
|
635
|
+
* touch, by the same subdivision search the boolean operations use: flat sub-curves, chord
|
|
636
|
+
* intersection, Newton polishing. Hits closer than `epsilon` (default: 1e-9 of the segments'
|
|
637
|
+
* size) are one. Coincident stretches report the hits the search finds along them.
|
|
638
|
+
*/
|
|
639
|
+
declare function segmentIntersections(a: BezierSegment, b: BezierSegment, options?: {
|
|
640
|
+
epsilon?: number;
|
|
641
|
+
budget?: number;
|
|
642
|
+
}): SegmentIntersection[];
|
|
643
|
+
/**
|
|
644
|
+
* Applies a boolean operation to any number of path operands, keeping curves:
|
|
645
|
+
* - `union`: covered by any operand.
|
|
646
|
+
* - `intersection`: covered by every operand.
|
|
647
|
+
* - `difference`: the first operand minus all others.
|
|
648
|
+
* - `xor`: covered by an odd number of operands (Exclude).
|
|
649
|
+
*
|
|
650
|
+
* Open subpaths are closed for filling. Non-finite input gives an empty path (`error`); with
|
|
651
|
+
* `fallback: false` a failure of the curve engine throws a `PathOpsError`. The result is a set of simple closed subpaths that
|
|
652
|
+
* never cross (they may touch at points): outer subpaths run counter-clockwise in a y-up
|
|
653
|
+
* frame (clockwise on screen), holes the other way, so it fills the same with `nonzero`
|
|
654
|
+
* and `evenodd`. Segments of the inputs are kept as exact sub-curves.
|
|
655
|
+
*/
|
|
656
|
+
declare function pathBoolean(operation: PathBooleanOperation, operands: PathOperandInput[], options?: PathOpsOptions): PathBooleanResult;
|
|
657
|
+
declare const unitePaths: (a: PathOperandInput, b: PathOperandInput, options?: PathOpsOptions) => BezierPath;
|
|
658
|
+
declare const intersectPaths: (a: PathOperandInput, b: PathOperandInput, options?: PathOpsOptions) => BezierPath;
|
|
659
|
+
declare const subtractPaths: (a: PathOperandInput, b: PathOperandInput, options?: PathOpsOptions) => BezierPath;
|
|
660
|
+
declare const excludePaths: (a: PathOperandInput, b: PathOperandInput, options?: PathOpsOptions) => BezierPath;
|
|
661
|
+
/**
|
|
662
|
+
* Splits the arrangement of the operands into its regions (Divide): every connected area
|
|
663
|
+
* covered by the same set of operands becomes one piece (an outer subpath with its holes).
|
|
664
|
+
* Pieces are ordered by operand set, then by area (largest first).
|
|
665
|
+
*/
|
|
666
|
+
declare function dividePaths(operands: PathOperandInput[], options?: PathOpsOptions): DividePathsResult;
|
|
667
|
+
/**
|
|
668
|
+
* Resolves self-intersections and overlaps of one filled path into simple, consistently
|
|
669
|
+
* oriented subpaths (outer subpaths and holes) under its fill rule.
|
|
670
|
+
*/
|
|
671
|
+
declare function normalizeBezierPath(path: BezierPathInput, fillRule?: PolygonFillRule, options?: PathOpsOptions): BezierPath;
|
|
672
|
+
/** Filled area of a path under its fill rule (overlaps counted once; exact for curves). */
|
|
673
|
+
declare function bezierPathArea(path: BezierPathInput, fillRule?: PolygonFillRule, options?: PathOpsOptions): number;
|
|
674
|
+
/** One edge of `planarSplit`: a piece of an input segment between two vertices. */
|
|
675
|
+
interface PlanarSplitEdge {
|
|
676
|
+
/** Vertex indices at the start and end of `pts`. */
|
|
677
|
+
a: number;
|
|
678
|
+
b: number;
|
|
679
|
+
/** Control points from vertex `a` to vertex `b`. */
|
|
680
|
+
pts: Point[];
|
|
681
|
+
/** Inputs whose chains run along this edge, ascending (several where outlines coincide). */
|
|
682
|
+
operands: number[];
|
|
683
|
+
/** The source segment it is a piece of (index into `sources`) and its parameters there at `a` and `b`. */
|
|
684
|
+
src: number;
|
|
685
|
+
sa: number;
|
|
686
|
+
sb: number;
|
|
687
|
+
}
|
|
688
|
+
interface PlanarSplitResult {
|
|
689
|
+
vertices: Point[];
|
|
690
|
+
edges: PlanarSplitEdge[];
|
|
691
|
+
/** The input segments, as `src` refers to them. */
|
|
692
|
+
sources: BezierSegment[];
|
|
693
|
+
eps: number;
|
|
694
|
+
scale: number;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* The chains of every input (open or closed, as given: nothing is closed for filling) split
|
|
698
|
+
* wherever they cross or touch each other or themselves, with the same intersection search as
|
|
699
|
+
* the boolean operations. Coincident pieces become one edge that lists every input along it.
|
|
700
|
+
* Throws a `PathOpsError` when the search does not settle or the input is not finite.
|
|
701
|
+
*/
|
|
702
|
+
declare function planarSplit(inputs: readonly (readonly (readonly BezierSegment[])[])[], options?: PathOpsOptions): PlanarSplitResult;
|
|
703
|
+
/** Result of `pathBooleanSelect`: per selector, its connected parts (each an outer subpath with its holes). */
|
|
704
|
+
interface PathSelectResult extends PathOpsReport {
|
|
705
|
+
parts: BezierPath[][];
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Several boolean selections over one arrangement of the operands (the Pathfinder panel's
|
|
709
|
+
* Trim, Merge, Crop and Minus Back are each a selection per output). A selector gets the
|
|
710
|
+
* ascending indices of the operands covering an area and keeps the area when it returns true.
|
|
711
|
+
* Each selection comes back as its connected parts, largest first, built as `pathBoolean`
|
|
712
|
+
* builds its result; areas that only touch at an edge are one part. Falls back to the
|
|
713
|
+
* polygon engine like `pathBoolean`.
|
|
714
|
+
*/
|
|
715
|
+
declare function pathBooleanSelect(operands: PathOperandInput[], selectors: readonly ((covering: readonly number[]) => boolean)[], options?: PathOpsOptions): PathSelectResult;
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Offset Path and Outline Stroke on Bézier paths.
|
|
719
|
+
*
|
|
720
|
+
* The stroker offsets every segment by ± half the width (lines exactly, curves by cubic
|
|
721
|
+
* approximations refined until they stay within `tolerance` of the true offset), joins the
|
|
722
|
+
* pieces (miter with a miter limit, round or bevel on the outer side of a turn; through the
|
|
723
|
+
* vertex on the inner side) and adds caps (butt, round, square) to open subpaths. Dashes
|
|
724
|
+
* follow arc length, restart on every subpath and join across the start of a closed
|
|
725
|
+
* subpath. The raw outline overlaps itself, so it is filled with the non-zero rule and
|
|
726
|
+
* normalised with the curve boolean engine.
|
|
727
|
+
*
|
|
728
|
+
* When the stroke is wider than a curve's radius of curvature the inner offset turns inside
|
|
729
|
+
* out and its loops cancel winding, leaving holes that are really covered. Every face of
|
|
730
|
+
* the true result is bounded by the arrangement, so a face is spurious exactly when a point
|
|
731
|
+
* deep inside it is closer to the path than the offset distance (holes when growing,
|
|
732
|
+
* islands when insetting); such faces are dropped.
|
|
733
|
+
*
|
|
734
|
+
* Portions ported from vectorcraft-main/crates/pathops/src/offset.rs,
|
|
735
|
+
* vectorcraft-main/crates/pathops/src/boolean.rs (`try_merge`) and
|
|
736
|
+
* vectorcraft-main/crates/effects/src/stroke/dash.rs (MIT OR Apache-2.0, © ArtCraft Team
|
|
737
|
+
* and contributors). See THIRD_PARTY_NOTICES.md.
|
|
738
|
+
*/
|
|
739
|
+
|
|
740
|
+
/** Line join (as SVG `stroke-linejoin`). */
|
|
741
|
+
type StrokeJoin = 'miter' | 'round' | 'bevel';
|
|
742
|
+
/** Line cap (as SVG `stroke-linecap`). */
|
|
743
|
+
type StrokeCap = 'butt' | 'round' | 'square';
|
|
744
|
+
interface OutlineStrokeOptions extends PathOpsOptions {
|
|
745
|
+
/** Stroke width. Non-positive or non-finite widths give an empty result. */
|
|
746
|
+
width: number;
|
|
747
|
+
/** Default `miter` (as SVG). */
|
|
748
|
+
join?: StrokeJoin;
|
|
749
|
+
/** Default `butt` (as SVG). */
|
|
750
|
+
cap?: StrokeCap;
|
|
751
|
+
/** Miter length / stroke width above which a miter join is bevelled (default 4, as SVG). */
|
|
752
|
+
miterLimit?: number;
|
|
753
|
+
/** Dash pattern (as SVG `stroke-dasharray`); odd patterns repeat twice. */
|
|
754
|
+
dash?: readonly number[];
|
|
755
|
+
/** Distance into the dash pattern at the start of each subpath (as `stroke-dashoffset`). */
|
|
756
|
+
dashOffset?: number;
|
|
757
|
+
/** Maximum distance between the approximated and the true offset curve (default 0.01). */
|
|
758
|
+
tolerance?: number;
|
|
759
|
+
}
|
|
760
|
+
interface OffsetPathOptions extends PathOpsOptions {
|
|
761
|
+
/** Corner style of the grown / shrunk outline (default `miter`). */
|
|
762
|
+
join?: StrokeJoin;
|
|
763
|
+
/** Miter limit (default 4). */
|
|
764
|
+
miterLimit?: number;
|
|
765
|
+
/** Fill rule of the input (default `nonzero`). */
|
|
766
|
+
fillRule?: PolygonFillRule;
|
|
767
|
+
/** Maximum distance between the approximated and the true offset curve (default 0.01). */
|
|
768
|
+
tolerance?: number;
|
|
769
|
+
}
|
|
770
|
+
interface DashedPath {
|
|
771
|
+
/** The dashes, as open subpaths. */
|
|
772
|
+
contours: BezierContour[];
|
|
773
|
+
/** Zero-length dashes: where they sit and the path direction there. */
|
|
774
|
+
dots: {
|
|
775
|
+
point: Point;
|
|
776
|
+
tangent: Point;
|
|
777
|
+
}[];
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Applies a dash pattern (SVG `stroke-dasharray` / `stroke-dashoffset` semantics) to the
|
|
781
|
+
* subpaths. Returns `null` when the pattern is invalid (negative or non-finite entries),
|
|
782
|
+
* has no positive entry, or would make more than 200,000 dashes: the line is solid.
|
|
783
|
+
*/
|
|
784
|
+
declare function dashContours(contours: readonly BezierContour[], pattern: readonly number[], offset?: number): DashedPath | null;
|
|
785
|
+
/**
|
|
786
|
+
* The raw (overlapping) outline of a stroke as closed subpaths to fill with `nonzero`.
|
|
787
|
+
* `outlineStroke` normalises it; this is useful for previews.
|
|
788
|
+
*/
|
|
789
|
+
declare function strokeOutlineRings(input: BezierPathInput, options: OutlineStrokeOptions): BezierPath;
|
|
790
|
+
/**
|
|
791
|
+
* Outline Stroke: the filled area painted by stroking `path`, as one clean path (overlaps
|
|
792
|
+
* removed, consistently oriented). Joins, caps, miter limit and dashes follow SVG.
|
|
793
|
+
*/
|
|
794
|
+
declare function outlineStroke(input: BezierPathInput, options: OutlineStrokeOptions): BezierPath;
|
|
795
|
+
/**
|
|
796
|
+
* Offset Path: grows (`distance > 0`) or shrinks (`distance < 0`) the filled area by
|
|
797
|
+
* `distance`. Closed subpaths are a filled region (read with `fillRule`); open subpaths are
|
|
798
|
+
* outlined with a stroke of width `2·|distance|` and butt caps (when growing). Loops of the
|
|
799
|
+
* offset curves are removed.
|
|
800
|
+
*/
|
|
801
|
+
declare function offsetPath(input: BezierPathInput, distance: number, options?: OffsetPathOptions): BezierPath;
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Planar maps: the faces and edges a set of paths divides the plane into (Shape Builder, fill
|
|
805
|
+
* regions, Pathfinder Outline).
|
|
806
|
+
*
|
|
807
|
+
* Unlike `dividePaths`, which splits the *filled areas* of closed shapes, every path here is an
|
|
808
|
+
* edge, open ones included: three crossing lines enclose a triangle, a line across a rectangle
|
|
809
|
+
* splits it in two, and an area enclosed by paths is a face whether or not anything fills it.
|
|
810
|
+
*
|
|
811
|
+
* 1. `planarSplit` (the boolean engine's intersection search) cuts every input segment wherever
|
|
812
|
+
* it meets another one; coincident pieces become one edge.
|
|
813
|
+
* 2. Each edge gives two half-edges. Around every vertex the half-edges leaving it are sorted by
|
|
814
|
+
* the direction in which they cross a small circle around it (half the shortest edge there),
|
|
815
|
+
* which orders tangent curves correctly.
|
|
816
|
+
* 3. A face walk takes, at each vertex, the half-edge just clockwise of the way back — the
|
|
817
|
+
* sharpest left turn. Bounded faces come out counter-clockwise (positive area in a y-up
|
|
818
|
+
* frame), the outer boundary of every connected piece clockwise; spurs (loose line ends) are
|
|
819
|
+
* dropped. Each outer boundary becomes a hole of the smallest face of another piece around
|
|
820
|
+
* it, so nesting is decided face by face, never by an even-odd rule over the whole network.
|
|
821
|
+
* 4. Faces learn which inputs fill them from a point well inside each face.
|
|
822
|
+
*
|
|
823
|
+
* Adjacency is kept in per-vertex lists and half-edge slots (no scans over all edges per step).
|
|
824
|
+
*
|
|
825
|
+
* Ported from vectorcraft-main/crates/pathops/src/planar.rs (`map`, `Map::merge`, `build`,
|
|
826
|
+
* `interior_point`, `without_spurs`, `leaving_angle`), figcraft-main/crates/doc/src/vector_faces.rs
|
|
827
|
+
* (`faces`: half-edge walk with an angle-sorted next edge, the winding deciding inside or
|
|
828
|
+
* outside) and cadcraft-main/crates/geom/src/region.rs (`enclosing_loop`, `boundary_at_gap`:
|
|
829
|
+
* a ray from the point, the sharpest left turn, islands as holes, gaps between open ends
|
|
830
|
+
* closed). MIT OR Apache-2.0, © ArtCraft Team and contributors. See THIRD_PARTY_NOTICES.md.
|
|
831
|
+
*/
|
|
832
|
+
|
|
833
|
+
/** One input of a planar map. */
|
|
834
|
+
interface PlanarInput {
|
|
835
|
+
/** Its subpaths: closed ones bound faces and the area the input fills, open ones are edges only. */
|
|
836
|
+
path: BezierPathInput;
|
|
837
|
+
/** How its closed subpaths fill (default `nonzero`). */
|
|
838
|
+
fillRule?: PolygonFillRule;
|
|
839
|
+
/** Whether its closed subpaths count as filling the faces inside them (default true). */
|
|
840
|
+
fills?: boolean;
|
|
841
|
+
}
|
|
842
|
+
interface PlanarMapOptions extends PathOpsOptions {
|
|
843
|
+
/**
|
|
844
|
+
* Close gaps up to this distance between loose ends (vertices with one edge): each one joins
|
|
845
|
+
* the nearest other loose end within reach by a straight edge (default 0, off).
|
|
846
|
+
*/
|
|
847
|
+
gap?: number;
|
|
848
|
+
/** Most input segments mapped (default 4096); a bigger input gives no map. */
|
|
849
|
+
maxSegments?: number;
|
|
850
|
+
}
|
|
851
|
+
/** A bounded face: an outer outline with the holes other pieces of the map make in it. */
|
|
852
|
+
interface PlanarFace {
|
|
853
|
+
/** Outer subpath (counter-clockwise in a y-up frame) followed by its holes (clockwise). */
|
|
854
|
+
path: BezierPath;
|
|
855
|
+
/** Area (holes taken out). */
|
|
856
|
+
area: number;
|
|
857
|
+
bounds: {
|
|
858
|
+
minX: number;
|
|
859
|
+
minY: number;
|
|
860
|
+
maxX: number;
|
|
861
|
+
maxY: number;
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
/** A run of edges between junctions (vertices where paths meet or a path ends). */
|
|
865
|
+
interface PlanarChain {
|
|
866
|
+
segments: BezierSegment[];
|
|
867
|
+
/** A loop with no junction on it. */
|
|
868
|
+
closed: boolean;
|
|
869
|
+
/** The input it lies on: the front-most (highest index) where several coincide. */
|
|
870
|
+
source: number;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* A point strictly inside a filled path (nonzero), well away from its edges where it can be:
|
|
874
|
+
* the middle of the widest span of a few horizontal lines through it. Null for an empty path.
|
|
875
|
+
* Ported from vectorcraft's `interior_point` (cadcraft's `interior_point` for polygons).
|
|
876
|
+
*/
|
|
877
|
+
declare function interiorPoint(path: BezierPathInput | readonly (readonly BezierSegment[])[]): Point | null;
|
|
878
|
+
/**
|
|
879
|
+
* The planar map of a set of paths. Build it with `PlanarMap.build`; faces, chains and fills
|
|
880
|
+
* are computed on first use.
|
|
881
|
+
*/
|
|
882
|
+
declare class PlanarMap {
|
|
883
|
+
readonly vertices: Point[];
|
|
884
|
+
private readonly edges;
|
|
885
|
+
private readonly sources;
|
|
886
|
+
readonly eps: number;
|
|
887
|
+
readonly inputCount: number;
|
|
888
|
+
private readonly around;
|
|
889
|
+
private readonly slot;
|
|
890
|
+
private readonly component;
|
|
891
|
+
private cyclesCache;
|
|
892
|
+
private sourcesCache;
|
|
893
|
+
private readonly fills;
|
|
894
|
+
private constructor();
|
|
895
|
+
/**
|
|
896
|
+
* Maps `inputs` (their subpaths as given: open ones stay open). Null when they cannot be mapped
|
|
897
|
+
* (non-finite points, more than `maxSegments` segments or an intersection search that does
|
|
898
|
+
* not settle) or have no edges.
|
|
899
|
+
*/
|
|
900
|
+
static build(inputs: readonly PlanarInput[], options?: PlanarMapOptions): PlanarMap | null;
|
|
901
|
+
private origin;
|
|
902
|
+
private halfPts;
|
|
903
|
+
/** The half-edge after `h` on the face to its left: the sharpest left turn at its end. */
|
|
904
|
+
private next;
|
|
905
|
+
/** Direction (angle) in which half-edge `h`, leaving `at`, first gets `r` away from it. */
|
|
906
|
+
private leavingAngle;
|
|
907
|
+
/** Segments of a run of half-edges (a loop unless `closed` is false), pieces of one input segment joined again. */
|
|
908
|
+
private cycleSegments;
|
|
909
|
+
private cycles;
|
|
910
|
+
private faceChains;
|
|
911
|
+
/** The bounded faces of the map. */
|
|
912
|
+
get faces(): PlanarFace[];
|
|
913
|
+
/** The inputs whose fill covers face `i`, ascending (empty for an area only outlines enclose). */
|
|
914
|
+
faceSources(i: number): number[];
|
|
915
|
+
/** A point well inside face `i`. */
|
|
916
|
+
faceInterior(i: number): Point | null;
|
|
917
|
+
/** The face under `p` (the smallest one around it), or -1. */
|
|
918
|
+
faceAt(p: Point): number;
|
|
919
|
+
/**
|
|
920
|
+
* The union of faces, traced along the map's own edges: those between two merged faces drop
|
|
921
|
+
* out exactly, so no seam or sliver is left where a boolean union of their outlines could
|
|
922
|
+
* round shared corners apart. Outer subpaths come out counter-clockwise (y-up), holes clockwise.
|
|
923
|
+
*/
|
|
924
|
+
merge(faceIndices: readonly number[]): BezierPath;
|
|
925
|
+
/**
|
|
926
|
+
* The edges joined into runs between junctions: vertices where other than two edges meet, or
|
|
927
|
+
* where the two edges lie on different inputs. Each run is keyed by its input.
|
|
928
|
+
*/
|
|
929
|
+
chains(): PlanarChain[];
|
|
930
|
+
/**
|
|
931
|
+
* The region enclosing `p`, traced as cadcraft does it: a ray from `p` towards +x meets the
|
|
932
|
+
* edges in order; for each, the half-edge with `p` on its left is followed by always taking the
|
|
933
|
+
* sharpest left turn, until a loop that runs counter-clockwise round `p` comes back. The pieces
|
|
934
|
+
* of the map nested inside that loop (islands) become its holes. Null when nothing encloses `p`.
|
|
935
|
+
*/
|
|
936
|
+
regionAt(p: Point): BezierPath | null;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* The Pathfinder panel's dividing operations over a stack of shapes (index 0 at the back):
|
|
941
|
+
* Divide, Trim, Merge, Crop, Outline and Minus Back. (Unite, Minus Front, Intersect and Exclude
|
|
942
|
+
* are `pathBoolean`.) Every result carries the index of the shape whose paint it keeps.
|
|
943
|
+
*
|
|
944
|
+
* The filled areas come from one arrangement of all shapes (`dividePaths`, `pathBooleanSelect`):
|
|
945
|
+
* each area knows the shapes covering it, so an operation is a choice of areas per result.
|
|
946
|
+
* Outline and Divide with cutting lines use the planar map (`PlanarMap`).
|
|
947
|
+
*
|
|
948
|
+
* Ported from vectorcraft-main/crates/pathops/src/pathfinder.rs (`pathfinder`, `PathfinderOp`,
|
|
949
|
+
* `outline_edges`), MIT OR Apache-2.0, © ArtCraft Team and contributors. See
|
|
950
|
+
* THIRD_PARTY_NOTICES.md.
|
|
951
|
+
*/
|
|
952
|
+
|
|
953
|
+
type PathfinderOperation = 'divide' | 'trim' | 'merge' | 'crop' | 'outline' | 'minusBack';
|
|
954
|
+
declare const PATHFINDER_OPERATIONS: readonly PathfinderOperation[];
|
|
955
|
+
/** A shape of the stack. */
|
|
956
|
+
interface PathfinderShape {
|
|
957
|
+
/** Its outline; open subpaths are closed for filling (unless `line`). */
|
|
958
|
+
path: BezierPathInput;
|
|
959
|
+
fillRule?: PolygonFillRule;
|
|
960
|
+
/** Identifies its paint: Merge joins touching areas whose shapes share a key (default: the index). */
|
|
961
|
+
key?: string | number;
|
|
962
|
+
/**
|
|
963
|
+
* An unfilled open path: a cutting edge for Divide, splitting the faces it crosses without
|
|
964
|
+
* becoming one (ignored by the other operations).
|
|
965
|
+
*/
|
|
966
|
+
line?: boolean;
|
|
967
|
+
}
|
|
968
|
+
/** One result: a filled area (or an open stroke for Outline). */
|
|
969
|
+
interface PathfinderPiece {
|
|
970
|
+
path: BezierPath;
|
|
971
|
+
/** Index of the shape whose paint it keeps. */
|
|
972
|
+
source: number;
|
|
973
|
+
/** Outline edges are open paths (a loop with no junction on it stays closed). */
|
|
974
|
+
open?: boolean;
|
|
975
|
+
}
|
|
976
|
+
interface PathfinderResult extends PathOpsReport {
|
|
977
|
+
pieces: PathfinderPiece[];
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Runs a Pathfinder operation:
|
|
981
|
+
* - `divide`: every face of the arrangement becomes its own piece, painted like the top-most
|
|
982
|
+
* shape covering it. `line` shapes cut the faces they cross.
|
|
983
|
+
* - `trim`: hidden parts are removed; one piece per shape that stays visible, nothing merged.
|
|
984
|
+
* - `merge`: like Trim, then touching or overlapping visible parts of shapes with the same key
|
|
985
|
+
* become one piece (one per connected area).
|
|
986
|
+
* - `crop`: the top shape crops the others; it is removed, the visible parts of the shapes below
|
|
987
|
+
* inside it stay.
|
|
988
|
+
* - `outline`: every outline is cut where it meets another; the pieces are open paths painted
|
|
989
|
+
* like the shape they bound (the top-most where outlines coincide).
|
|
990
|
+
* - `minusBack`: the top shape minus everything behind it.
|
|
991
|
+
*/
|
|
992
|
+
declare function pathfinder(op: PathfinderOperation, shapes: readonly PathfinderShape[], options?: PathOpsOptions): PathfinderResult;
|
|
993
|
+
|
|
994
|
+
export { type PolygonInput as $, type AffineMatrix as A, type BezierPathInput as B, type CanvasGradientDef as C, type DashedPath as D, type EllipseShape as E, type FlattenOptions as F, type GetShapeRenderDataOptions as G, type PathOpsOptions as H, type PathOpsReport as I, type PathPiece as J, type PathPointAtLength as K, type PathSelectResult as L, type MultiPolygon as M, type PathShape as N, type OffsetPathOptions as O, PlanarMap as P, type PathfinderPiece as Q, type PathfinderResult as R, type SvgGradientDef as S, type PlanarChain as T, type PlanarFace as U, type PlanarInput as V, type PlanarMapOptions as W, type PlanarSplitEdge as X, type PlanarSplitResult as Y, type PolygonBooleanOperation as Z, type PolygonBooleanOptions as _, type BezierPath as a, segmentExtrema as a$, type PolygonOperand as a0, type PolygonPiece as a1, type PolygonToPathOptions as a2, type PolygonWithHoles as a3, type RectShape as a4, type RegularPolygonShape as a5, SHAPE_KINDS as a6, type SegmentIntersection as a7, type ShapeKind as a8, type ShapePaint as a9, intersectPaths as aA, intersectPolygons as aB, isCanvasGradient as aC, isShapeDescription as aD, multiPolygonArea as aE, multiPolygonBounds as aF, normalizeBezierPath as aG, offsetPath as aH, outlineStroke as aI, parseCssGradient as aJ, parseSvgPath as aK, pathBoolean as aL, pathBooleanSelect as aM, pathToContours as aN, pathfinder as aO, planarSplit as aP, pointInBezierPath as aQ, pointInMultiPolygon as aR, pointInRings as aS, polygonArea as aT, polygonBoolean as aU, polygonToSvgPath as aV, resolveCanvasGradient as aW, ringSignedArea as aX, segmentBounds as aY, segmentCurvature as aZ, segmentDerivative as a_, type ShapeRenderData as aa, type StarShape as ab, type SvgGradientStop as ac, angleToSvgLinearCoords as ad, arcToCubics as ae, bezierPathArea as af, bezierPathBounds as ag, bezierPathLength as ah, bezierPathPointAtLength as ai, bezierPathSignedArea as aj, bezierPathToSvg as ak, closeContour as al, contourSignedArea as am, contoursToPath as an, countPathSegments as ao, dashContours as ap, distanceToBezierPath as aq, dividePaths as ar, dividePolygons as as, excludePaths as at, flattenSvgPath as au, formatNumber as av, getShapeRenderData as aw, getShapeSvg as ax, gradientToSvgDef as ay, interiorPoint as az, type StrokeJoin as b, segmentIntersections as b0, segmentLength as b1, segmentNearest as b2, segmentParamAtLength as b3, segmentPoint as b4, segmentTangent as b5, shapeToPolygon as b6, simplifyRing as b7, splitSegment as b8, strokeOutlineRings as b9, subSegment as ba, subtractPaths as bb, subtractPolygons as bc, toBezierPath as bd, transformBezierPath as be, trimSegments as bf, unionPolygons as bg, unitePaths as bh, windingNumber as bi, xorPolygons as bj, type StrokeCap as c, type BezierSegment as d, type PathfinderOperation as e, type PathfinderShape as f, type ShapeDescription as g, type PolygonFillRule as h, type PolygonRing as i, type ShapeSize as j, type BezierCommand as k, type BezierContour as l, type BezierPathToSvgOptions as m, CANVAS_GRADIENT_PRESETS as n, type CanvasLinearGradient as o, type CanvasRadialGradient as p, type DividePathsResult as q, type GradientStop as r, type OutlineStrokeOptions as s, PATHFINDER_OPERATIONS as t, type PathBooleanOperation as u, type PathBooleanResult as v, type PathCommand as w, type PathOperand as x, type PathOperandInput as y, PathOpsError as z };
|