interface DrawPointOptions { radius?: number; } declare function drawPoint(ctx: CanvasRenderingContext2D, x: number, y: number, options?: DrawPointOptions): void; /** * @link https://developer.mozilla.org/docs/Web/SVG/Attribute/d */ type Path2DCommand = { type: 'm' | 'M'; x: number; y: number; } | { type: 'h' | 'H'; x: number; } | { type: 'v' | 'V'; y: number; } | { type: 'l' | 'L'; x: number; y: number; } | { type: 'c' | 'C'; x1: number; y1: number; x2: number; y2: number; x: number; y: number; } | { type: 's' | 'S'; x2: number; y2: number; x: number; y: number; } | { type: 'q' | 'Q'; x1: number; y1: number; x: number; y: number; } | { type: 't' | 'T'; x: number; y: number; } | { type: 'a' | 'A'; rx: number; ry: number; angle: number; largeArcFlag: number; sweepFlag: number; x: number; y: number; } | { type: 'z' | 'Z'; }; /** * SVG path data * * @link https://developer.mozilla.org/docs/Web/SVG/Attribute/d */ type Path2DData = string; type FillRule = 'nonzero' | 'evenodd'; type StrokeLinecap = 'butt' | 'round' | 'square'; type StrokeLinejoin = 'arcs' | 'bevel' | 'miter' | 'miter-clip' | 'round'; interface Path2DDrawStyle { fill: string | any; stroke: string | any; shadowColor: string; shadowOffsetX: number; shadowOffsetY: number; shadowBlur: number; } interface Path2DStyle extends Path2DDrawStyle { [key: string]: any; fillOpacity: number; fillRule: FillRule; opacity: number; strokeOpacity: number; strokeWidth: number; strokeLinecap: StrokeLinecap; strokeLinejoin: StrokeLinejoin; strokeMiterlimit: number; strokeDasharray: number[]; strokeDashoffset: number; visibility: string; } declare function setCanvasContext(ctx: CanvasRenderingContext2D, style: Partial): void; interface Vector2Like { x: number; y: number; } declare class Vector2 implements Vector2Like { protected _x: number; protected _y: number; protected _onUpdate?: ((vec: Vector2) => void) | undefined; static get MAX(): Vector2; static get MIN(): Vector2; static lerp(a: Vector2Like, b: Vector2Like, t: number): Vector2; get width(): number; set width(val: number); get height(): number; set height(val: number); get left(): number; set left(val: number); get top(): number; set top(val: number); get x(): number; set x(value: number); get y(): number; set y(value: number); constructor(_x?: number, _y?: number, _onUpdate?: ((vec: Vector2) => void) | undefined); set(x?: number, y?: number): this; add(p: Vector2Like): this; sub(p: Vector2Like): this; subVectors(a: Vector2Like, b: Vector2Like): this; multiply(x?: number, y?: number): this; divide(x?: number, y?: number): this; cross(p: Vector2Like): number; dot(p: Vector2Like): number; rotate(rad: number, origin?: Vector2Like): this; getLength(): number; getAngle(): number; distanceTo(p: Vector2Like): number; normalize(): this; copyFrom(p: Vector2Like): this; copyTo(p: T): T; equals(vec: Vector2Like): boolean; get array(): [number, number]; finite(): this; lengthSquared(): number; length(): number; scale(sx: number, sy?: number, origin?: Vector2Like): this; skew(ax: number, ay?: number, origin?: Vector2Like): this; clampMin(...pList: Vector2Like[]): this; clampMax(...pList: Vector2Like[]): this; clone(_onUpdate?: (vec: Vector2) => void): Vector2; toJSON(): Vector2Like; destroy(): void; } declare class BoundingBox { left: number; top: number; width: number; height: number; get x(): number; set x(val: number); get y(): number; set y(val: number); get right(): number; get bottom(): number; get center(): Vector2; get array(): [number, number, number, number]; constructor(left?: number, top?: number, width?: number, height?: number); static from(...boxes: BoundingBox[]): BoundingBox; translate(tx: number, ty: number): this; copy(box: BoundingBox): this; clone(): BoundingBox; } interface TransformableObject { position: { x: number; y: number; }; scale: { x: number; y: number; }; skew: { x: number; y: number; }; rotation: number; } /** * Transform * * | a | c | tx| * | b | d | ty| * | 0 | 0 | 1 | */ declare class Transform2D { a: number; b: number; c: number; d: number; tx: number; ty: number; protected _array?: Float32Array; constructor(a?: number, b?: number, c?: number, d?: number, tx?: number, ty?: number); set(a: number, b: number, c: number, d: number, tx: number, ty: number): this; append(t2d: Transform2D): this; appendFrom(a: Transform2D, b: Transform2D): this; setTransform(x: number, y: number, pivotX: number, pivotY: number, scaleX: number, scaleY: number, rotation: number, skewX: number, skewY: number): this; prepend(t2d: Transform2D): this; skewX(x: number): this; skewY(y: number): this; skew(x: number, y: number): this; translateX(x: number): this; translateY(y: number): this; translateZ(z: number): this; translate3d(x: number, y: number, z: number): this; translate(x: number, y: number, _z?: number): this; scaleX(x: number): this; scaleY(y: number): this; scale3d(x: number, y: number, z?: number): this; scale(x: number, y: number, _z?: number): this; rotateX(x: number): this; rotateY(y: number): this; rotateZ(z: number): this; rotate(angle: number): this; rotate3d(x: number, y: number, z: number, rad: number): this; protected _rotateToScale(rad: number): number; protected _rotate3d(x: number, y: number, z: number, rad: number): number[]; decompose(pivot?: { x: number; y: number; }, output?: TransformableObject): TransformableObject; apply

(pos: Vector2Like, newPos?: P): P; affineInvert(): this; affineInverse(): this; applyAffineInverse

(pos: Vector2Like, newPos?: P): P; identity(): this; isIdentity(): boolean; copyTo(t2d: Transform2D): Transform2D; copyFrom(t2d: Transform2D): this; equals(t2d: Transform2D): boolean; prependCssTransform(cssTransform: string, ctx?: { width?: number; height?: number; }): this; clone(): this; toArray(transpose?: boolean, out?: Float32Array): Float32Array; toString(): string; toJSON(): { a: number; b: number; c: number; d: number; tx: number; ty: number; }; destroy(): void; } type BooleanOp = 'union' | 'intersection' | 'difference' | 'xor'; /** * A flat ring: `[x0, y0, x1, y1, …]` (the same layout {@link Curve.getAdaptiveVertices} produces). * A path is described as an array of such rings (one per sub-path). */ type FlatRing = number[]; /** * Boolean operation between two ring soups, returning the result as flat rings (shells wound * counter-clockwise, holes clockwise — so a nonzero fill renders them correctly). Powered by the * Martinez–Rueda sweep-line clipper (`polygon-clipping`); because the inputs are sampled outlines, * the result is a polygonal approximation of curved paths. */ declare function polygonBoolean(op: BooleanOp, ringsA: FlatRing[], ringsB: FlatRing[]): FlatRing[]; declare function catmullRom(t: number, p0: number, p1: number, p2: number, p3: number): number; interface CssFunctionArg { unit: string | null; value: string; intValue: number; normalizedIntValue: number; normalizedDefaultIntValue: number; } interface CssFunction { name: string; args: CssFunctionArg[]; } interface ParseCssFunctionContext { index?: number; fontSize?: number; width?: number; height?: number; } declare function parseCssFunctions(propertyValue: string, context?: ParseCssFunctionContext): CssFunction[]; declare function parseCssArgs(name: string, value: string, context?: ParseCssFunctionContext): CssFunctionArg[]; declare function parseCssArg(name: string, value: string, context?: ParseCssFunctionContext): CssFunctionArg; declare function cubicBezier(t: number, p0: number, p1: number, p2: number, p3: number): number; interface EvenoddFillRuleResult { index: number; /** Containment depth: 0/2/4… are filled shells, 1/3/5… are holes. */ depth: number; /** Immediate enclosing ring (deepest container), or -1 for a top-level ring. */ parentIndex: number; } /** * Even-odd nesting of a ring soup: classify each ring by how many other rings contain it. * Even depth → a filled shell; odd depth → a hole. Each ring also gets its immediate parent * (the deepest container), so a shell can collect exactly the holes one level inside it — and * an island inside a hole becomes its own shell again (nested donuts work). * * Mirrors {@link nonzeroFillRule}'s output shape, but uses containment parity (even-odd) instead * of winding. Used by `Path2D.fillTriangulate` for `fillRule: 'evenodd'` so WebGL/triangulated * fills get real holes instead of solid overlapping rings. */ declare function evenoddFillRule(paths: number[][]): EvenoddFillRuleResult[]; interface FillTriangulateOptions { holes?: number[]; vertices?: number[]; indices?: number[]; verticesStride?: number; verticesOffset?: number; indicesOffset?: number; /** * Fill rule for multi-sub-path grouping in `Path2D.fillTriangulate`. Takes precedence over * `style.fillRule`. Ignored by the low-level single-ring `fillTriangulate()` function. */ fillRule?: FillRule; style?: Partial; } interface FillTriangulatedResult { vertices: number[]; indices: number[]; } declare function fillTriangulate(pointArray: number[], options?: FillTriangulateOptions): FillTriangulatedResult; declare function getAdaptiveCubicBezierCurvePoints(sX: number, sY: number, x1: number, y1: number, x2: number, y2: number, x: number, y: number, smoothness?: number, points?: number[]): number[]; declare function getAdaptiveQuadraticBezierCurvePoints(sX: number, sY: number, x1: number, y1: number, x: number, y: number, smoothness?: number, points?: number[]): number[]; declare function getDirectedArea(vertices: number[]): number; declare const PI: number; declare const PI_2: number; declare function toKebabCase(str: string): string; /** * Intersection of line p1→p2 with line q1→q2, or `null` when the segments are parallel * (`crossRS === 0`) or the intersection lies too far off p1→p2 (`|t| > 1`). Callers must * handle `null` (e.g. `Path2D.bold` skips the join when there is no usable point). */ declare function getIntersectionPoint(p1: Vector2, p2: Vector2, q1: Vector2, q2: Vector2): Vector2 | null; interface NonzeroFillRuleResult { index: number; parentIndex?: number; dist?: number; winding?: number; } declare function nonzeroFillRule(paths: number[][]): NonzeroFillRuleResult[]; /** * Test whether a point lies inside a single polygon ring. * * `vertices` is a flat `[x0, y0, x1, y1, ...]` array and is treated as implicitly closed * (the last vertex connects back to the first). A ring with fewer than 3 points has no * area and always returns `false`. * * @param point The point to test. * @param vertices Flat vertex array of the ring. * @param fillRule `'nonzero'` (default, matches SVG/Canvas) or `'evenodd'`. */ declare function pointInPolygon(point: Vector2Like, vertices: number[], fillRule?: FillRule): boolean; /** * Test whether a point lies inside a shape composed of multiple rings (sub-paths). * * This is the multi-ring counterpart of {@link pointInPolygon} and is what donut / * hollow shapes need: every ring is evaluated together so holes are honored. * - `'nonzero'`: sum the signed winding numbers of all rings, inside if the total ≠ 0. * - `'evenodd'`: sum the ray-crossing counts of all rings, inside if the total is odd. * * @param point The point to test. * @param polygons Array of flat vertex arrays, one per ring. * @param fillRule `'nonzero'` (default) or `'evenodd'`. */ declare function pointInPolygons(point: Vector2Like, polygons: number[][], fillRule?: FillRule): boolean; /** * Shortest distance from a point to a single line segment a→b. */ declare function pointToSegmentDistance(point: Vector2Like, a: Vector2Like, b: Vector2Like): number; /** * Shortest distance from a point to a polyline. * * @param point The point to test. * @param vertices Flat `[x0, y0, x1, y1, ...]` array of the polyline. * @param closed When `true`, also considers the closing edge from the last vertex back * to the first (use for closed paths, e.g. `z`/`Z` or `CurvePath.autoClose`). */ declare function pointToPolylineDistance(point: Vector2Like, vertices: number[], closed?: boolean): number; declare function quadraticBezier(t: number, p0: number, p1: number, p2: number): number; type LineCap = 'butt' | 'round' | 'square'; type LineJoin = 'round' | 'bevel' | 'miter'; interface StrokeTriangulateOptions { vertices?: number[]; indices?: number[]; /** * When provided, receives one UV pair per generated vertex: u = cumulative * arc length (in path units, unnormalized) along this subpath's centerline, * v = position across the stroke width (0/1 at the two boundaries, 0.5 on * the centerline — round join/cap fan centers). Enables along-the-path * fragment effects (flow pulses, dashes, gradients) with consistent physical * scale across paths of different lengths, and screen-space edge feathering, * without re-triangulating. */ uvs?: number[]; lineStyle?: LineStyle; flipAlignment?: boolean; closed?: boolean; style?: Partial; } interface StrokeTriangulatedResult { vertices: number[]; indices: number[]; uvs?: number[]; } interface LineStyle { width: number; alignment: number; join: LineJoin; cap: LineCap; miterLimit: number; } /** * Derive a triangulator {@link LineStyle} from a (partial) {@link Path2DStyle}. This is what * makes `path.strokeTriangulate()` honor `style.strokeWidth` / `strokeLinejoin` / `strokeLinecap` * / `strokeMiterlimit` instead of silently falling back to a 1px miter hairline. */ declare function resolveLineStyle(style?: Partial): LineStyle; declare function strokeTriangulate(points: number[], options?: StrokeTriangulateOptions): StrokeTriangulatedResult; interface IsPointInFillOptions { fillRule?: FillRule; } interface IsPointInStrokeOptions { strokeWidth?: number; tolerance?: number; closed?: boolean; } declare abstract class Curve { arcLengthDivision: number; protected _lengths: number[]; protected _adaptiveCache?: number[]; /** * Parent composite, set lazily when a composite caches its children. Lets * {@link invalidate} propagate up so an ancestor's caches refresh too. */ _owner?: Curve; protected _invalidating: boolean; abstract getPoint(t: number, output?: Vector2): Vector2; /** * Drop cached arc lengths and the cached sampled outline used by hit testing, then * bubble up to {@link _owner}. Called automatically by {@link applyTransform} and the * `Path2D` mutators; call it manually after mutating control-point coordinates in place — * the caches cannot observe such mutations. */ invalidate(): this; /** Clears this curve's own caches. Composites also clear their children (see override). */ protected _invalidateSelf(): void; /** * Sampled outline cached for repeated hit tests (read-only — do not mutate the result). * Invalidated by {@link invalidate}. */ protected _getCachedAdaptiveVertices(): number[]; getPointAt(u: number, output?: Vector2): Vector2; isClockwise(): boolean; getControlPointRefs(): Vector2[]; /** * Reverse the traversal direction in place (start ↔ end, same geometry). The base * implementation reverses the order of the control-point *values*, which is correct for * line / Bézier / spline primitives whose {@link getControlPointRefs} order matches their * parametric order. {@link RoundCurve} (angle-based) and composites (child order) override it. */ reverse(): this; applyTransform(transform: Transform2D | ((point: Vector2) => void)): this; getUnevenVertices(count?: number, output?: number[]): number[]; getSpacedVertices(count?: number, output?: number[]): number[]; getAdaptiveVertices(output?: number[]): number[]; protected _verticesToPoints(vertices: number[], output?: Vector2[]): Vector2[]; getSpacedPoints(count?: number, output?: Vector2[]): Vector2[]; getUnevenPoints(count?: number, output?: Vector2[]): Vector2[]; getAdaptivePoints(output?: Vector2[]): Vector2[]; getPoints(count?: number, output?: Vector2[]): Vector2[]; getLength(): number; getLengths(): number[]; updateLengths(): void; getUToTMapping(u: number, distance?: number): number; getTangent(t: number, output?: Vector2): Vector2; getTangentAt(u: number, output?: Vector2): Vector2; /** * PathKit-style sample at an absolute arc-length `distance` along the curve: the point, the unit * tangent, and the tangent `angle` in radians. `distance` is clamped to `[0, getLength()]`, so * passing `0`/`getLength()` always yields the endpoints. See {@link PathMeasure} for a wrapper. */ getPosTan(distance: number): { position: Vector2; tangent: Vector2; angle: number; }; getNormal(t: number, output?: Vector2): Vector2; getNormalAt(u: number, output?: Vector2): Vector2; getTForPoint(target: Vector2Like, epsilon?: number): number; getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; getBoundingBox(): BoundingBox; /** * Test whether a point lies inside the area enclosed by this curve. * * The curve is sampled via {@link getAdaptiveVertices} into a single implicitly closed * ring. This is purely geometric (it ignores any `fill`/`stroke` style), mirroring * `CanvasRenderingContext2D.isPointInPath`. * * Composites that hold multiple sub-paths (e.g. {@link Path2D}) override this so holes * are honored — a single `Curve` is always one ring. */ isPointInFill(point: Vector2Like, options?: IsPointInFillOptions): boolean; /** * Test whether a point lies on this curve's stroke, i.e. within `strokeWidth / 2 + tolerance` * of the sampled outline. The point must be in the same coordinate space as the curve. * * Options: `strokeWidth` (path units, default `1`), `tolerance` (extra hit slack in path * units, default `0` — useful for thin strokes; no coordinate scaling is assumed, so convert * pixel tolerance to path units upstream if your path is normalized), and `closed` (whether * to include the closing edge from the last vertex back to the first). */ isPointInStroke(point: Vector2Like, options?: IsPointInStrokeOptions): boolean; /** * Concise PathKit-style fill containment test: `contains(x, y)` is shorthand for * {@link isPointInFill} with a `{ x, y }` point. */ contains(x: number, y: number, options?: IsPointInFillOptions): boolean; getFillVertices(_options?: FillTriangulateOptions): number[]; fillTriangulate(options?: FillTriangulateOptions): FillTriangulatedResult; /** * Whether this curve forms a closed loop (its outline should be stroked without end caps, * stitching the last vertex back to the first). The base test is purely geometric — the first * sampled vertex coincides with the last. Curves that close without a duplicated endpoint * (a full-revolution {@link RoundCurve}, rectangles, polygons) override this. */ isClosed(): boolean; strokeTriangulate(options?: StrokeTriangulateOptions): StrokeTriangulatedResult; toCommands(): Path2DCommand[]; toData(): Path2DData; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: Curve): this; clone(): this; } declare class RoundCurve extends Curve { _center: Vector2; _radius: Vector2; _diff: Vector2; rotate: number; startAngle: number; endAngle: number; clockwise: boolean; get cx(): number; set cx(val: number); get cy(): number; set cy(val: number); get rx(): number; set rx(val: number); get ry(): number; set ry(val: number); get dx(): number; set dx(val: number); get dy(): number; set dy(val: number); constructor(_center?: Vector2, _radius?: Vector2, _diff?: Vector2, rotate?: number, startAngle?: number, endAngle?: number, clockwise?: boolean); isClockwise(): boolean; /** * A circle/ellipse arc is closed when it sweeps (at least) a full revolution — the sampled * outline does not duplicate the start vertex, so the geometric first==last test in the base * class would wrongly report a full circle as open and leave a seam gap in the stroke. */ isClosed(): boolean; reverse(): this; protected _getDeltaAngle(): number; getPoint(t: number, output?: Vector2): Vector2; /** * Point on the ellipse at an absolute angle (mirrors {@link getPoint}'s parameterization, * ignoring `_diff`). */ protected _pointAtAngle(angle: number, output: Vector2): Vector2; /** * Analytical bounds of the (elliptical) arc: the start/end points plus the per-axis * extrema angles that fall within the swept interval. Matches {@link getPoint}, so it is * exact for `ArcCurve`/`EllipseCurve`. The `_diff` offset (used only by the legacy * `_getAdaptiveVerticesByCircle` path) is intentionally ignored here. */ getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; toCommands(): Path2DCommand[]; drawTo(ctx: CanvasRenderingContext2D): this; applyTransform(transform: Transform2D): this; getControlPointRefs(): Vector2[]; protected _getAdaptiveVerticesByArc(output?: number[]): number[]; protected _getAdaptiveVerticesByCircle(output?: number[]): number[]; getAdaptiveVertices(output?: number[]): number[]; copyFrom(source: RoundCurve): this; } declare class ArcCurve extends RoundCurve { constructor(cx?: number, cy?: number, radius?: number, startAngle?: number, endAngle?: number, clockwise?: boolean); drawTo(ctx: CanvasRenderingContext2D): this; } declare class CompositeCurve extends Curve { curves: T[]; protected _adaptiveCacheLen: number; constructor(curves?: T[]); protected _invalidateSelf(): void; protected _getCachedAdaptiveVertices(): number[]; getFlatCurves(): Curve[]; addCurve(curve: T): this; getPoint(t: number, output?: Vector2): Vector2; getLengths(): number[]; updateLengths(): void; getControlPointRefs(): Vector2[]; protected _removeNextPointIfEqualPrevPoint(output: number[], offset: number): number[]; getSpacedVertices(count?: number, output?: number[]): number[]; getAdaptiveVertices(output?: number[]): number[]; /** * A composite is closed when its single child is closed (e.g. a lone full-circle arc), or when * its assembled outline returns to its start (rectangles, polygons, multi-segment loops). */ isClosed(): boolean; strokeTriangulate(options?: StrokeTriangulateOptions): StrokeTriangulatedResult; /** Reverse the sub-curve order and reverse each sub-curve, so the whole outline runs backwards. */ reverse(): this; getFillVertices(options?: FillTriangulateOptions): number[]; applyTransform(transform: Transform2D | ((point: Vector2) => void)): this; getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; getBoundingBox(): BoundingBox; toCommands(): Path2DCommand[]; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: CompositeCurve): this; } declare class CubicBezierCurve extends Curve { p1: Vector2; cp1: Vector2; cp2: Vector2; p2: Vector2; static from(p1x: number, p1y: number, cp1x: number, cp1y: number, cp2x: number, cp2y: number, p2x: number, p2y: number): CubicBezierCurve; constructor(p1?: Vector2, cp1?: Vector2, cp2?: Vector2, p2?: Vector2); getPoint(t: number, output?: Vector2): Vector2; getAdaptiveVertices(output?: number[]): number[]; getControlPointRefs(): Vector2[]; reverse(): this; protected _solveQuadratic(a: number, b: number, c: number): number[]; getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; toCommands(): Path2DCommand[]; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: CubicBezierCurve): this; } declare class EllipseCurve extends RoundCurve { constructor(cx?: number, cy?: number, rx?: number, ry?: number, rotate?: number, startAngle?: number, endAngle?: number, clockwise?: boolean); drawTo(ctx: CanvasRenderingContext2D): this; } declare class LineCurve extends Curve { p1: Vector2; p2: Vector2; static from(p1x: number, p1y: number, p2x: number, p2y: number): LineCurve; constructor(p1?: Vector2, p2?: Vector2); getPoint(t: number, output?: Vector2): Vector2; getPointAt(u: number, output?: Vector2): Vector2; getTangent(_t: number, output?: Vector2): Vector2; getTangentAt(u: number, output?: Vector2): Vector2; getControlPointRefs(): Vector2[]; reverse(): this; getAdaptiveVertices(output?: number[]): number[]; getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; toCommands(): Path2DCommand[]; getFillVertices(options?: FillTriangulateOptions): number[]; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: LineCurve): this; } declare class PolygonCurve extends CompositeCurve { } declare class EquilateralPolygonCurve extends PolygonCurve { cx: number; cy: number; radius: number; sideCount: number; constructor(cx?: number, cy?: number, radius?: number, sideCount?: number); update(): this; copyFrom(source: EquilateralPolygonCurve): this; } declare class QuadraticBezierCurve extends Curve { p1: Vector2; cp: Vector2; p2: Vector2; static from(p1x: number, p1y: number, cpx: number, cpy: number, p2x: number, p2y: number): QuadraticBezierCurve; constructor(p1?: Vector2, cp?: Vector2, p2?: Vector2); getPoint(t: number, output?: Vector2): Vector2; getControlPointRefs(): Vector2[]; reverse(): this; getAdaptiveVertices(output?: number[]): number[]; getMinMax(min?: Vector2, max?: Vector2): { min: Vector2; max: Vector2; }; toCommands(): Path2DCommand[]; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: QuadraticBezierCurve): this; } declare class RectangleCurve extends PolygonCurve { x: number; y: number; width: number; height: number; constructor(x?: number, y?: number, width?: number, height?: number); update(): this; drawTo(ctx: CanvasRenderingContext2D): this; getFillVertices(_options?: FillTriangulateOptions): number[]; copyFrom(source: RectangleCurve): this; } /** * A rounded rectangle, modelled as a real composite of 4 `LineCurve` edges + 4 quarter * `ArcCurve` corners (like {@link RectangleCurve}). `getPoint`/`getLength`/`getMinMax`/ * `toCommands` therefore describe the actual rounded outline — not a bare ellipse. */ declare class RoundRectangleCurve extends CompositeCurve { x: number; y: number; width: number; height: number; radius: number; constructor(x?: number, y?: number, width?: number, height?: number, radius?: number); update(): this; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: RoundRectangleCurve): this; } declare class SplineCurve extends Curve { points: Vector2[]; constructor(points?: Vector2[]); getPoint(t: number, output?: Vector2): Vector2; getControlPointRefs(): Vector2[]; reverse(): this; copyFrom(source: SplineCurve): this; } declare class CurvePath extends CompositeCurve { startPoint?: Vector2; currentPoint?: Vector2; autoClose: boolean; constructor(points?: Vector2[]); addPoints(points: Vector2[]): this; addCommands(commands: Path2DCommand[]): this; addData(data: string): this; /** * A sub-path is closed if it was explicitly closed (`autoClose`, i.e. a `Z`/`closePath`), or if * it forms a geometric loop / wraps a single closed primitive (handled by the base class). */ isClosed(): boolean; /** Reverse direction, then refresh the {@link startPoint}/{@link currentPoint} cursors. */ reverse(): this; protected _closeVertices(output: number[]): number[]; getUnevenVertices(count?: number, output?: number[]): number[]; getSpacedVertices(count?: number, output?: number[]): number[]; getAdaptiveVertices(output?: number[]): number[]; getFillVertices(options?: FillTriangulateOptions): number[]; /** * Same as {@link Curve.isPointInStroke}, but `closed` defaults to this sub-path's actual * closed-ness: explicitly `autoClose`, or geometrically closed (first vertex === last). */ isPointInStroke(point: Vector2Like, options?: IsPointInStrokeOptions): boolean; protected _setCurrentPoint(point: Vector2Like): this; protected _connetLineTo(curve: Curve): this; closePath(): this; moveTo(x: number, y: number): this; lineTo(x: number, y: number): this; bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): this; quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): this; arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; relativeArc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): this; ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; relativeEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; rect(x: number, y: number, width: number, height: number): this; roundRect(x: number, y: number, width: number, height: number, radii: number): this; splineThru(points: Vector2[]): this; drawTo(ctx: CanvasRenderingContext2D): this; copyFrom(source: CurvePath): this; } /** * @link https://developer.mozilla.org/zh-CN/docs/Web/API/Path2D * * Path2D * --CurvePath * ----LineCurve * ----EllipseCurve * ----CubicBezierCurve * ----... */ declare class Path2D extends CompositeCurve { protected _meta?: T; protected _ringsCache?: number[][]; protected _ringsCacheLen: number; currentCurve: CurvePath; style: Partial; get startPoint(): Vector2 | undefined; get currentPoint(): Vector2 | undefined; get strokeWidth(): number; constructor(path?: Path2D | Path2DCommand[] | Path2DData, style?: Partial); getMeta(): T | undefined; setMeta(meta: T | undefined): this; addPath(path: Path2D | CurvePath): this; closePath(): this; moveTo(x: number, y: number): this; lineTo(x: number, y: number): this; bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): this; quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): this; arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): this; ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, counterclockwise?: boolean): this; rect(x: number, y: number, width: number, height: number): this; roundRect(x: number, y: number, width: number, height: number, radii: number): this; reset(): this; addCommands(commands: Path2DCommand[]): this; addData(data: Path2DData): this; splineThru(points: Vector2[]): this; scale(sx: number, sy?: number, target?: Vector2Like): this; skew(ax: number, ay?: number, target?: Vector2Like): this; rotate(rad: number, target?: Vector2Like): this; bold(b: number): this; /** * Test whether a point lies inside the filled area of this path. * * Each sub-path ({@link CurvePath}) is sampled into its own ring and all rings are * evaluated together via {@link pointInPolygons}, so holes (donut / hollow shapes) are * honored. This is purely geometric and ignores `style.fill` — for the `fill: 'none'` * fallback, gate the call upstream (see {@link Path2DSet.hitTest}). * * Defaults `fillRule` to `style.fillRule`, then `'nonzero'` (matching SVG/Canvas). */ protected _invalidateSelf(): void; /** Per-sub-path sampled rings, cached for repeated hit tests. */ protected _getRings(): number[][]; isPointInFill(point: Vector2Like, options?: IsPointInFillOptions): boolean; /** Build a `Path2D` from flat rings (`[x0,y0,…]` per sub-path); closed-and-filled as sub-paths. */ static fromRings(rings: number[][], style?: Partial): Path2D; /** * Boolean (path) operation against another path, returning a NEW `Path2D` whose outline is the * polygonal result. Curves are sampled before clipping, so the result is a polygonal * approximation (see {@link polygonBoolean}). The result inherits this path's `style` unless * overridden via `style`. Holes are emitted as oppositely-wound sub-paths (nonzero fill). */ booleanOp(op: BooleanOp, other: Path2D, style?: Partial): Path2D; /** `this ∪ other` — the combined filled area. */ union(other: Path2D, style?: Partial): Path2D; /** `this ∩ other` — only the overlapping area. */ intersection(other: Path2D, style?: Partial): Path2D; /** `this − other` — this path with `other` cut away. */ difference(other: Path2D, style?: Partial): Path2D; /** `this ⊕ other` — areas covered by exactly one of the two paths. */ xor(other: Path2D, style?: Partial): Path2D; /** * Test whether a point lies on this path's stroke. A hit on any sub-path counts. * * Defaults `strokeWidth` to this path's own {@link strokeWidth} (which is `0` when * `style.stroke` is `'none'`). Each sub-path infers its own closed-ness unless `closed` * is given explicitly. */ isPointInStroke(point: Vector2Like, options?: IsPointInStrokeOptions): boolean; getMinMax(min?: Vector2, max?: Vector2, withStyle?: boolean): { min: Vector2; max: Vector2; }; strokeTriangulate(options?: StrokeTriangulateOptions): StrokeTriangulatedResult; fillTriangulate(options?: FillTriangulateOptions): FillTriangulatedResult; getBoundingBox(withStyle?: boolean): BoundingBox; drawTo(ctx: CanvasRenderingContext2D, style?: Partial): this; drawControlPointsTo(ctx: CanvasRenderingContext2D, style?: Partial): this; toCommands(): Path2DCommand[]; toData(): Path2DData; toSvgPathString(): string; copyFrom(source: Path2D): this; } interface TriangulatedResult { vertices: number[]; indices: number[]; points?: number[]; } declare class Path2DSet { paths: Path2D[]; viewBox?: number[] | undefined; constructor(paths?: Path2D[], viewBox?: number[] | undefined); /** * Test whether a point lies inside the filled area of any path in this set. * Purely geometric (ignores `fill: 'none'`); use {@link hitTest} for style-aware hits. */ isPointInFill(point: Vector2Like, options?: { fillRule?: FillRule; }): boolean; /** * Concise PathKit-style fill containment test across the whole set; shorthand for * {@link isPointInFill} with a `{ x, y }` point. */ contains(x: number, y: number, options?: { fillRule?: FillRule; }): boolean; /** * Find the topmost path hit by a point, or `undefined` if none. * * Paths are tested top-to-bottom (last drawn first). For each path a fill hit is checked * first (skipped when `style.fill` is `'none'`), then — if `stroke` is enabled — a stroke * hit (skipped when `style.stroke` is `'none'`). This honors the "fill: none falls back to * stroke" rule; the coordinate space of `point` must match the paths (no scaling assumed). * * Options: `stroke` (also test strokes, default `true`), `tolerance` (extra stroke hit slack * in path units, default `0`), `fillRule` (overrides each path's own fill rule), and * `forceStroke` (default `false`) — test the stroke even when a path has no `style.stroke`. * Use it when the outline is owned externally (e.g. an element-level outline) rather than * declared on the path itself; otherwise an unstroked open path (a line) is never hit. */ hitTest(point: Vector2Like, options?: { stroke?: boolean; forceStroke?: boolean; tolerance?: number; fillRule?: FillRule; }): Path2D | undefined; getBoundingBox(withStyle?: boolean): BoundingBox | undefined; toTriangulatedSvgString(result?: TriangulatedResult | TriangulatedResult[], padding?: number): string; toTriangulatedSvg(result?: TriangulatedResult | TriangulatedResult[], padding?: number): SVGElement; toSvgString(): string; toSvgUrl(): string; toSvg(): SVGElement; toCanvas(options?: Partial): HTMLCanvasElement; } interface PosTan { position: Vector2; tangent: Vector2; angle: number; } /** * PathKit/Skia-style arc-length measurement over any {@link Curve} (including `CurvePath` and * `Path2D`). Wraps the curve's existing arc-length cache, so repeated queries are cheap. * * ```ts * const measure = new PathMeasure(path) * const { position, angle } = measure.getPosTan(measure.getLength() * progress) * ``` */ declare class PathMeasure { curve: Curve; constructor(curve: Curve); /** Total arc length of the path. */ getLength(): number; /** Whether the path forms a closed loop (see {@link Curve.isClosed}). */ isClosed(): boolean; /** Point + unit tangent + tangent angle at an absolute arc-length `distance` (clamped). */ getPosTan(distance: number): PosTan; /** Point at an absolute arc-length `distance` (clamped to `[0, getLength()]`). */ getPosition(distance: number): Vector2; /** Point + tangent at a normalized progress `t ∈ [0, 1]` along the path. */ getPosTanAtProgress(t: number): PosTan; /** * Evenly sample the path into `count + 1` {@link PosTan} entries (arc-length spaced), e.g. to * lay glyphs along a path or drive an `animate(progress)`-style traversal. */ sample(count?: number): PosTan[]; } declare class FFDControlGrid { rows: number; cols: number; width: number; height: number; controlPoints: { x: number; y: number; }[][]; constructor(rows: number, cols: number, width?: number, height?: number); moveControlPoint(i: number, j: number, dx: number, dy: number): this; } declare function applyFFD(point: Vector2, grid: FFDControlGrid, width?: number, height?: number): void; /** * @link https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes * @link https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion * From * rx ry x-axis-rotation large-arc-flag sweep-flag x y * To * aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation */ declare function parseArcCommand(path: Path2D | CurvePath, rx: number, ry: number, xAxisRotation: number, largeArcFlag: number, sweepFlag: number, start: Vector2, end: Vector2): void; /** * from https://github.com/ppvg/svg-numbers (MIT License) */ declare function parsePathDataArgs(input: string, flags?: number[], stride?: number): number[]; /** * @link http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes */ declare function svgPathCommandsAddToPath2D(commands: Path2DCommand[], path: Path2D | CurvePath): void; declare function svgPathCommandsToData(commands: Path2DCommand[]): Path2DData; declare function svgPathDataToCommands(data: Path2DData): Path2DCommand[]; declare function svgToDom(svg: string | SVGElement): SVGElement; declare function svgToPath2DSet(svg: string | SVGElement): Path2DSet; export { ArcCurve, BoundingBox, CompositeCurve, CubicBezierCurve, Curve, CurvePath, EllipseCurve, EquilateralPolygonCurve, FFDControlGrid, LineCurve, PI, PI_2, Path2D, Path2DSet, PathMeasure, PolygonCurve, QuadraticBezierCurve, RectangleCurve, RoundRectangleCurve, SplineCurve, Transform2D, Vector2, applyFFD, catmullRom, cubicBezier, drawPoint, evenoddFillRule, fillTriangulate, getAdaptiveCubicBezierCurvePoints, getAdaptiveQuadraticBezierCurvePoints, getDirectedArea, getIntersectionPoint, nonzeroFillRule, parseArcCommand, parseCssArg, parseCssArgs, parseCssFunctions, parsePathDataArgs, pointInPolygon, pointInPolygons, pointToPolylineDistance, pointToSegmentDistance, polygonBoolean, quadraticBezier, resolveLineStyle, setCanvasContext, strokeTriangulate, svgPathCommandsAddToPath2D, svgPathCommandsToData, svgPathDataToCommands, svgToDom, svgToPath2DSet, toKebabCase }; export type { BooleanOp, CssFunction, CssFunctionArg, DrawPointOptions, EvenoddFillRuleResult, FillRule, FillTriangulateOptions, FillTriangulatedResult, FlatRing, IsPointInFillOptions, IsPointInStrokeOptions, LineCap, LineJoin, LineStyle, ParseCssFunctionContext, Path2DCommand, Path2DData, Path2DDrawStyle, Path2DStyle, PosTan, StrokeLinecap, StrokeLinejoin, StrokeTriangulateOptions, StrokeTriangulatedResult, TransformableObject, TriangulatedResult, Vector2Like };