@js-draw/math
Version:
A math library for js-draw.
269 lines (268 loc) • 10.5 kB
TypeScript
import LineSegment2 from './LineSegment2';
import Mat33 from '../Mat33';
import Rect2 from './Rect2';
import { Point2 } from '../Vec2';
import Parameterized2DShape from './Parameterized2DShape';
/** Identifiers for different path commands. These commands can make up a {@link Path}. */
export declare enum PathCommandType {
LineTo = 0,
MoveTo = 1,
CubicBezierTo = 2,
QuadraticBezierTo = 3
}
export interface CubicBezierPathCommand {
kind: PathCommandType.CubicBezierTo;
controlPoint1: Point2;
controlPoint2: Point2;
endPoint: Point2;
}
export interface QuadraticBezierPathCommand {
kind: PathCommandType.QuadraticBezierTo;
controlPoint: Point2;
endPoint: Point2;
}
export interface LinePathCommand {
kind: PathCommandType.LineTo;
point: Point2;
}
export interface MoveToPathCommand {
kind: PathCommandType.MoveTo;
point: Point2;
}
export type PathCommand = CubicBezierPathCommand | QuadraticBezierPathCommand | MoveToPathCommand | LinePathCommand;
export interface IntersectionResult {
curve: Parameterized2DShape;
curveIndex: number;
/** Parameter value for the closest point **on** the path to the intersection. @internal */
parameterValue: number;
/** Point at which the intersection occured. */
point: Point2;
}
/** Options for {@link Path.splitNear} and {@link Path.splitAt} */
export interface PathSplitOptions {
/**
* Allows mapping points on newly added segments. This is useful, for example,
* to round points to prevent long decimals when later saving.
*/
mapNewPoint?: (point: Point2) => Point2;
}
/**
* Allows indexing a particular part of a path.
*
* @see {@link Path.at} {@link Path.tangentAt}
*/
export interface CurveIndexRecord {
curveIndex: number;
parameterValue: number;
}
/** Returns a positive number if `a` comes after `b`, 0 if equal, and negative otherwise. */
export declare const compareCurveIndices: (a: CurveIndexRecord, b: CurveIndexRecord) => number;
/**
* Returns a version of `index` with its parameter value incremented by `stepBy`
* (which can be either positive or negative).
*/
export declare const stepCurveIndexBy: (index: CurveIndexRecord, stepBy: number) => CurveIndexRecord;
/**
* Represents a union of lines and curves.
*
* To create a path from a string, see {@link fromString}.
*
* @example
* ```ts,runnable,console
* import {Path, Mat33, Vec2, LineSegment2} from '@js-draw/math';
*
* // Creates a path from an SVG path string.
* // In this case,
* // 1. Move to (0,0)
* // 2. Line to (100,0)
* const path = Path.fromString('M0,0 L100,0');
*
* // Logs the distance from (10,0) to the curve 1 unit
* // away from path. This curve forms a stroke with the path at
* // its center.
* const strokeRadius = 1;
* console.log(path.signedDistance(Vec2.of(10,0), strokeRadius));
*
* // Log a version of the path that's scaled by a factor of 4.
* console.log(path.transformedBy(Mat33.scaling2D(4)).toString());
*
* // Log all intersections of a stroked version of the path with
* // a vertical line segment.
* // (Try removing the `strokeRadius` parameter).
* const segment = new LineSegment2(Vec2.of(5, -100), Vec2.of(5, 100));
* console.log(path.intersection(segment, strokeRadius).map(i => i.point));
* ```
*/
export declare class Path {
readonly startPoint: Point2;
/**
* A rough estimate of the bounding box of the path.
* A slight overestimate.
* See {@link getExactBBox}
*/
readonly bbox: Rect2;
/** The individual shapes that make up this path. */
readonly parts: Readonly<PathCommand>[];
/**
* Creates a new `Path` that starts at `startPoint` and is made up of the path commands,
* `parts`.
*
* See also {@link fromString}
*/
constructor(startPoint: Point2, parts: Readonly<PathCommand>[]);
/**
* Computes and returns the full bounding box for this path.
*
* If a slight over-estimate of a path's bounding box is sufficient, use
* {@link bbox} instead.
*/
getExactBBox(): Rect2;
private cachedGeometry;
get geometry(): Parameterized2DShape[];
/**
* Iterates through the start/end points of each component in this path.
*
* If a start point is equivalent to the end point of the previous segment,
* the point is **not** emitted twice.
*/
startEndPoints(): Generator<import("../Vec3").Vec3, undefined, unknown>;
private cachedPolylineApproximation;
polylineApproximation(): LineSegment2[];
static computeBBoxForSegment(startPoint: Point2, part: PathCommand): Rect2;
/**
* Returns the signed distance between `point` and a curve `strokeRadius` units
* away from this path.
*
* This returns the **signed distance**, which means that points inside this shape
* have their distance negated. For example,
* ```ts,runnable,console
* import {Path, Vec2} from '@js-draw/math';
* console.log(Path.fromString('m0,0 L100,0').signedDistance(Vec2.zero, 1));
* ```
* would print `-1` because (0,0) is on `m0,0 L100,0` and thus one unit away from its boundary.
*
* **Note**: `strokeRadius = strokeWidth / 2`
*/
signedDistance(point: Point2, strokeRadius: number): number;
/**
* Let `S` be a closed path a distance `strokeRadius` from this path.
*
* @returns Approximate intersections of `line` with `S` using ray marching, starting from
* both end points of `line` and each point in `additionalRaymarchStartPoints`.
*/
private raymarchIntersectionWith;
/**
* Returns a list of intersections with this path. If `strokeRadius` is given,
* intersections are approximated with the surface `strokeRadius` away from this.
*
* If `strokeRadius > 0`, the resultant `parameterValue` has no defined value.
*
* **Note**: `strokeRadius` is half of a stroke's width.
*/
intersection(line: LineSegment2, strokeRadius?: number): IntersectionResult[];
/**
* @returns the nearest point on this path to the given `point`.
*/
nearestPointTo(point: Point2): IntersectionResult;
at(index: CurveIndexRecord): import("../Vec3").Vec3;
tangentAt(index: CurveIndexRecord): import("../Vec3").Vec3;
/** Splits this path in two near the given `point`. */
splitNear(point: Point2, options?: PathSplitOptions): [Path] | [Path, Path];
/**
* Returns a copy of this path with `deleteFrom` until `deleteUntil` replaced with `insert`.
*
* This method is analogous to {@link Array.toSpliced}.
*/
spliced(deleteFrom: CurveIndexRecord, deleteTo: CurveIndexRecord, insert: Path | undefined, options?: PathSplitOptions): Path;
splitAt(at: CurveIndexRecord, options?: PathSplitOptions): [Path] | [Path, Path];
splitAt(at: CurveIndexRecord[], options?: PathSplitOptions): Path[];
/**
* Replaces all `MoveTo` commands with `LineTo` commands and connects the end point of this
* path to the start point.
*/
asClosed(): Path;
private static mapPathCommand;
mapPoints(mapping: (point: Point2) => Point2): Path;
transformedBy(affineTransfm: Mat33): Path;
/**
* @internal -- TODO: This method may have incorrect output in some cases.
*/
closedContainsPoint(point: Point2): boolean;
/**
* @returns `true` if this path (interpreted as a closed path) contains the given rectangle.
*/
closedContainsRect(rect: Rect2): boolean;
union(other: Path | PathCommand[] | null, options?: {
allowReverse?: boolean;
}): Path;
/**
* @returns a version of this path with the direction reversed.
*
* Example:
* ```ts,runnable,console
* import {Path} from '@js-draw/math';
* console.log(Path.fromString('m0,0l1,1').reversed()); // -> M1,1 L0,0
* ```
*/
reversed(): Path;
/** Computes and returns the end point of this path */
getEndPoint(): import("../Vec3").Vec3;
/**
* Like {@link closedRoughlyIntersects} except takes stroke width into account.
*
* This is intended to be a very fast and rough approximation. Use {@link intersection}
* and {@link signedDistance} for more accurate (but much slower) intersection calculations.
*
* **Note**: Unlike other methods, this accepts `strokeWidth` (and not `strokeRadius`).
*
* `strokeRadius` is half of `strokeWidth`.
*/
roughlyIntersects(rect: Rect2, strokeWidth?: number): boolean;
/**
* Treats this as a closed path and returns true if part of `rect` is *roughly* within
* this path's interior.
*
* **Note**: Assumes that this is a closed, non-self-intersecting path.
*/
closedRoughlyIntersects(rect: Rect2): boolean;
/** @returns true if all points on this are equivalent to the points on `other` */
eq(other: Path, tolerance?: number): boolean;
/**
* Returns a path that outlines `rect`.
*
* If `lineWidth` is given, the resultant path traces a `lineWidth` thick
* border around `rect`. Otherwise, the resultant path is just the border
* of `rect`.
*/
static fromRect(rect: Rect2, lineWidth?: number | null): Path;
private cachedStringVersion;
/**
* Convert to an [SVG path representation](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths).
*
* If `useNonAbsCommands` is given, relative path commands (e.g. `l10,0`) are to be used instead of
* absolute commands (e.g. `L10,0`).
*
* See also {@link fromString}.
*/
toString(useNonAbsCommands?: boolean, ignoreCache?: boolean): string;
serialize(): string;
static toString(startPoint: Point2, parts: PathCommand[], onlyAbsCommands?: boolean): string;
/**
* Create a `Path` from a subset of the SVG path specification.
*
* Currently, this does not support elliptical arcs or `s` and `t` command
* shorthands. See https://github.com/personalizedrefrigerator/js-draw/pull/19.
*
* @example
* ```ts,runnable,console
* import { Path } from '@js-draw/math';
*
* const path = Path.fromString('m0,0l100,100');
* console.log(path.toString(true)); // true: Prefer relative to absolute path commands
* ```
*/
static fromString(pathString: string): Path;
static fromConvexHullOf(points: Point2[]): Path;
static empty: Path;
}
export default Path;