UNPKG

@joemaddalone/path

Version:

a simple svg path generation utility

648 lines (646 loc) 29 kB
/** * Path Class - A comprehensive SVG path builder * * This class provides a fluent interface for building SVG path data strings. * It supports all standard SVG path commands (move, line, curve, arc) as well as * convenient shape methods (circle, rectangle, polygon, etc.). * * The class maintains internal state for path data, allowing * for method chaining and easy manipulation of SVG paths. * * @example * const path = new Path() * .M(10, 10) // Move to absolute position * .l(50, 0) // Draw line relative to current position * .l(0, 50) // Draw line relative to current position * .close() // Close the path */ declare class Path { /** Array to store SVG path command strings */ private pathData; /** * Constructor - Initializes a new Path instance * * Creates an empty path with no commands or attributes. * Returns the instance for method chaining. * * @returns {Path} The initialized Path instance */ constructor(); /** Convert angle from degrees to radians */ static angleInRadians: (angle: number) => number; /** Convert polar coordinates (radius, angle) to Cartesian coordinates (x, y) */ static polarToCartesian: (cx: number, cy: number, radius: number, angle: number) => { x: number; y: number; }; /** Calculate point position clockwise from center at given angle and radius */ static clockwisePoint: (cx: number, cy: number, radius: number, angle: number) => { x: number; y: number; }; /** Generate array of points in a circle at given radius and center */ static radialPoints: (radius: number, cx: number, cy: number, numOfPoints: number, offsetAngle?: number, vertexSkip?: number) => Array<[number, number]>; /** Position elements in a grid based on array configuration */ static positionByArray: (size: number, shape: any[], sx: number, sy: number) => Array<{ size: number; cx: number; cy: number; ri: number; ci: number; value: any; }>; /** * Macro system - Dynamically add methods to Path prototype * * This allows for runtime extension of the Path class with custom methods. * Useful for adding domain-specific path building functionality. * * @param {string} name - The name of the method to add * @param {Function} fn - The function to add as a method * @returns {Function} The added function * * @example * Path.macro('zigzag', function(width, height) { * return this.M(0, 0).l(width/2, height).l(width/2, -height); * }); */ static macro: (name: string, fn: (...args: any[]) => any) => Function; /** Move to position (x, y) - relative coordinates */ m: (x: number, y: number) => Path; /** Move to position (x, y) - absolute coordinates */ M: (x: number, y: number) => Path; /** Draw line to position (x, y) - relative coordinates */ l: (x: number, y: number) => Path; /** Draw line to position (x, y) - absolute coordinates */ L: (x: number, y: number) => Path; /** Draw horizontal line to x - absolute coordinates */ H: (x: number) => Path; /** Draw horizontal line to x - relative coordinates */ h: (x: number) => Path; /** Draw vertical line to y - absolute coordinates */ V: (y: number) => Path; /** Draw vertical line to y - relative coordinates */ v: (y: number) => Path; /** Draw quadratic curve - absolute coordinates */ Q: (cx: number, cy: number, ex: number, ey: number) => Path; /** Draw quadratic curve - relative coordinates */ q: (cx: number, cy: number, ex: number, ey: number) => Path; /** Draw smooth quadratic curve - absolute coordinates */ T: (ex: number, ey: number) => Path; /** Draw smooth quadratic curve - relative coordinates */ t: (ex: number, ey: number) => Path; /** Draw cubic curve - absolute coordinates */ C: (cx1: number, cy1: number, cx2: number, cy2: number, ex: number, ey: number) => Path; /** Draw cubic curve - relative coordinates */ c: (cx1: number, cy1: number, cx2: number, cy2: number, ex: number, ey: number) => Path; /** Draw smooth cubic curve - absolute coordinates */ S: (cx: number, cy: number, ex: number, ey: number) => Path; /** Draw smooth cubic curve - relative coordinates */ s: (cx: number, cy: number, ex: number, ey: number) => Path; /** Draw arc - absolute coordinates */ A: (rx: number, ry: number, rotation: number, arc: 1 | 0, sweep: 1 | 0, ex: number, ey: number) => Path; /** Draw arc - relative coordinates */ a: (rx: number, ry: number, rotation: number, arc: 1 | 0, sweep: 1 | 0, ex: number, ey: number) => Path; /** Close path - absolute coordinates */ Z: () => Path; /** Close path - relative coordinates */ z: () => Path; /** * Move SVG cursor to position (x, y) * * This is the foundation command that sets the starting point for subsequent * drawing operations. If relative is true, coordinates are relative to the * current cursor position. * * @param {number} x - X coordinate * @param {number} y - Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ moveTo: (x: number, y: number, relative?: boolean) => Path; /** * Draw a straight line to position (x, y) * * Creates a line segment from the current cursor position to the specified * coordinates. If relative is true, coordinates are relative to current position. * * @param {number} x - X coordinate * @param {number} y - Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ lineTo: (x: number, y: number, relative?: boolean) => Path; /** * Draw a horizontal line to x coordinate * * Creates a horizontal line segment from the current cursor position. * Only the x coordinate changes; y remains the same. * * @param {number} x - X coordinate * @param {boolean} relative - Whether x is relative to current position (default: false) * @returns {Path} The Path instance for chaining */ horizontalTo: (x: number, relative?: boolean) => Path; /** * Draw a vertical line to y coordinate * * Creates a vertical line segment from the current cursor position. * Only the y coordinate changes; x remains the same. * * @param {number} x - Y coordinate (parameter name is x for consistency) * @param {boolean} relative - Whether y is relative to current position (default: false) * @returns {Path} The Path instance for chaining */ verticalTo: (x: number, relative?: boolean) => Path; /** * Draw a quadratic Bézier curve * * Creates a quadratic curve using a single control point (cx, cy) to define * the curve shape, ending at (ex, ey). The curve will pass through the * control point's influence area. * * @param {number} cx - Control point X coordinate * @param {number} cy - Control point Y coordinate * @param {number} ex - End point X coordinate * @param {number} ey - End point Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ qCurve: (cx: number, cy: number, ex: number, ey: number, relative?: boolean) => Path; /** * Draw a smooth quadratic Bézier curve * * Creates a quadratic curve that smoothly continues from the previous curve. * The control point is automatically calculated based on the previous curve's * end point, creating a smooth transition. * * @param {number} ex - End point X coordinate * @param {number} ey - End point Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ tCurveTo: (ex: number, ey: number, relative?: boolean) => Path; /** * Draw a cubic Bézier curve * * Creates a cubic curve using two control points (cx1, cy1) and (cx2, cy2) * to define the curve shape, ending at (ex, ey). This provides more control * over the curve than quadratic curves. * * @param {number} cx1 - First control point X coordinate * @param {number} cy1 - First control point Y coordinate * @param {number} cx2 - Second control point X coordinate * @param {number} cy2 - Second control point Y coordinate * @param {number} ex - End point X coordinate * @param {number} ey - End point Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ cCurve: (cx1: number, cy1: number, cx2: number, cy2: number, ex: number, ey: number, relative?: boolean) => Path; /** * Draw a smooth cubic Bézier curve * * Creates a cubic curve that smoothly continues from the previous curve. * The first control point is automatically calculated, while the second * control point (cx, cy) is explicitly specified. * * @param {number} cx - Second control point X coordinate * @param {number} cy - Second control point Y coordinate * @param {number} ex - End point X coordinate * @param {number} ey - End point Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ sCurveTo: (cx: number, cy: number, ex: number, ey: number, relative?: boolean) => Path; /** * Draw an elliptical arc * * Creates an arc segment of an ellipse. The arc is defined by: * - rx, ry: x and y radius of the ellipse * - rotation: rotation of the ellipse in degrees * - arc: large arc flag (0 = small arc, 1 = large arc) * - sweep: sweep flag (0 = counterclockwise, 1 = clockwise) * - ex, ey: end point coordinates * * @param {number} rx - X radius of the ellipse * @param {number} ry - Y radius of the ellipse * @param {number} rotation - Rotation of the ellipse in degrees * @param {1|0} arc - Large arc flag (0 = small, 1 = large) * @param {1|0} sweep - Sweep flag (0 = counterclockwise, 1 = clockwise) * @param {number} ex - End point X coordinate * @param {number} ey - End point Y coordinate * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ arc: (rx: number, ry: number, rotation: number, arc: 1 | 0, sweep: 1 | 0, ex: number, ey: number, relative?: boolean) => Path; /** * Close the current path * * Draws a straight line from the current position back to the starting point * of the current subpath, effectively closing the shape. * * @returns {Path} The Path instance for chaining */ close: () => Path; /** Move down by specified pixels (positive y direction) */ down: (px: number) => Path; /** Move up by specified pixels (negative y direction) */ up: (px: number) => Path; /** Move right by specified pixels (positive x direction) */ right: (px: number) => Path; /** Move left by specified pixels (negative x direction) */ left: (px: number) => Path; /** * Get the path data as an array of command strings * * Returns the internal pathData array containing all SVG path commands * that have been added to this path instance. * * @returns {string[]} Array of SVG path command strings */ toArray: () => string[]; /** * Convert the path to an SVG path data string * * Joins all path commands into a single string suitable for use * as the 'd' attribute of an SVG path element. * * @returns {string} SVG path data string */ toString: () => string; /** * Convert path commands to structured array format * * Parses the path data into an array where each element contains: * - First element: the command letter (M, L, Q, etc.) * - Subsequent elements: the numeric parameters for that command * * @returns {Array<(string|number)[]>} Array of command arrays * * @example * // For path "M10 10 L20 20" * // Returns: [["M", 10, 10], ["L", 20, 20]] */ toCommands: () => Array<(string | number)[]>; /** * Convert path commands to annotated format with named parameters * * Uses the docs mapping to convert numeric parameters to named parameters * based on the command type. This makes the path data more readable * and self-documenting. * * @returns {Array<{fn: string, args?: Record<string, number>}>} Array of annotated commands * * @example * // For path "M10 10 L20 20" * // Returns: [{fn: "M", args: {x: 10, y: 10}}, {fn: "L", args: {x: 20, y: 20}}] */ toAnnotatedCommands: () => Array<{ fn: string; args?: Record<string, number>; }>; /** * Create an SVG path element from the current path * * Generates a DOM SVG path element with all the accumulated path data * and attributes. This is useful for programmatically creating SVG elements * that can be inserted into the DOM. * * @param {Record<string, any>} attributes - Additional attributes to apply to the element * @returns {SVGPathElement} SVG path element */ toElement: (attributes?: {}) => SVGPathElement; /** * Create a circle * * Draws a perfect circle using two arc commands. The circle is centered * at (cx, cy) with the specified size as diameter. * * @param {number} size - Diameter of the circle * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ circle: (size: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create an ellipse * * Draws an ellipse using two arc commands. The ellipse is centered * at (cx, cy) with the specified width and height as dimensions. * * @param {number} width - Width of the ellipse * @param {number} height - Height of the ellipse * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ ellipse: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a kite shape * * Draws a kite (diamond-like shape) with adjustable height offset. * The kite has four points: top, left, bottom, and right. * * @param {number} width - Width of the kite * @param {number} height - Height of the kite * @param {number} dh - Height offset for left/right points (defaults to height * 0.33) * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ kite: (width: number, height: number, dh: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a polygon from an array of points * * Draws a closed polygon by connecting the provided points in sequence. * The polygon automatically closes by drawing a line back to the first point. * * @param {number[][]} points - Array of [x, y] coordinate pairs * @returns {Path} The Path instance for chaining */ polygon: (points: number[][]) => Path; /** * Create a polygram (star-like polygon) * * Draws a polygram by connecting vertices with a specified skip pattern. * For example, with vertexSkip=2, it connects every other vertex, creating * a star pattern. * * @param {number} size - Size of the polygram * @param {number} points - Number of vertices * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {number} vertexSkip - How many vertices to skip when connecting (default: 2) * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ polygram: (size: number, points: number, cx: number, cy: number, vertexSkip?: number, centerEnd?: boolean) => Path; /** * Create a polyline from an array of points * * Draws a series of connected line segments through the provided points. * Unlike polygon, this does not automatically close the shape. * * @param {number[][]} points - Array of [x, y] coordinate pairs * @param {boolean} relative - Whether coordinates are relative (default: false) * @returns {Path} The Path instance for chaining */ polyline: (points: number[][], relative?: boolean) => Path; /** * Create a rectangle * * Draws a rectangle centered at (cx, cy) with the specified width and height. * Uses the convenience directional methods for clean, readable code. * * @param {number} width - Width of the rectangle * @param {number} height - Height of the rectangle * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ rect: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a regular polygon * * Draws a regular polygon with equal sides and angles. The polygon is * centered at (cx, cy) and inscribed in a circle of the specified size. * * @param {number} size - Diameter of the circumscribed circle * @param {number} sides - Number of sides in the polygon * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ regPolygon: (size: number, sides: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a rounded rectangle * * Draws a rectangle with rounded corners. The radius is automatically * adjusted if it exceeds half the width or height of the rectangle. * * @param {number} width - Width of the rectangle * @param {number} height - Height of the rectangle * @param {number} radius - Corner radius * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ roundedRect: (width: number, height: number, radius: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a rounded square * * Convenience method for creating a square with rounded corners. * * @param {number} size - Size of the square * @param {number} radius - Corner radius * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ roundedSquare: (size: number, radius: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a square * * Convenience method for creating a square (equal width and height). * * @param {number} size - Size of the square * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ square: (size: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a star shape * * Draws a star by alternating between inner and outer radius points. * The star has the specified number of points and uses two different * radii to create the characteristic star appearance. * * @param {number} outerSize - Diameter of outer circle for star points * @param {number} innerSize - Diameter of inner circle for star valleys * @param {number} points - Number of star points * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ star: (outerSize: number, innerSize: number, points: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a triangle * * Draws an equilateral triangle centered at (cx, cy) with the specified size. * The triangle points upward by default. * * @param {number} size - Size of the triangle (length of each side) * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ triangle: (size: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a sector (pie slice) * * Draws a pie slice from startAngle to endAngle. The sector is filled * and includes lines from the center to both edges. * * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {number} size - Diameter of the circle * @param {number} startAngle - Starting angle in degrees * @param {number} endAngle - Ending angle in degrees * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ sector: (cx: number, cy: number, size: number, startAngle: number, endAngle: number, centerEnd?: boolean) => Path; /** * Create a segment (arc without center lines) * * Draws an arc segment from startAngle to endAngle without filling * the center area. This creates just the curved edge. * * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {number} size - Diameter of the circle * @param {number} startAngle - Starting angle in degrees * @param {number} endAngle - Ending angle in degrees * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ segment: (cx: number, cy: number, size: number, startAngle: number, endAngle: number, centerEnd?: boolean) => Path; /** * Create radial lines pattern * * Draws lines radiating from inner to outer circles at regular intervals. * Creates a sunburst or starburst effect. * * @param {number} outerSize - Diameter of outer circle * @param {number} innerSize - Diameter of inner circle * @param {number} points - Number of radial lines * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ radialLines: (outerSize: number, innerSize: number, points: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a lens shape * * Draws a lens (oval with pointed ends) using quadratic curves. * The lens is centered at (cx, cy) with the specified width and height. * * @param {number} width - Width of the lens * @param {number} height - Height of the lens * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ lens: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create an omino pattern * * Draws a pattern based on a grid arrangement where each cell can be * connected to its neighbors. The shape array defines which cells are * occupied, and the method draws lines between adjacent cells. * * @param {number} size - Size of each grid cell * @param {any[]} shape - 2D array defining the pattern (1 = occupied, 0 = empty) * @param {number} sx - Starting X coordinate * @param {number} sy - Starting Y coordinate * @param {boolean} lined - Whether to always draw lines (default: false) * @returns {Path} The Path instance for chaining */ omino: (size: number, shape: any[], sx: number, sy: number, lined?: boolean) => Path; /** * Create an isometric cube * * Draws a hexagon (top view of cube) with lines extending from inner * to outer points to create a 3D cube effect. * * @param {number} size - Size of the cube * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ isocube: (size: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a cross shape * * Draws a cross with horizontal and vertical lines intersecting at center. * The cross extends width/2 pixels left and right, height/2 pixels up and down. * * @param {number} width - Width of the cross * @param {number} height - Height of the cross * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ cross: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create an H-shaped symmetry pattern * * Draws an H shape with vertical lines on left and right sides, * connected by a horizontal line in the center. * * @param {number} width - Width of the H pattern * @param {number} height - Height of the H pattern * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ symH: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create an I-shaped symmetry pattern * * Draws an I shape with horizontal lines on top and bottom, * connected by a vertical line in the center. * * @param {number} width - Width of the I pattern * @param {number} height - Height of the I pattern * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ symI: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create a V-shaped symmetry pattern * * Draws a V shape with lines from the top corners meeting at the center bottom. * * @param {number} width - Width of the V pattern * @param {number} height - Height of the V pattern * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ symV: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; /** * Create an X-shaped symmetry pattern * * Draws an X shape with diagonal lines crossing at the center. * * @param {number} width - Width of the X pattern * @param {number} height - Height of the X pattern * @param {number} cx - Center X coordinate * @param {number} cy - Center Y coordinate * @param {boolean} centerEnd - Whether to end at center (default: true) * @returns {Path} The Path instance for chaining */ symX: (width: number, height: number, cx: number, cy: number, centerEnd?: boolean) => Path; } export { Path as default };