new changes

This commit is contained in:
Niranjan
2026-04-07 05:05:28 +05:30
parent 7c070224bd
commit a18bba15f2
29975 changed files with 3247495 additions and 2761 deletions

View File

@@ -0,0 +1,46 @@
import { TransformableSVG } from './TransformableSVG.js';
import type { SVGCommand } from './types.js';
export declare class SVGPathData extends TransformableSVG {
commands: SVGCommand[];
constructor(content: string | SVGCommand[]);
encode(): string;
getBounds(): import("./types.js").TransformFunction & {
minX: number;
maxX: number;
minY: number;
maxY: number;
};
transform(transformFunction: (input: SVGCommand) => SVGCommand | SVGCommand[]): this;
/**
* Reverses the order of path commands to go from end to start
* IMPORTANT: This function expects absolute commands as input.
* @param preserveSubpathOrder If true, keeps subpaths in their original order
*/
reverse(preserveSubpathOrder?: boolean): this;
static encode(commands: SVGCommand[]): string;
static parse(path: string): SVGCommand[];
static readonly CLOSE_PATH: 1;
static readonly MOVE_TO: 2;
static readonly HORIZ_LINE_TO: 4;
static readonly VERT_LINE_TO: 8;
static readonly LINE_TO: 16;
static readonly CURVE_TO: 32;
static readonly SMOOTH_CURVE_TO: 64;
static readonly QUAD_TO: 128;
static readonly SMOOTH_QUAD_TO: 256;
static readonly ARC: 512;
static readonly LINE_COMMANDS: number;
static readonly DRAWING_COMMANDS: number;
}
export declare const COMMAND_ARG_COUNTS: {
2: number;
16: number;
4: number;
8: number;
1: number;
128: number;
256: number;
32: number;
64: number;
512: number;
};

View File

@@ -0,0 +1,89 @@
import { encodeSVGPath } from './SVGPathDataEncoder.js';
import { SVGPathDataParser } from './SVGPathDataParser.js';
import { SVGPathDataTransformer } from './SVGPathDataTransformer.js';
import { TransformableSVG } from './TransformableSVG.js';
export class SVGPathData extends TransformableSVG {
commands;
constructor(content) {
super();
if ('string' === typeof content) {
this.commands = SVGPathData.parse(content);
}
else {
this.commands = content;
}
}
encode() {
return SVGPathData.encode(this.commands);
}
getBounds() {
const boundsTransform = SVGPathDataTransformer.CALCULATE_BOUNDS();
this.transform(boundsTransform);
return boundsTransform;
}
transform(transformFunction) {
const newCommands = [];
for (const command of this.commands) {
const transformedCommand = transformFunction(command);
if (Array.isArray(transformedCommand)) {
newCommands.push(...transformedCommand);
}
else {
newCommands.push(transformedCommand);
}
}
this.commands = newCommands;
return this;
}
/**
* Reverses the order of path commands to go from end to start
* IMPORTANT: This function expects absolute commands as input.
* @param preserveSubpathOrder If true, keeps subpaths in their original order
*/
reverse(preserveSubpathOrder = true) {
this.commands = SVGPathDataTransformer.REVERSE_PATH(this.commands, preserveSubpathOrder);
return this;
}
static encode(commands) {
return encodeSVGPath(commands);
}
static parse(path) {
const parser = new SVGPathDataParser();
const commands = [];
parser.parse(path, commands);
parser.finish(commands);
return commands;
}
static CLOSE_PATH = 1;
static MOVE_TO = 2;
static HORIZ_LINE_TO = 4;
static VERT_LINE_TO = 8;
static LINE_TO = 16;
static CURVE_TO = 32;
static SMOOTH_CURVE_TO = 64;
static QUAD_TO = 128;
static SMOOTH_QUAD_TO = 256;
static ARC = 512;
static LINE_COMMANDS = SVGPathData.LINE_TO | SVGPathData.HORIZ_LINE_TO | SVGPathData.VERT_LINE_TO;
static DRAWING_COMMANDS = SVGPathData.HORIZ_LINE_TO |
SVGPathData.VERT_LINE_TO |
SVGPathData.LINE_TO |
SVGPathData.CURVE_TO |
SVGPathData.SMOOTH_CURVE_TO |
SVGPathData.QUAD_TO |
SVGPathData.SMOOTH_QUAD_TO |
SVGPathData.ARC;
}
export const COMMAND_ARG_COUNTS = {
[SVGPathData.MOVE_TO]: 2,
[SVGPathData.LINE_TO]: 2,
[SVGPathData.HORIZ_LINE_TO]: 1,
[SVGPathData.VERT_LINE_TO]: 1,
[SVGPathData.CLOSE_PATH]: 0,
[SVGPathData.QUAD_TO]: 4,
[SVGPathData.SMOOTH_QUAD_TO]: 2,
[SVGPathData.CURVE_TO]: 6,
[SVGPathData.SMOOTH_CURVE_TO]: 4,
[SVGPathData.ARC]: 7,
};
//# sourceMappingURL=SVGPathData.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SVGPathData.js","sourceRoot":"","sources":["../src/SVGPathData.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,2 @@
import type { SVGCommand } from './types.js';
export declare function encodeSVGPath(commands: SVGCommand | SVGCommand[]): string;

View File

@@ -0,0 +1,92 @@
import { SVGPathData } from './SVGPathData.js';
// Encode SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
// Private consts : Char groups
const WSP = ' ';
export function encodeSVGPath(commands) {
let str = '';
if (!Array.isArray(commands)) {
commands = [commands];
}
for (let i = 0; i < commands.length; i++) {
const command = commands[i];
if (command.type === SVGPathData.CLOSE_PATH) {
str += 'z';
}
else if (command.type === SVGPathData.HORIZ_LINE_TO) {
str += (command.relative ? 'h' : 'H') + command.x;
}
else if (command.type === SVGPathData.VERT_LINE_TO) {
str += (command.relative ? 'v' : 'V') + command.y;
}
else if (command.type === SVGPathData.MOVE_TO) {
str += (command.relative ? 'm' : 'M') + command.x + WSP + command.y;
}
else if (command.type === SVGPathData.LINE_TO) {
str += (command.relative ? 'l' : 'L') + command.x + WSP + command.y;
}
else if (command.type === SVGPathData.CURVE_TO) {
str +=
(command.relative ? 'c' : 'C') +
command.x1 +
WSP +
command.y1 +
WSP +
command.x2 +
WSP +
command.y2 +
WSP +
command.x +
WSP +
command.y;
}
else if (command.type === SVGPathData.SMOOTH_CURVE_TO) {
str +=
(command.relative ? 's' : 'S') +
command.x2 +
WSP +
command.y2 +
WSP +
command.x +
WSP +
command.y;
}
else if (command.type === SVGPathData.QUAD_TO) {
str +=
(command.relative ? 'q' : 'Q') +
command.x1 +
WSP +
command.y1 +
WSP +
command.x +
WSP +
command.y;
}
else if (command.type === SVGPathData.SMOOTH_QUAD_TO) {
str += (command.relative ? 't' : 'T') + command.x + WSP + command.y;
}
else if (command.type === SVGPathData.ARC) {
str +=
(command.relative ? 'a' : 'A') +
command.rX +
WSP +
command.rY +
WSP +
command.xRot +
WSP +
+command.lArcFlag +
WSP +
+command.sweepFlag +
WSP +
command.x +
WSP +
command.y;
}
else {
// Unknown command
throw new Error(`Unexpected command type "${command?.type}" at index ${i}.`);
}
}
return str;
}
//# sourceMappingURL=SVGPathDataEncoder.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SVGPathDataEncoder.js","sourceRoot":"","sources":["../src/SVGPathDataEncoder.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
import { TransformableSVG } from './TransformableSVG.js';
import type { SVGCommand, TransformFunction } from './types.js';
export declare class SVGPathDataParser extends TransformableSVG {
private curNumber;
private curCommandType;
private curCommandRelative;
private canParseCommandOrComma;
private curNumberHasExp;
private curNumberHasExpDigits;
private curNumberHasDecimal;
private curArgs;
constructor();
finish(commands?: SVGCommand[]): SVGCommand[];
parse(str: string, commands?: SVGCommand[]): SVGCommand[];
/**
* Return a wrapper around this parser which applies the transformation on parsed commands.
*/
transform(transform: TransformFunction): this;
}

View File

@@ -0,0 +1,281 @@
// Parse SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
import { COMMAND_ARG_COUNTS, SVGPathData } from './SVGPathData.js';
import { TransformableSVG } from './TransformableSVG.js';
// Private consts : Char groups
const isWhiteSpace = (c) => ' ' === c || '\t' === c || '\r' === c || '\n' === c;
const isDigit = (c) => '0'.charCodeAt(0) <= c.charCodeAt(0) && c.charCodeAt(0) <= '9'.charCodeAt(0);
export class SVGPathDataParser extends TransformableSVG {
curNumber = '';
curCommandType = -1;
curCommandRelative = false;
canParseCommandOrComma = true;
curNumberHasExp = false;
curNumberHasExpDigits = false;
curNumberHasDecimal = false;
curArgs = [];
constructor() {
super();
}
finish(commands = []) {
this.parse(' ', commands);
// Adding residual command
if (0 !== this.curArgs.length || !this.canParseCommandOrComma) {
throw new SyntaxError('Unterminated command at the path end.');
}
return commands;
}
parse(str, commands = []) {
const finishCommand = (command) => {
commands.push(command);
this.curArgs.length = 0;
this.canParseCommandOrComma = true;
};
for (let i = 0; i < str.length; i++) {
const c = str[i];
// White spaces parsing
const isAArcFlag = this.curCommandType === SVGPathData.ARC &&
(this.curArgs.length === 3 || this.curArgs.length === 4) &&
this.curNumber.length === 1 &&
(this.curNumber === '0' || this.curNumber === '1');
const isEndingDigit = isDigit(c) && ((this.curNumber === '0' && c === '0') || isAArcFlag);
if (isDigit(c) && !isEndingDigit) {
this.curNumber += c;
this.curNumberHasExpDigits = this.curNumberHasExp;
continue;
}
if ('e' === c || 'E' === c) {
this.curNumber += c;
this.curNumberHasExp = true;
continue;
}
if (('-' === c || '+' === c) &&
this.curNumberHasExp &&
!this.curNumberHasExpDigits) {
this.curNumber += c;
continue;
}
// if we already have a ".", it means we are starting a new number
if ('.' === c &&
!this.curNumberHasExp &&
!this.curNumberHasDecimal &&
!isAArcFlag) {
this.curNumber += c;
this.curNumberHasDecimal = true;
continue;
}
// New number
if (this.curNumber && -1 !== this.curCommandType) {
const val = Number(this.curNumber);
if (isNaN(val)) {
throw new SyntaxError(`Invalid number ending at ${i}`);
}
if (this.curCommandType === SVGPathData.ARC) {
if (0 === this.curArgs.length || 1 === this.curArgs.length) {
if (0 > val) {
throw new SyntaxError(`Expected positive number, got "${val}" at index "${i}"`);
}
}
else if (3 === this.curArgs.length || 4 === this.curArgs.length) {
if ('0' !== this.curNumber && '1' !== this.curNumber) {
throw new SyntaxError(`Expected a flag, got "${this.curNumber}" at index "${i}"`);
}
}
}
this.curArgs.push(val);
if (this.curArgs.length === COMMAND_ARG_COUNTS[this.curCommandType]) {
if (SVGPathData.HORIZ_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData.HORIZ_LINE_TO,
relative: this.curCommandRelative,
x: val,
});
}
else if (SVGPathData.VERT_LINE_TO === this.curCommandType) {
finishCommand({
type: SVGPathData.VERT_LINE_TO,
relative: this.curCommandRelative,
y: val,
});
// Move to / line to / smooth quadratic curve to commands (x, y)
}
else if (this.curCommandType === SVGPathData.MOVE_TO ||
this.curCommandType === SVGPathData.LINE_TO ||
this.curCommandType === SVGPathData.SMOOTH_QUAD_TO) {
finishCommand({
type: this.curCommandType,
relative: this.curCommandRelative,
x: this.curArgs[0],
y: this.curArgs[1],
});
// Switch to line to state
if (SVGPathData.MOVE_TO === this.curCommandType) {
this.curCommandType = SVGPathData.LINE_TO;
}
}
else if (this.curCommandType === SVGPathData.CURVE_TO) {
finishCommand({
type: SVGPathData.CURVE_TO,
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x2: this.curArgs[2],
y2: this.curArgs[3],
x: this.curArgs[4],
y: this.curArgs[5],
});
}
else if (this.curCommandType === SVGPathData.SMOOTH_CURVE_TO) {
finishCommand({
type: SVGPathData.SMOOTH_CURVE_TO,
relative: this.curCommandRelative,
x2: this.curArgs[0],
y2: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
}
else if (this.curCommandType === SVGPathData.QUAD_TO) {
finishCommand({
type: SVGPathData.QUAD_TO,
relative: this.curCommandRelative,
x1: this.curArgs[0],
y1: this.curArgs[1],
x: this.curArgs[2],
y: this.curArgs[3],
});
}
else if (this.curCommandType === SVGPathData.ARC) {
finishCommand({
type: SVGPathData.ARC,
relative: this.curCommandRelative,
rX: this.curArgs[0],
rY: this.curArgs[1],
xRot: this.curArgs[2],
lArcFlag: this.curArgs[3],
sweepFlag: this.curArgs[4],
x: this.curArgs[5],
y: this.curArgs[6],
});
}
}
this.curNumber = '';
this.curNumberHasExpDigits = false;
this.curNumberHasExp = false;
this.curNumberHasDecimal = false;
this.canParseCommandOrComma = true;
}
// Continue if a white space or a comma was detected
if (isWhiteSpace(c)) {
continue;
}
if (',' === c && this.canParseCommandOrComma) {
// L 0,0, H is not valid:
this.canParseCommandOrComma = false;
continue;
}
// if a sign is detected, then parse the new number
if ('+' === c || '-' === c || '.' === c) {
this.curNumber = c;
this.curNumberHasDecimal = '.' === c;
continue;
}
// if a 0 is detected, then parse the new number
if (isEndingDigit) {
this.curNumber = c;
this.curNumberHasDecimal = false;
continue;
}
// Adding residual command
if (0 !== this.curArgs.length) {
throw new SyntaxError(`Unterminated command at index ${i}.`);
}
if (!this.canParseCommandOrComma) {
throw new SyntaxError(`Unexpected character "${c}" at index ${i}. Command cannot follow comma`);
}
this.canParseCommandOrComma = false;
// Detecting the next command
if ('z' === c || 'Z' === c) {
commands.push({
type: SVGPathData.CLOSE_PATH,
});
this.canParseCommandOrComma = true;
this.curCommandType = -1;
continue;
// Horizontal move to command
}
else if ('h' === c || 'H' === c) {
this.curCommandType = SVGPathData.HORIZ_LINE_TO;
this.curCommandRelative = 'h' === c;
// Vertical move to command
}
else if ('v' === c || 'V' === c) {
this.curCommandType = SVGPathData.VERT_LINE_TO;
this.curCommandRelative = 'v' === c;
// Move to command
}
else if ('m' === c || 'M' === c) {
this.curCommandType = SVGPathData.MOVE_TO;
this.curCommandRelative = 'm' === c;
// Line to command
}
else if ('l' === c || 'L' === c) {
this.curCommandType = SVGPathData.LINE_TO;
this.curCommandRelative = 'l' === c;
// Curve to command
}
else if ('c' === c || 'C' === c) {
this.curCommandType = SVGPathData.CURVE_TO;
this.curCommandRelative = 'c' === c;
// Smooth curve to command
}
else if ('s' === c || 'S' === c) {
this.curCommandType = SVGPathData.SMOOTH_CURVE_TO;
this.curCommandRelative = 's' === c;
// Quadratic bezier curve to command
}
else if ('q' === c || 'Q' === c) {
this.curCommandType = SVGPathData.QUAD_TO;
this.curCommandRelative = 'q' === c;
// Smooth quadratic bezier curve to command
}
else if ('t' === c || 'T' === c) {
this.curCommandType = SVGPathData.SMOOTH_QUAD_TO;
this.curCommandRelative = 't' === c;
// Elliptic arc command
}
else if ('a' === c || 'A' === c) {
this.curCommandType = SVGPathData.ARC;
this.curCommandRelative = 'a' === c;
}
else {
throw new SyntaxError(`Unexpected character "${c}" at index ${i}.`);
}
}
return commands;
}
/**
* Return a wrapper around this parser which applies the transformation on parsed commands.
*/
transform(transform) {
const result = Object.create(this, {
parse: {
value(chunk, commands = []) {
const parsedCommands = Object.getPrototypeOf(this).parse.call(this, chunk);
for (const c of parsedCommands) {
const cT = transform(c);
if (Array.isArray(cT)) {
commands.push(...cT);
}
else {
commands.push(cT);
}
}
return commands;
},
},
});
return result;
}
}
//# sourceMappingURL=SVGPathDataParser.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
import { REVERSE_PATH } from './transformers/reverse_path.js';
import type { SVGCommand, TransformFunction } from './types.js';
declare function ROUND(roundVal?: number): (command: SVGCommand) => SVGCommand;
declare function TO_ABS(): (command: SVGCommand) => any;
declare function TO_REL(): (command: SVGCommand) => any;
declare function NORMALIZE_HVZ(normalizeZ?: boolean, normalizeH?: boolean, normalizeV?: boolean, normalizeC?: boolean): (command: SVGCommand) => any;
declare function NORMALIZE_ST(): (command: SVGCommand) => any;
declare function QT_TO_C(): (command: SVGCommand) => any;
declare function INFO(f: (command: any, prevXAbs: number, prevYAbs: number, pathStartXAbs: number, pathStartYAbs: number) => any | any[]): (command: SVGCommand) => any;
declare function SANITIZE(EPS?: number): (command: SVGCommand) => any;
declare function MATRIX(a: number, b: number, c: number, d: number, e: number, f: number): (command: SVGCommand) => any;
declare function ROTATE(a: number, x?: number, y?: number): (command: SVGCommand) => any;
declare function TRANSLATE(dX: number, dY?: number): (command: SVGCommand) => any;
declare function SCALE(dX: number, dY?: number): (command: SVGCommand) => any;
declare function SKEW_X(a: number): (command: SVGCommand) => any;
declare function SKEW_Y(a: number): (command: SVGCommand) => any;
declare function X_AXIS_SYMMETRY(xOffset?: number): (command: SVGCommand) => any;
declare function Y_AXIS_SYMMETRY(yOffset?: number): (command: SVGCommand) => any;
declare function A_TO_C(): (command: SVGCommand) => any;
declare function ANNOTATE_ARCS(): (command: SVGCommand) => any;
declare function CLONE<T extends SVGCommand>(): (c: T) => T;
declare function CALCULATE_BOUNDS(): TransformFunction & {
minX: number;
maxX: number;
minY: number;
maxY: number;
};
export declare const SVGPathDataTransformer: {
ROUND: typeof ROUND;
TO_ABS: typeof TO_ABS;
TO_REL: typeof TO_REL;
NORMALIZE_HVZ: typeof NORMALIZE_HVZ;
NORMALIZE_ST: typeof NORMALIZE_ST;
QT_TO_C: typeof QT_TO_C;
INFO: typeof INFO;
SANITIZE: typeof SANITIZE;
MATRIX: typeof MATRIX;
ROTATE: typeof ROTATE;
TRANSLATE: typeof TRANSLATE;
SCALE: typeof SCALE;
SKEW_X: typeof SKEW_X;
SKEW_Y: typeof SKEW_Y;
X_AXIS_SYMMETRY: typeof X_AXIS_SYMMETRY;
Y_AXIS_SYMMETRY: typeof Y_AXIS_SYMMETRY;
A_TO_C: typeof A_TO_C;
ANNOTATE_ARCS: typeof ANNOTATE_ARCS;
CLONE: typeof CLONE;
CALCULATE_BOUNDS: typeof CALCULATE_BOUNDS;
REVERSE_PATH: typeof REVERSE_PATH;
};
export {};

View File

@@ -0,0 +1,696 @@
/* eslint @typescript-eslint/no-explicit-any:0 */
// Transform SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
import { a2c, annotateArcCommand, arcAt, assertNumbers, bezierAt, bezierRoot, intersectionUnitCircleLine, arePointsCollinear, } from './mathUtils.js';
import { SVGPathData } from './SVGPathData.js';
import { REVERSE_PATH } from './transformers/reverse_path.js';
// Predefined transforming functions
// Rounds commands values
function ROUND(roundVal = 1e13) {
assertNumbers(roundVal);
function rf(val) {
return Math.round(val * roundVal) / roundVal;
}
return function round(command) {
if ('x1' in command && 'undefined' !== typeof command.x1) {
command.x1 = rf(command.x1);
}
if ('y1' in command && 'undefined' !== typeof command.y1) {
command.y1 = rf(command.y1);
}
if ('x2' in command && 'undefined' !== typeof command.x2) {
command.x2 = rf(command.x2);
}
if ('y2' in command && 'undefined' !== typeof command.y2) {
command.y2 = rf(command.y2);
}
if ('x' in command && 'undefined' !== typeof command.x) {
command.x = rf(command.x);
}
if ('y' in command && 'undefined' !== typeof command.y) {
command.y = rf(command.y);
}
if ('rX' in command && 'undefined' !== typeof command.rX) {
command.rX = rf(command.rX);
}
if ('rY' in command && 'undefined' !== typeof command.rY) {
command.rY = rf(command.rY);
}
return command;
};
}
// Relative to absolute commands
function TO_ABS() {
return INFO((command, prevX, prevY) => {
if (command.relative) {
// x1/y1 values
if ('undefined' !== typeof command.x1) {
command.x1 += prevX;
}
if ('undefined' !== typeof command.y1) {
command.y1 += prevY;
}
// x2/y2 values
if ('undefined' !== typeof command.x2) {
command.x2 += prevX;
}
if ('undefined' !== typeof command.y2) {
command.y2 += prevY;
}
// Finally x/y values
if ('undefined' !== typeof command.x) {
command.x += prevX;
}
if ('undefined' !== typeof command.y) {
command.y += prevY;
}
command.relative = false;
}
return command;
});
}
// Absolute to relative commands
function TO_REL() {
return INFO((command, prevX, prevY) => {
if (!command.relative) {
// x1/y1 values
if ('undefined' !== typeof command.x1) {
command.x1 -= prevX;
}
if ('undefined' !== typeof command.y1) {
command.y1 -= prevY;
}
// x2/y2 values
if ('undefined' !== typeof command.x2) {
command.x2 -= prevX;
}
if ('undefined' !== typeof command.y2) {
command.y2 -= prevY;
}
// Finally x/y values
if ('undefined' !== typeof command.x) {
command.x -= prevX;
}
if ('undefined' !== typeof command.y) {
command.y -= prevY;
}
command.relative = true;
}
return command;
});
}
// Convert H, V, Z, A with rX = 0, and straight Bezier curves to L
function NORMALIZE_HVZ(normalizeZ = true, normalizeH = true, normalizeV = true, normalizeC = true) {
return INFO((command, prevX, prevY, pathStartX, pathStartY) => {
if (isNaN(pathStartX) && !(command.type & SVGPathData.MOVE_TO)) {
throw new Error('path must start with moveto');
}
if (normalizeH && command.type & SVGPathData.HORIZ_LINE_TO) {
command.type = SVGPathData.LINE_TO;
command.y = command.relative ? 0 : prevY;
}
if (normalizeV && command.type & SVGPathData.VERT_LINE_TO) {
command.type = SVGPathData.LINE_TO;
command.x = command.relative ? 0 : prevX;
}
if (normalizeZ && command.type & SVGPathData.CLOSE_PATH) {
command.type = SVGPathData.LINE_TO;
command.x = command.relative ? pathStartX - prevX : pathStartX;
command.y = command.relative ? pathStartY - prevY : pathStartY;
}
// Handle degenerate arcs
if (command.type & SVGPathData.ARC &&
(0 === command.rX || 0 === command.rY)) {
command.type = SVGPathData.LINE_TO;
delete command.rX;
delete command.rY;
delete command.xRot;
delete command.lArcFlag;
delete command.sweepFlag;
}
// Check for quad curves that are lines
if (normalizeC && command.type & SVGPathData.QUAD_TO) {
const startPoint = [prevX, prevY];
const controlPoint = command.relative
? [prevX + command.x1, prevY + command.y1]
: [command.x1, command.y1];
const endPoint = command.relative
? [prevX + command.x, prevY + command.y]
: [command.x, command.y];
if (arePointsCollinear(startPoint, controlPoint, endPoint)) {
command.type = SVGPathData.LINE_TO;
// Keep the endpoint
delete command.x1;
delete command.y1;
}
}
// Check for cubic curves that are lines
if (normalizeC && command.type & SVGPathData.CURVE_TO) {
const startPoint = [prevX, prevY];
const control1 = command.relative
? [prevX + command.x1, prevY + command.y1]
: [command.x1, command.y1];
const control2 = command.relative
? [prevX + command.x2, prevY + command.y2]
: [command.x2, command.y2];
const endPoint = command.relative
? [prevX + command.x, prevY + command.y]
: [command.x, command.y];
// All points need to be collinear
if (arePointsCollinear(startPoint, control1, endPoint) &&
arePointsCollinear(startPoint, control2, endPoint)) {
command.type = SVGPathData.LINE_TO;
// Keep the endpoint
delete command.x1;
delete command.y1;
delete command.x2;
delete command.y2;
}
}
return command;
});
}
/*
* Transforms smooth curves and quads to normal curves and quads (SsTt to CcQq)
*/
function NORMALIZE_ST() {
let prevCurveC2X = NaN;
let prevCurveC2Y = NaN;
let prevQuadCX = NaN;
let prevQuadCY = NaN;
return INFO((command, prevX, prevY) => {
if (command.type & SVGPathData.SMOOTH_CURVE_TO) {
command.type = SVGPathData.CURVE_TO;
prevCurveC2X = isNaN(prevCurveC2X) ? prevX : prevCurveC2X;
prevCurveC2Y = isNaN(prevCurveC2Y) ? prevY : prevCurveC2Y;
command.x1 = command.relative
? prevX - prevCurveC2X
: 2 * prevX - prevCurveC2X;
command.y1 = command.relative
? prevY - prevCurveC2Y
: 2 * prevY - prevCurveC2Y;
}
if (command.type & SVGPathData.CURVE_TO) {
prevCurveC2X = command.relative ? prevX + command.x2 : command.x2;
prevCurveC2Y = command.relative ? prevY + command.y2 : command.y2;
}
else {
prevCurveC2X = NaN;
prevCurveC2Y = NaN;
}
if (command.type & SVGPathData.SMOOTH_QUAD_TO) {
command.type = SVGPathData.QUAD_TO;
prevQuadCX = isNaN(prevQuadCX) ? prevX : prevQuadCX;
prevQuadCY = isNaN(prevQuadCY) ? prevY : prevQuadCY;
command.x1 = command.relative
? prevX - prevQuadCX
: 2 * prevX - prevQuadCX;
command.y1 = command.relative
? prevY - prevQuadCY
: 2 * prevY - prevQuadCY;
}
if (command.type & SVGPathData.QUAD_TO) {
prevQuadCX = command.relative ? prevX + command.x1 : command.x1;
prevQuadCY = command.relative ? prevY + command.y1 : command.y1;
}
else {
prevQuadCX = NaN;
prevQuadCY = NaN;
}
return command;
});
}
/*
* A quadratic bézier curve can be represented by a cubic bézier curve which has
* the same end points as the quadratic and both control points in place of the
* quadratic"s one.
*
* This transformer replaces QqTt commands with Cc commands respectively.
* This is useful for reading path data into a system which only has a
* representation for cubic curves.
*/
function QT_TO_C() {
let prevQuadX1 = NaN;
let prevQuadY1 = NaN;
return INFO((command, prevX, prevY) => {
if (command.type & SVGPathData.SMOOTH_QUAD_TO) {
command.type = SVGPathData.QUAD_TO;
prevQuadX1 = isNaN(prevQuadX1) ? prevX : prevQuadX1;
prevQuadY1 = isNaN(prevQuadY1) ? prevY : prevQuadY1;
command.x1 = command.relative
? prevX - prevQuadX1
: 2 * prevX - prevQuadX1;
command.y1 = command.relative
? prevY - prevQuadY1
: 2 * prevY - prevQuadY1;
}
if (command.type & SVGPathData.QUAD_TO) {
prevQuadX1 = command.relative ? prevX + command.x1 : command.x1;
prevQuadY1 = command.relative ? prevY + command.y1 : command.y1;
const x1 = command.x1;
const y1 = command.y1;
command.type = SVGPathData.CURVE_TO;
command.x1 = ((command.relative ? 0 : prevX) + x1 * 2) / 3;
command.y1 = ((command.relative ? 0 : prevY) + y1 * 2) / 3;
command.x2 = (command.x + x1 * 2) / 3;
command.y2 = (command.y + y1 * 2) / 3;
}
else {
prevQuadX1 = NaN;
prevQuadY1 = NaN;
}
return command;
});
}
function INFO(f) {
let prevXAbs = 0;
let prevYAbs = 0;
let pathStartXAbs = NaN;
let pathStartYAbs = NaN;
return function transform(command) {
if (isNaN(pathStartXAbs) && !(command.type & SVGPathData.MOVE_TO)) {
throw new Error('path must start with moveto');
}
const result = f(command, prevXAbs, prevYAbs, pathStartXAbs, pathStartYAbs);
if (command.type & SVGPathData.CLOSE_PATH) {
prevXAbs = pathStartXAbs;
prevYAbs = pathStartYAbs;
}
if ('x' in command && 'undefined' !== typeof command.x) {
prevXAbs = command.relative ? prevXAbs + command.x : command.x;
}
if ('y' in command && 'undefined' !== typeof command.y) {
prevYAbs = command.relative ? prevYAbs + command.y : command.y;
}
if (command.type & SVGPathData.MOVE_TO) {
pathStartXAbs = prevXAbs;
pathStartYAbs = prevYAbs;
}
return result;
};
}
/*
* remove 0-length segments
*/
function SANITIZE(EPS = 0) {
assertNumbers(EPS);
let prevCurveC2X = NaN;
let prevCurveC2Y = NaN;
let prevQuadCX = NaN;
let prevQuadCY = NaN;
return INFO((command, prevX, prevY, pathStartX, pathStartY) => {
const abs = Math.abs;
let skip = false;
let x1Rel = 0;
let y1Rel = 0;
if (command.type & SVGPathData.SMOOTH_CURVE_TO) {
x1Rel = isNaN(prevCurveC2X) ? 0 : prevX - prevCurveC2X;
y1Rel = isNaN(prevCurveC2Y) ? 0 : prevY - prevCurveC2Y;
}
if (command.type & (SVGPathData.CURVE_TO | SVGPathData.SMOOTH_CURVE_TO)) {
prevCurveC2X = command.relative ? prevX + command.x2 : command.x2;
prevCurveC2Y = command.relative ? prevY + command.y2 : command.y2;
}
else {
prevCurveC2X = NaN;
prevCurveC2Y = NaN;
}
if (command.type & SVGPathData.SMOOTH_QUAD_TO) {
prevQuadCX = isNaN(prevQuadCX) ? prevX : 2 * prevX - prevQuadCX;
prevQuadCY = isNaN(prevQuadCY) ? prevY : 2 * prevY - prevQuadCY;
}
else if (command.type & SVGPathData.QUAD_TO) {
prevQuadCX = command.relative ? prevX + command.x1 : command.x1;
prevQuadCY = command.relative ? prevY + command.y1 : command.y2;
}
else {
prevQuadCX = NaN;
prevQuadCY = NaN;
}
if (command.type & SVGPathData.LINE_COMMANDS ||
(command.type & SVGPathData.ARC &&
(0 === command.rX || 0 === command.rY || !command.lArcFlag)) ||
command.type & SVGPathData.CURVE_TO ||
command.type & SVGPathData.SMOOTH_CURVE_TO ||
command.type & SVGPathData.QUAD_TO ||
command.type & SVGPathData.SMOOTH_QUAD_TO) {
const xRel = 'undefined' === typeof command.x
? 0
: command.relative
? command.x
: command.x - prevX;
const yRel = 'undefined' === typeof command.y
? 0
: command.relative
? command.y
: command.y - prevY;
x1Rel = !isNaN(prevQuadCX)
? prevQuadCX - prevX
: 'undefined' === typeof command.x1
? x1Rel
: command.relative
? command.x
: command.x1 - prevX;
y1Rel = !isNaN(prevQuadCY)
? prevQuadCY - prevY
: 'undefined' === typeof command.y1
? y1Rel
: command.relative
? command.y
: command.y1 - prevY;
const x2Rel = 'undefined' === typeof command.x2
? 0
: command.relative
? command.x
: command.x2 - prevX;
const y2Rel = 'undefined' === typeof command.y2
? 0
: command.relative
? command.y
: command.y2 - prevY;
if (abs(xRel) <= EPS &&
abs(yRel) <= EPS &&
abs(x1Rel) <= EPS &&
abs(y1Rel) <= EPS &&
abs(x2Rel) <= EPS &&
abs(y2Rel) <= EPS) {
skip = true;
}
}
if (command.type & SVGPathData.CLOSE_PATH) {
if (abs(prevX - pathStartX) <= EPS && abs(prevY - pathStartY) <= EPS) {
skip = true;
}
}
return skip ? [] : command;
});
}
// SVG Transforms : http://www.w3.org/TR/SVGTiny12/coords.html#TransformList
// Matrix : http://apike.ca/prog_svg_transform.html
// a c e
// b d f
function MATRIX(a, b, c, d, e, f) {
assertNumbers(a, b, c, d, e, f);
return INFO((command, prevX, prevY, pathStartX) => {
const origX1 = command.x1;
const origX2 = command.x2;
// if isNaN(pathStartX), then this is the first command, which is ALWAYS an
// absolute MOVE_TO, regardless what the relative flag says
const comRel = command.relative && !isNaN(pathStartX);
const x = 'undefined' !== typeof command.x ? command.x : comRel ? 0 : prevX;
const y = 'undefined' !== typeof command.y ? command.y : comRel ? 0 : prevY;
if (command.type & SVGPathData.HORIZ_LINE_TO && 0 !== b) {
command.type = SVGPathData.LINE_TO;
command.y = command.relative ? 0 : prevY;
}
if (command.type & SVGPathData.VERT_LINE_TO && 0 !== c) {
command.type = SVGPathData.LINE_TO;
command.x = command.relative ? 0 : prevX;
}
if ('undefined' !== typeof command.x) {
command.x = command.x * a + y * c + (comRel ? 0 : e);
}
if ('undefined' !== typeof command.y) {
command.y = x * b + command.y * d + (comRel ? 0 : f);
}
if ('undefined' !== typeof command.x1) {
command.x1 = command.x1 * a + command.y1 * c + (comRel ? 0 : e);
}
if ('undefined' !== typeof command.y1) {
command.y1 = origX1 * b + command.y1 * d + (comRel ? 0 : f);
}
if ('undefined' !== typeof command.x2) {
command.x2 = command.x2 * a + command.y2 * c + (comRel ? 0 : e);
}
if ('undefined' !== typeof command.y2) {
command.y2 = origX2 * b + command.y2 * d + (comRel ? 0 : f);
}
function sqr(x) {
return x * x;
}
const det = a * d - b * c;
if ('undefined' !== typeof command.xRot) {
// Skip if this is a pure translation
if (1 !== a || 0 !== b || 0 !== c || 1 !== d) {
// Special case for singular matrix
if (0 === det) {
// In the singular case, the arc is compressed to a line. The actual geometric image of the original
// curve under this transform possibly extends beyond the starting and/or ending points of the segment, but
// for simplicity we ignore this detail and just replace this command with a single line segment.
delete command.rX;
delete command.rY;
delete command.xRot;
delete command.lArcFlag;
delete command.sweepFlag;
command.type = SVGPathData.LINE_TO;
}
else {
// Convert to radians
const xRot = (command.xRot * Math.PI) / 180;
// Convert rotated ellipse to general conic form
// x0^2/rX^2 + y0^2/rY^2 - 1 = 0
// x0 = x*cos(xRot) + y*sin(xRot)
// y0 = -x*sin(xRot) + y*cos(xRot)
// --> A*x^2 + B*x*y + C*y^2 - 1 = 0, where
const sinRot = Math.sin(xRot);
const cosRot = Math.cos(xRot);
const xCurve = 1 / sqr(command.rX);
const yCurve = 1 / sqr(command.rY);
const A = sqr(cosRot) * xCurve + sqr(sinRot) * yCurve;
const B = 2 * sinRot * cosRot * (xCurve - yCurve);
const C = sqr(sinRot) * xCurve + sqr(cosRot) * yCurve;
// Apply matrix to A*x^2 + B*x*y + C*y^2 - 1 = 0
// x1 = a*x + c*y
// y1 = b*x + d*y
// (we can ignore e and f, since pure translations don"t affect the shape of the ellipse)
// --> A1*x1^2 + B1*x1*y1 + C1*y1^2 - det^2 = 0, where
const A1 = A * d * d - B * b * d + C * b * b;
const B1 = B * (a * d + b * c) - 2 * (A * c * d + C * a * b);
const C1 = A * c * c - B * a * c + C * a * a;
// Unapply newXRot to get back to axis-aligned ellipse equation
// x1 = x2*cos(newXRot) - y2*sin(newXRot)
// y1 = x2*sin(newXRot) + y2*cos(newXRot)
// A1*x1^2 + B1*x1*y1 + C1*y1^2 - det^2 =
// x2^2*(A1*cos(newXRot)^2 + B1*sin(newXRot)*cos(newXRot) + C1*sin(newXRot)^2)
// + x2*y2*(2*(C1 - A1)*sin(newXRot)*cos(newXRot) + B1*(cos(newXRot)^2 - sin(newXRot)^2))
// + y2^2*(A1*sin(newXRot)^2 - B1*sin(newXRot)*cos(newXRot) + C1*cos(newXRot)^2)
// (which must have the same zeroes as)
// x2^2/newRX^2 + y2^2/newRY^2 - 1
// (so we have)
// 2*(C1 - A1)*sin(newXRot)*cos(newXRot) + B1*(cos(newXRot)^2 - sin(newXRot)^2) = 0
// (A1 - C1)*sin(2*newXRot) = B1*cos(2*newXRot)
// 2*newXRot = atan2(B1, A1 - C1)
const newXRot = ((Math.atan2(B1, A1 - C1) + Math.PI) % Math.PI) / 2;
// For any integer n, (atan2(B1, A1 - C1) + n*pi)/2 is a solution to the above; incrementing n just swaps
// the x and y radii computed below (since that"s what rotating an ellipse by pi/2 does). Choosing the
// rotation between 0 and pi/2 eliminates the ambiguity and leads to more predictable output.
// Finally, we get newRX and newRY from the same-zeroes relationship that gave us newXRot
const newSinRot = Math.sin(newXRot);
const newCosRot = Math.cos(newXRot);
command.rX =
Math.abs(det) /
Math.sqrt(A1 * sqr(newCosRot) +
B1 * newSinRot * newCosRot +
C1 * sqr(newSinRot));
command.rY =
Math.abs(det) /
Math.sqrt(A1 * sqr(newSinRot) -
B1 * newSinRot * newCosRot +
C1 * sqr(newCosRot));
command.xRot = (newXRot * 180) / Math.PI;
}
}
}
// sweepFlag needs to be inverted when mirroring shapes
// see http://www.itk.ilstu.edu/faculty/javila/SVG/SVG_drawing1/elliptical_curve.htm
// m 65,10 a 50,25 0 1 0 50,25
// M 65,60 A 50,25 0 1 1 115,35
if ('undefined' !== typeof command.sweepFlag && 0 > det) {
command.sweepFlag = +!command.sweepFlag;
}
return command;
});
}
function ROTATE(a, x = 0, y = 0) {
assertNumbers(a, x, y);
const sin = Math.sin(a);
const cos = Math.cos(a);
return MATRIX(cos, sin, -sin, cos, x - x * cos + y * sin, y - x * sin - y * cos);
}
function TRANSLATE(dX, dY = 0) {
assertNumbers(dX, dY);
return MATRIX(1, 0, 0, 1, dX, dY);
}
function SCALE(dX, dY = dX) {
assertNumbers(dX, dY);
return MATRIX(dX, 0, 0, dY, 0, 0);
}
function SKEW_X(a) {
assertNumbers(a);
return MATRIX(1, 0, Math.tan(a), 1, 0, 0);
}
function SKEW_Y(a) {
assertNumbers(a);
return MATRIX(1, Math.tan(a), 0, 1, 0, 0);
}
function X_AXIS_SYMMETRY(xOffset = 0) {
assertNumbers(xOffset);
return MATRIX(-1, 0, 0, 1, xOffset, 0);
}
function Y_AXIS_SYMMETRY(yOffset = 0) {
assertNumbers(yOffset);
return MATRIX(1, 0, 0, -1, 0, yOffset);
}
// Convert arc commands to curve commands
function A_TO_C() {
return INFO((command, prevX, prevY) => {
if (SVGPathData.ARC === command.type) {
return a2c(command, command.relative ? 0 : prevX, command.relative ? 0 : prevY);
}
return command;
});
}
// @see annotateArcCommand
function ANNOTATE_ARCS() {
return INFO((c, x1, y1) => {
if (c.relative) {
x1 = 0;
y1 = 0;
}
if (SVGPathData.ARC === c.type) {
annotateArcCommand(c, x1, y1);
}
return c;
});
}
function CLONE() {
return (c) => {
return { ...c };
};
}
// @see annotateArcCommand
function CALCULATE_BOUNDS() {
const clone = CLONE();
const toAbs = TO_ABS();
const qtToC = QT_TO_C();
const normST = NORMALIZE_ST();
const f = INFO((command, prevXAbs, prevYAbs) => {
const c = normST(qtToC(toAbs(clone(command))));
function fixX(absX) {
if (absX > f.maxX) {
f.maxX = absX;
}
if (absX < f.minX) {
f.minX = absX;
}
}
function fixY(absY) {
if (absY > f.maxY) {
f.maxY = absY;
}
if (absY < f.minY) {
f.minY = absY;
}
}
if (c.type & SVGPathData.DRAWING_COMMANDS) {
fixX(prevXAbs);
fixY(prevYAbs);
}
if (c.type & SVGPathData.HORIZ_LINE_TO) {
fixX(c.x);
}
if (c.type & SVGPathData.VERT_LINE_TO) {
fixY(c.y);
}
if (c.type & SVGPathData.LINE_TO) {
fixX(c.x);
fixY(c.y);
}
if (c.type & SVGPathData.CURVE_TO) {
// add start and end points
fixX(c.x);
fixY(c.y);
const xDerivRoots = bezierRoot(prevXAbs, c.x1, c.x2, c.x);
for (const derivRoot of xDerivRoots) {
if (0 < derivRoot && 1 > derivRoot) {
fixX(bezierAt(prevXAbs, c.x1, c.x2, c.x, derivRoot));
}
}
const yDerivRoots = bezierRoot(prevYAbs, c.y1, c.y2, c.y);
for (const derivRoot of yDerivRoots) {
if (0 < derivRoot && 1 > derivRoot) {
fixY(bezierAt(prevYAbs, c.y1, c.y2, c.y, derivRoot));
}
}
}
if (c.type & SVGPathData.ARC) {
// add start and end points
fixX(c.x);
fixY(c.y);
annotateArcCommand(c, prevXAbs, prevYAbs);
// p = cos(phi) * xv + sin(phi) * yv
// dp = -sin(phi) * xv + cos(phi) * yv = 0
const xRotRad = (c.xRot / 180) * Math.PI;
// points on ellipse for phi = 0° and phi = 90°
const x0 = Math.cos(xRotRad) * c.rX;
const y0 = Math.sin(xRotRad) * c.rX;
const x90 = -Math.sin(xRotRad) * c.rY;
const y90 = Math.cos(xRotRad) * c.rY;
// annotateArcCommand returns phi1 and phi2 such that -180° < phi1 < 180° and phi2 is smaller or greater
// depending on the sweep flag. Calculate phiMin, phiMax such that -180° < phiMin < 180° and phiMin < phiMax
const [phiMin, phiMax] = c.phi1 < c.phi2
? [c.phi1, c.phi2]
: -180 > c.phi2
? [c.phi2 + 360, c.phi1 + 360]
: [c.phi2, c.phi1];
const normalizeXiEta = ([xi, eta]) => {
const phiRad = Math.atan2(eta, xi);
const phi = (phiRad * 180) / Math.PI;
return phi < phiMin ? phi + 360 : phi;
};
// xi = cos(phi), eta = sin(phi)
const xDerivRoots = intersectionUnitCircleLine(x90, -x0, 0).map(normalizeXiEta);
for (const derivRoot of xDerivRoots) {
if (derivRoot > phiMin && derivRoot < phiMax) {
fixX(arcAt(c.cX, x0, x90, derivRoot));
}
}
const yDerivRoots = intersectionUnitCircleLine(y90, -y0, 0).map(normalizeXiEta);
for (const derivRoot of yDerivRoots) {
if (derivRoot > phiMin && derivRoot < phiMax) {
fixY(arcAt(c.cY, y0, y90, derivRoot));
}
}
}
return command;
});
f.minX = Infinity;
f.maxX = -Infinity;
f.minY = Infinity;
f.maxY = -Infinity;
return f;
}
export const SVGPathDataTransformer = {
ROUND,
TO_ABS,
TO_REL,
NORMALIZE_HVZ,
NORMALIZE_ST,
QT_TO_C,
INFO,
SANITIZE,
MATRIX,
ROTATE,
TRANSLATE,
SCALE,
SKEW_X,
SKEW_Y,
X_AXIS_SYMMETRY,
Y_AXIS_SYMMETRY,
A_TO_C,
ANNOTATE_ARCS,
CLONE,
CALCULATE_BOUNDS,
REVERSE_PATH,
};
//# sourceMappingURL=SVGPathDataTransformer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,21 @@
import type { TransformFunction } from './types.js';
export declare abstract class TransformableSVG {
round(x?: number): this;
toAbs(): this;
toRel(): this;
normalizeHVZ(a?: boolean, b?: boolean, c?: boolean): this;
normalizeST(): this;
qtToC(): this;
aToC(): this;
sanitize(eps?: number): this;
translate(x: number, y?: number): this;
scale(x: number, y?: number): this;
rotate(a: number, x?: number, y?: number): this;
matrix(a: number, b: number, c: number, d: number, e: number, f: number): this;
skewX(a: number): this;
skewY(a: number): this;
xSymmetry(xOffset?: number): this;
ySymmetry(yOffset?: number): this;
annotateArcs(): this;
abstract transform(transformFunction: TransformFunction): this;
}

View File

@@ -0,0 +1,55 @@
import { SVGPathDataTransformer } from './SVGPathDataTransformer.js';
export class TransformableSVG {
round(x) {
return this.transform(SVGPathDataTransformer.ROUND(x));
}
toAbs() {
return this.transform(SVGPathDataTransformer.TO_ABS());
}
toRel() {
return this.transform(SVGPathDataTransformer.TO_REL());
}
normalizeHVZ(a, b, c) {
return this.transform(SVGPathDataTransformer.NORMALIZE_HVZ(a, b, c));
}
normalizeST() {
return this.transform(SVGPathDataTransformer.NORMALIZE_ST());
}
qtToC() {
return this.transform(SVGPathDataTransformer.QT_TO_C());
}
aToC() {
return this.transform(SVGPathDataTransformer.A_TO_C());
}
sanitize(eps) {
return this.transform(SVGPathDataTransformer.SANITIZE(eps));
}
translate(x, y) {
return this.transform(SVGPathDataTransformer.TRANSLATE(x, y));
}
scale(x, y) {
return this.transform(SVGPathDataTransformer.SCALE(x, y));
}
rotate(a, x, y) {
return this.transform(SVGPathDataTransformer.ROTATE(a, x, y));
}
matrix(a, b, c, d, e, f) {
return this.transform(SVGPathDataTransformer.MATRIX(a, b, c, d, e, f));
}
skewX(a) {
return this.transform(SVGPathDataTransformer.SKEW_X(a));
}
skewY(a) {
return this.transform(SVGPathDataTransformer.SKEW_Y(a));
}
xSymmetry(xOffset) {
return this.transform(SVGPathDataTransformer.X_AXIS_SYMMETRY(xOffset));
}
ySymmetry(yOffset) {
return this.transform(SVGPathDataTransformer.Y_AXIS_SYMMETRY(yOffset));
}
annotateArcs() {
return this.transform(SVGPathDataTransformer.ANNOTATE_ARCS());
}
}
//# sourceMappingURL=TransformableSVG.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TransformableSVG.js","sourceRoot":"","sources":["../src/TransformableSVG.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,MAAM,OAAgB,gBAAgB;IACpC,KAAK,CAAC,CAAU;QACd,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,YAAY,CAAC,CAAW,EAAE,CAAW,EAAE,CAAW;QAChD,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,YAAY,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ,CAAC,GAAY;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,SAAS,CAAC,CAAS,EAAE,CAAU;QAC7B,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,CAAS,EAAE,CAAU;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,CAAS,EAAE,CAAU,EAAE,CAAU;QACtC,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,CAAS;QACrE,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,KAAK,CAAC,CAAS;QACb,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,CAAS;QACb,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,SAAS,CAAC,OAAgB;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,SAAS,CAAC,OAAgB;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,EAAE,CAAC,CAAC;IAChE,CAAC;CAGF"}

View File

@@ -0,0 +1,5 @@
export * from './SVGPathData.js';
export type * from './types.js';
export { encodeSVGPath } from './SVGPathDataEncoder.js';
export { SVGPathDataParser } from './SVGPathDataParser.js';
export { SVGPathDataTransformer } from './SVGPathDataTransformer.js';

View File

@@ -0,0 +1,5 @@
export * from './SVGPathData.js';
export { encodeSVGPath } from './SVGPathDataEncoder.js';
export { SVGPathDataParser } from './SVGPathDataParser.js';
export { SVGPathDataTransformer } from './SVGPathDataTransformer.js';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}

View File

@@ -0,0 +1,39 @@
import { type CommandA, type CommandC } from './types.js';
export type Point = [x: number, y: number];
export declare function rotate([x, y]: Point, rad: number): number[];
export declare function assertNumbers(...numbers: number[]): boolean;
/**
* https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
* Fixes rX and rY.
* Ensures lArcFlag and sweepFlag are 0 or 1
* Adds center coordinates: command.cX, command.cY (relative or absolute, depending on command.relative)
* Adds start and end arc parameters (in degrees): command.phi1, command.phi2; phi1 < phi2 iff. c.sweepFlag == true
*/
export declare function annotateArcCommand(c: CommandA, x1: number, y1: number): void;
/**
* Solves a quadratic system of equations of the form
* a * x + b * y = c
* x² + y² = 1
* This can be understood as the intersection of the unit circle with a line.
* => y = (c - a x) / b
* => x² + (c - a x)² / b² = 1
* => x² b² + c² - 2 c a x + a² x² = b²
* => (a² + b²) x² - 2 a c x + (c² - b²) = 0
*/
export declare function intersectionUnitCircleLine(a: number, b: number, c: number): [number, number][];
export declare const DEG: number;
export declare function lerp(a: number, b: number, t: number): number;
export declare function arcAt(c: number, x1: number, x2: number, phiDeg: number): number;
export declare function bezierRoot(x0: number, x1: number, x2: number, x3: number): number[];
export declare function bezierAt(x0: number, x1: number, x2: number, x3: number, t: number): number;
export declare function a2c(arc: CommandA, x0: number, y0: number): CommandC[];
/**
* Determines if three points are collinear (lie on the same straight line)
* and the middle point is on the line segment between the first and third points
*
* @param p1 First point [x, y]
* @param p2 Middle point that might be removed
* @param p3 Last point [x, y]
* @returns true if the points are collinear and p2 is on the segment p1-p3
*/
export declare function arePointsCollinear(p1: Point, p2: Point, p3: Point): boolean;

View File

@@ -0,0 +1,255 @@
import { SVGPathData } from './SVGPathData.js';
export function rotate([x, y], rad) {
return [
x * Math.cos(rad) - y * Math.sin(rad),
x * Math.sin(rad) + y * Math.cos(rad),
];
}
const DEBUG_CHECK_NUMBERS = true;
export function assertNumbers(...numbers) {
if (DEBUG_CHECK_NUMBERS) {
for (let i = 0; i < numbers.length; i++) {
if ('number' !== typeof numbers[i]) {
throw new Error(`assertNumbers arguments[${i}] is not a number. ${typeof numbers[i]} == typeof ${numbers[i]}`);
}
}
}
return true;
}
const PI = Math.PI;
/**
* https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
* Fixes rX and rY.
* Ensures lArcFlag and sweepFlag are 0 or 1
* Adds center coordinates: command.cX, command.cY (relative or absolute, depending on command.relative)
* Adds start and end arc parameters (in degrees): command.phi1, command.phi2; phi1 < phi2 iff. c.sweepFlag == true
*/
export function annotateArcCommand(c, x1, y1) {
c.lArcFlag = 0 === c.lArcFlag ? 0 : 1;
c.sweepFlag = 0 === c.sweepFlag ? 0 : 1;
// tslint:disable-next-line
let { rX, rY } = c;
const { x, y } = c;
if (Math.abs(rX) < 1e-10 || Math.abs(rY) < 1e-10) {
c.rX = 0;
c.rY = 0;
c.cX = (x1 + x) / 2;
c.cY = (y1 + y) / 2;
c.phi1 = 0;
c.phi2 = 0;
return;
}
rX = Math.abs(c.rX);
rY = Math.abs(c.rY);
const xRotRad = (c.xRot / 180) * PI;
const [x1_, y1_] = rotate([(x1 - x) / 2, (y1 - y) / 2], -xRotRad);
const testValue = Math.pow(x1_, 2) / Math.pow(rX, 2) + Math.pow(y1_, 2) / Math.pow(rY, 2);
if (1 < testValue) {
rX *= Math.sqrt(testValue);
rY *= Math.sqrt(testValue);
}
c.rX = rX;
c.rY = rY;
const c_ScaleTemp = Math.pow(rX, 2) * Math.pow(y1_, 2) + Math.pow(rY, 2) * Math.pow(x1_, 2);
const c_Scale = (c.lArcFlag !== c.sweepFlag ? 1 : -1) *
Math.sqrt(Math.max(0, (Math.pow(rX, 2) * Math.pow(rY, 2) - c_ScaleTemp) / c_ScaleTemp));
const cx_ = ((rX * y1_) / rY) * c_Scale;
const cy_ = ((-rY * x1_) / rX) * c_Scale;
const cRot = rotate([cx_, cy_], xRotRad);
c.cX = cRot[0] + (x1 + x) / 2;
c.cY = cRot[1] + (y1 + y) / 2;
c.phi1 = Math.atan2((y1_ - cy_) / rY, (x1_ - cx_) / rX);
c.phi2 = Math.atan2((-y1_ - cy_) / rY, (-x1_ - cx_) / rX);
if (0 === c.sweepFlag && c.phi2 > c.phi1) {
c.phi2 -= 2 * PI;
}
if (1 === c.sweepFlag && c.phi2 < c.phi1) {
c.phi2 += 2 * PI;
}
c.phi1 *= 180 / PI;
c.phi2 *= 180 / PI;
}
/**
* Solves a quadratic system of equations of the form
* a * x + b * y = c
* x² + y² = 1
* This can be understood as the intersection of the unit circle with a line.
* => y = (c - a x) / b
* => x² + (c - a x)² / b² = 1
* => x² b² + c² - 2 c a x + a² x² = b²
* => (a² + b²) x² - 2 a c x + (c² - b²) = 0
*/
export function intersectionUnitCircleLine(a, b, c) {
assertNumbers(a, b, c);
// cf. pqFormula
const termSqr = a * a + b * b - c * c;
if (0 > termSqr) {
return [];
}
else if (0 === termSqr) {
return [[(a * c) / (a * a + b * b), (b * c) / (a * a + b * b)]];
}
const term = Math.sqrt(termSqr);
return [
[
(a * c + b * term) / (a * a + b * b),
(b * c - a * term) / (a * a + b * b),
],
[
(a * c - b * term) / (a * a + b * b),
(b * c + a * term) / (a * a + b * b),
],
];
}
export const DEG = Math.PI / 180;
export function lerp(a, b, t) {
return (1 - t) * a + t * b;
}
export function arcAt(c, x1, x2, phiDeg) {
return (c + Math.cos((phiDeg / 180) * PI) * x1 + Math.sin((phiDeg / 180) * PI) * x2);
}
export function bezierRoot(x0, x1, x2, x3) {
const EPS = 1e-6;
// Coefficients for the derivative of a cubic Bezier curve
// B'(t) = 3(1-t)²(P₁-P₀) + 6(1-t)t(P₂-P₁) + 3t²(P₃-P₂)
// When rearranged to at² + bt + c:
const x01 = x1 - x0;
const x12 = x2 - x1;
const x23 = x3 - x2;
const a = 3 * x01 + 3 * x23 - 6 * x12;
const b = (x12 - x01) * 6;
const c = 3 * x01;
// solve a * t² + b * t + c = 0
if (Math.abs(a) < EPS) {
// For near-zero a, it becomes a linear equation: b * t + c = 0
return Math.abs(b) < EPS ? [] : [-c / b];
}
return pqFormula(b / a, c / a, EPS);
}
export function bezierAt(x0, x1, x2, x3, t) {
// Calculates a point on a cubic Bezier curve at parameter t.
// B(t) = (1-t)³P₀ + 3(1-t)²tP₁ + 3(1-t)t²P₂ + t³P₃
// Which is equivalent to:
// B(t) = (s³)P₀ + (3s²t)P₁ + (3st²)P₂ + (t³)P₃ where s = 1-t
const s = 1 - t;
const c0 = s * s * s;
const c1 = 3 * s * s * t;
const c2 = 3 * s * t * t;
const c3 = t * t * t;
return x0 * c0 + x1 * c1 + x2 * c2 + x3 * c3;
}
function pqFormula(p, q, PRECISION = 1e-6) {
// 4 times the discriminant:in
const discriminantX4 = (p * p) / 4 - q;
if (discriminantX4 < -PRECISION) {
return [];
}
else if (discriminantX4 <= PRECISION) {
return [-p / 2];
}
const root = Math.sqrt(discriminantX4);
return [-(p / 2) - root, -(p / 2) + root];
}
export function a2c(arc, x0, y0) {
if (!arc.cX) {
annotateArcCommand(arc, x0, y0);
}
// Convert xRot to radians
const xRotRad = (arc.xRot / 180) * PI;
// Handle zero radius case - convert to a straight line represented as a curve
if (Math.abs(arc.rX) < 1e-10 || Math.abs(arc.rY) < 1e-10) {
return [
{
relative: arc.relative,
type: SVGPathData.CURVE_TO,
x1: x0 + (arc.x - x0) / 3,
y1: y0 + (arc.y - y0) / 3,
x2: x0 + (2 * (arc.x - x0)) / 3,
y2: y0 + (2 * (arc.y - y0)) / 3,
x: arc.x,
y: arc.y,
},
];
}
const phiMin = Math.min(arc.phi1, arc.phi2), phiMax = Math.max(arc.phi1, arc.phi2), deltaPhi = phiMax - phiMin;
const partCount = Math.ceil(deltaPhi / 90);
const result = new Array(partCount);
let prevX = x0;
let prevY = y0;
const transform = (x, y) => {
const [xTemp, yTemp] = rotate([x * arc.rX, y * arc.rY], xRotRad);
return [arc.cX + xTemp, arc.cY + yTemp];
};
for (let i = 0; i < partCount; i++) {
const phiStart = lerp(arc.phi1, arc.phi2, i / partCount);
const phiEnd = lerp(arc.phi1, arc.phi2, (i + 1) / partCount);
const deltaPhi = phiEnd - phiStart;
const f = (4 / 3) * Math.tan((deltaPhi * DEG) / 4);
// x1/y1, x2/y2 and x/y coordinates on the unit circle for phiStart/phiEnd
const x1 = Math.cos(phiStart * DEG) - f * Math.sin(phiStart * DEG);
const y1 = Math.sin(phiStart * DEG) + f * Math.cos(phiStart * DEG);
const x = Math.cos(phiEnd * DEG);
const y = Math.sin(phiEnd * DEG);
const x2 = x + f * y;
const y2 = y - f * x;
const cp1 = transform(x1, y1);
const cp2 = transform(x2, y2);
const end = transform(x, y);
const command = {
relative: arc.relative,
type: SVGPathData.CURVE_TO,
x: end[0],
y: end[1],
x1: cp1[0],
y1: cp1[1],
x2: cp2[0],
y2: cp2[1],
};
if (arc.relative) {
command.x1 -= prevX;
command.y1 -= prevY;
command.x2 -= prevX;
command.y2 -= prevY;
command.x -= prevX;
command.y -= prevY;
}
prevX = end[0];
prevY = end[1];
result[i] = command;
}
return result;
}
/**
* Determines if three points are collinear (lie on the same straight line)
* and the middle point is on the line segment between the first and third points
*
* @param p1 First point [x, y]
* @param p2 Middle point that might be removed
* @param p3 Last point [x, y]
* @returns true if the points are collinear and p2 is on the segment p1-p3
*/
export function arePointsCollinear(p1, p2, p3) {
// Create vectors
const v1x = p2[0] - p1[0];
const v1y = p2[1] - p1[1];
const v2x = p3[0] - p1[0];
const v2y = p3[1] - p1[1];
// Cross product: v1 × v2 = v1x * v2y - v1y * v2x
// If cross product is close to zero, points are collinear
const cross = v1x * v2y - v1y * v2x;
const isCollinear = Math.abs(cross) < 1e-10;
if (!isCollinear)
return false;
// Now check if p2 is on the segment p1-p3
// For this we check if the projection of v1 onto v2 is between 0 and |v2|
// Calculate dot product
const dot = v1x * v2x + v1y * v2y;
// Calculate squared lengths
const lenSqV1 = v1x * v1x + v1y * v1y;
const lenSqV2 = v2x * v2x + v2y * v2y;
// p2 is on segment p1-p3 if:
// 1. 0 ≤ dot(v1,v2) ≤ dot(v2,v2) - this checks if projection is within segment
// 2. |v1| ≤ |v2| - this checks if p2 is not beyond p3
return 0 <= dot && dot <= lenSqV2 && lenSqV1 <= lenSqV2;
}
//# sourceMappingURL=mathUtils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,192 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing elliptical arc commands', () => {
test('should not work when badly declared', () => {
expect(() => new SVGPathData('A')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50 0')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50 0 0')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50 0 0 1')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50 0 0 1 162.55')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('A 30 50 0 0 1 A 30 50 0 0 1 162.55 162.45')).toThrow(new SyntaxError('Unterminated command at index 14.'));
});
test('should not work with bad rX value', () => {
expect(() => new SVGPathData('A-30,50,0,0,1,162.55,162.45')).toThrow(new SyntaxError('Expected positive number, got "-30" at index "4"'));
});
test('should not work with bad rY value', () => {
expect(() => new SVGPathData('A30,-50,0,0,1,162.55,162.45')).toThrow(new SyntaxError('Expected positive number, got "-50" at index "7"'));
});
test('should not work with bad lArcFlag value', () => {
expect(() => new SVGPathData('A30,50,0,15,1,162.55,162.45')).toThrow(new SyntaxError('Expected a flag, got "5" at index "11"'));
});
test('should not work with bad sweepFlag value', () => {
expect(() => new SVGPathData('A30,50,0,0,15,162.55,162.45')).toThrow(new SyntaxError('Unterminated command at the path end.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('A 30,50,0,0,1,162.55,162.45')
.commands;
expect(commands[0].type).toEqual(SVGPathData.ARC);
expect(commands[0].relative).toEqual(false);
expect(commands[0].rX).toEqual(30);
expect(commands[0].rY).toEqual(50);
expect(commands[0].xRot).toEqual(0);
expect(commands[0].lArcFlag).toEqual(0);
expect(commands[0].sweepFlag).toEqual(1);
expect(commands[0].x).toEqual(162.55);
expect(commands[0].y).toEqual(162.45);
});
test('should not work with a comma immediately after A', () => {
expect(() => new SVGPathData('A,30,50,0,0,1,162.55,162.45')).toThrow(new SyntaxError('Unexpected character "," at index 1. Command cannot follow comma'));
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('A 30 50 0 0 1 162.55 162.45')
.commands;
expect(commands[0].type).toEqual(SVGPathData.ARC);
expect(commands[0].relative).toEqual(false);
expect(commands[0].rX).toEqual(30);
expect(commands[0].rY).toEqual(50);
expect(commands[0].xRot).toEqual(0);
expect(commands[0].lArcFlag).toEqual(0);
expect(commands[0].sweepFlag).toEqual(1);
expect(commands[0].x).toEqual(162.55);
expect(commands[0].y).toEqual(162.45);
});
test('should work with nested separated complexer coordinate pairs', () => {
const commands = new SVGPathData('A 30,50 0 0 1 162.55,162.45')
.commands;
expect(commands[0].type).toEqual(SVGPathData.ARC);
expect(commands[0].relative).toEqual(false);
expect(commands[0].rX).toEqual(30);
expect(commands[0].rY).toEqual(50);
expect(commands[0].xRot).toEqual(0);
expect(commands[0].lArcFlag).toEqual(0);
expect(commands[0].sweepFlag).toEqual(1);
expect(commands[0].x).toEqual(162.55);
expect(commands[0].y).toEqual(162.45);
});
test('should work with multiple pairs of coordinates', () => {
const commands = new SVGPathData(`
A 10.0032e-5,20.0032e-5 0 0 1 -30.0032e-5,-40.0032e-5
50.0032e-5,60.0032e-5 0 1 0 -70.0032e-5,-80.0032e-5
90.0032e-5,90.0032e-5 0 0 1 -80.0032e-5,-70.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.ARC);
expect(commands[0].relative).toEqual(false);
expect(commands[0].rX).toEqual(10.0032e-5);
expect(commands[0].rY).toEqual(20.0032e-5);
expect(commands[0].xRot).toEqual(0);
expect(commands[0].lArcFlag).toEqual(0);
expect(commands[0].sweepFlag).toEqual(1);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.ARC);
expect(commands[1].relative).toEqual(false);
expect(commands[1].rX).toEqual(50.0032e-5);
expect(commands[1].rY).toEqual(60.0032e-5);
expect(commands[1].xRot).toEqual(0);
expect(commands[1].lArcFlag).toEqual(1);
expect(commands[1].sweepFlag).toEqual(0);
expect(commands[1].x).toEqual(-70.0032e-5);
expect(commands[1].y).toEqual(-80.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.ARC);
expect(commands[2].relative).toEqual(false);
expect(commands[2].rX).toEqual(90.0032e-5);
expect(commands[2].rY).toEqual(90.0032e-5);
expect(commands[2].xRot).toEqual(0);
expect(commands[2].lArcFlag).toEqual(0);
expect(commands[2].sweepFlag).toEqual(1);
expect(commands[2].x).toEqual(-80.0032e-5);
expect(commands[2].y).toEqual(-70.0032e-5);
});
test('should work with multiple declared pairs of coordinates', () => {
const commands = new SVGPathData(`
A 10.0032e-5,20.0032e-5 0 0 1 -30.0032e-5,-40.0032e-5
a50.0032e-5,60.0032e-5 0 1 0 -70.0032e-5,-80.0032e-5
A90.0032e-5,90.0032e-5 0 0 1 -80.0032e-5,-70.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.ARC);
expect(commands[0].relative).toEqual(false);
expect(commands[0].rX).toEqual(10.0032e-5);
expect(commands[0].rY).toEqual(20.0032e-5);
expect(commands[0].xRot).toEqual(0);
expect(commands[0].lArcFlag).toEqual(0);
expect(commands[0].sweepFlag).toEqual(1);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.ARC);
expect(commands[1].relative).toEqual(true);
expect(commands[1].rX).toEqual(50.0032e-5);
expect(commands[1].rY).toEqual(60.0032e-5);
expect(commands[1].xRot).toEqual(0);
expect(commands[1].lArcFlag).toEqual(1);
expect(commands[1].sweepFlag).toEqual(0);
expect(commands[1].x).toEqual(-70.0032e-5);
expect(commands[1].y).toEqual(-80.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.ARC);
expect(commands[2].relative).toEqual(false);
expect(commands[2].rX).toEqual(90.0032e-5);
expect(commands[2].rY).toEqual(90.0032e-5);
expect(commands[2].xRot).toEqual(0);
expect(commands[2].lArcFlag).toEqual(0);
expect(commands[2].sweepFlag).toEqual(1);
expect(commands[2].x).toEqual(-80.0032e-5);
expect(commands[2].y).toEqual(-70.0032e-5);
});
});
describe('Encoding eliptical arc commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('A30 50 0 0 1 162.55 162.45').encode()).toEqual('A30 50 0 0 1 162.55 162.45');
});
test('should work with several commands', () => {
expect(new SVGPathData('A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45').encode()).toEqual('A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45');
});
});
describe('Transforming elliptical arc commands', () => {
function assertDeepCloseTo(x, y, delta) {
if (typeof x === 'number' && typeof y === 'number') {
expect(x).toBeCloseTo(y, delta);
}
else if (typeof x === 'object' && typeof y === 'object') {
const keys = Object.getOwnPropertyNames(x);
expect(keys).toEqual(Object.getOwnPropertyNames(y));
for (let i = 0; i < keys.length; i++) {
assertDeepCloseTo(x[keys[i]], y[keys[i]], delta);
}
}
else if (x instanceof Array && y instanceof Array) {
expect(x.length).toEqual(y.length);
for (let i = 0; i < x.length; i++) {
assertDeepCloseTo(x[i], y[i], delta);
}
}
else {
expect(x).toEqual(y);
}
}
test('should rotate an axis-aligned arc', () => {
assertDeepCloseTo(new SVGPathData('M 0,0 A 100,50 0 0 1 100,50z').rotate(Math.PI / 6)
.commands, new SVGPathData('M 0,0 A 100,50 30 0 1 61.6,93.3z').commands, 0.1);
});
test('should rotate an arbitrary arc', () => {
assertDeepCloseTo(new SVGPathData('M 0,0 A 100,50 -15 0 1 100,0z').rotate(Math.PI / 4)
.commands, new SVGPathData('M 0,0 A 100,50 30 0 1 70.7,70.7z').commands, 0.1);
});
test('should skew by -45deg', () => {
function degToRad(deg) {
return deg * (Math.PI / 180);
}
assertDeepCloseTo(new SVGPathData('M 0,0 A 50,100 0 0 1 50,100z').skewX(degToRad(-45))
.commands, new SVGPathData('M 0,0 A 34.2,146.0 48.6 0 1 -50,100 Z').commands, 0.1);
});
test('should tolerate singular matrices', () => {
assertDeepCloseTo(new SVGPathData('M 0,0 A 80,80 0 0 1 50,100z').matrix(0.8, 2, 0.5, 1.25, 0, 0).commands, new SVGPathData('M 0,0 L 90,225 Z').commands, 0.1);
});
test('should match what Inkscape does on this random case', () => {
assertDeepCloseTo(new SVGPathData('M 170.19275,911.55263 A 61.42857,154.28572 21.033507 0 1 57.481868,1033.5109 61.42857,154.28572 21.033507 0 1 55.521508,867.4575 61.42857,154.28572 21.033507 0 1 168.2324,745.4993 A 61.42857,154.28572 21.033507 0 1 170.19275,911.55263 z').matrix(-0.10825745, -0.37157241, 0.77029181, 0.3345653, -560.10375, 633.84215).commands, new SVGPathData('M 123.63314,875.5771 A 135.65735,17.465974 30.334289 0 1 229.77839,958.26036 135.65735,17.465974 30.334289 0 1 102.08104,903.43307 135.65735,17.465974 30.334289 0 1 -4.0641555,820.74983 135.65735,17.465974 30.334289 0 1 123.63314,875.5771 z').commands, 0.0001);
});
test('should reflect the sweep flag any time the determinant is negative', () => {
assertDeepCloseTo(new SVGPathData('M 0,0 A 50,100 -30 1 1 80,80 Z').matrix(-1, 0, 0, 1, 0, 0).commands, new SVGPathData('M 0,0 A 50,100 30 1 0 -80,80 Z').commands, 0.1);
});
});
//# sourceMappingURL=arc.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,53 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
// Sample paths from MDN
// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
// Here we have to round output before testing since there is some lil
// differences across browsers.
describe('Converting elliptical arc commands to curves', () => {
test('should work sweepFlag on 0 and largeArcFlag on 0', () => {
const arc = new SVGPathData('M80 80 A 45 45, 0, 0, 0, 125 125').annotateArcs().commands[1];
expect(arc.rX).toEqual(45);
expect(arc.rY).toEqual(45);
expect(arc.cX).toEqual(125);
expect(arc.cY).toEqual(80);
expect(arc.phi1).toEqual(180);
expect(arc.phi2).toEqual(90);
});
test('should work sweepFlag on 1 and largeArcFlag on 0', () => {
const arc = new SVGPathData('M230 80 a 45 45, 0, 1, 0, 45 45').annotateArcs().commands[1];
expect(arc.relative).toEqual(true);
expect(arc.rX).toEqual(45);
expect(arc.rY).toEqual(45);
expect(arc.cX).toEqual(0);
expect(arc.cY).toEqual(45);
expect(arc.phi2 - arc.phi1).toEqual(-270);
expect((arc.phi1 + 360) % 360).toEqual(270);
});
test('should work sweepFlag on 0 and largeArcFlag on 1', () => {
const arc = new SVGPathData('M230 80 a 45 45, 0, 0, 1, 45 45').annotateArcs().commands[1];
expect(arc.relative).toEqual(true);
expect(arc.rX).toEqual(45);
expect(arc.rY).toEqual(45);
expect(arc.cX).toEqual(0);
expect(arc.cY).toEqual(45);
expect(arc.phi2 - arc.phi1).toEqual(90);
expect((arc.phi1 + 360) % 360).toEqual(270);
});
test('should work sweepFlag on 1 and largeArcFlag on 1', () => {
const arc = new SVGPathData('M110 215 A 30 50 0 1 1 162.55 162.45').annotateArcs().commands[1];
expect(arc.relative).toEqual(false);
expect(arc.rX).toEqual(30.64165220741206);
expect(arc.rY).toEqual(51.069420345686765);
expect(arc.cX).toEqual(136.275);
expect(arc.cY).toEqual(188.725);
expect(arc.phi1).toEqual(149.03624346792648);
expect(arc.phi2).toEqual(329.03624346792645);
});
test('should work sweepFlag on 0 and largeArcFlag on 1xx', () => {
const arc = new SVGPathData('M80 80 A 45 60, 0, 1, 1, 125 125').annotateArcs().commands[1];
expect(arc.relative).toEqual(false);
expect(arc.cX).toBeLessThan(125);
});
});
//# sourceMappingURL=arcannotate.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"arcannotate.test.js","sourceRoot":"","sources":["../../src/tests/arcannotate.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,96 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
// Sample pathes from MDN
// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
// Here we have to round output before testing since there is some lil
// differences across browsers.
function testArcToCurve(input) {
const data = new SVGPathData(input).aToC();
return data.round().encode();
}
describe('Converting elliptical arc commands to curves', () => {
test('should work sweepFlag on 0 and largeArcFlag on 0', () => {
// Absolute
expect(testArcToCurve('M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z')).toEqual('M80 80C80 104.8528137423857 100.1471862576143 125 125 125L125 80z');
// Relative
expect(testArcToCurve('M80 80 a 45 45, 0, 0, 0, 125 125 L 125 80 Z')).toEqual('M80 80c-34.5177968644246 34.5177968644246 -34.5177968644246 90.4822031355754 0 125c34.5177968644246 34.5177968644246 90.4822031355754 34.5177968644246 125 0L125 80z');
});
test('should work sweepFlag on 1 and largeArcFlag on 0', () => {
// Absolute
expect(testArcToCurve('M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z')).toEqual('M230 80C205.1471862576143 80 185 100.1471862576143 185 125C185 149.8528137423857 205.1471862576143 170 230 170C254.8528137423857 170 275 149.8528137423857 275 125L275 80z');
// Relative
expect(testArcToCurve('M230 80 a 45 45 0 1 0 45 45')).toEqual('M230 80c-24.8528137423857 0 -45 20.1471862576143 -45 45c0 24.8528137423857 20.1471862576143 45 45 45c24.8528137423857 0 45 -20.1471862576143 45 -45');
});
test('should work sweepFlag on 0 and largeArcFlag on 1', () => {
// Absolute
expect(testArcToCurve('M80 230 A 45 45, 0, 0, 1, 125 275 L 125 230 Z')).toEqual('M80 230C104.8528137423857 230 125 250.1471862576143 125 275L125 230z');
// Relative
expect(testArcToCurve('M80 230 a 45 45 0 0 1 45 45')).toEqual('M80 230c24.8528137423857 0 45 20.1471862576143 45 45');
});
test('should work sweepFlag on 1 and largeArcFlag on 1', () => {
// Absolute
expect(testArcToCurve('M230 230 A 45 45, 0, 1, 1, 275 275 L 275 230 Z')).toEqual('M230 230C230 205.1471862576143 250.1471862576143 185 275 185C299.8528137423857 185 320 205.1471862576143 320 230C320 254.8528137423857 299.8528137423857 275 275 275L275 230z');
// Relative
expect(testArcToCurve('M230 230 a 45 45 0 1 1 45 45')).toEqual('M230 230c0 -24.8528137423857 20.1471862576143 -45 45 -45c24.8528137423857 0 45 20.1471862576143 45 45c0 24.8528137423857 -20.1471862576143 45 -45 45');
});
test('should handle zero radius arcs by converting to lines', () => {
// Zero y-radius (absolute)
expect(testArcToCurve('M0 0A80 0 0 0 0 125 125')).toEqual('M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125');
// Zero x-radius (absolute)
expect(testArcToCurve('M0 0A0 80 0 0 0 125 125')).toEqual('M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125');
// Both x and y radius zero (absolute)
expect(testArcToCurve('M0 0A0 0 0 0 0 125 125')).toEqual('M0 0C41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125');
// Both x and y radius zero (relative)
expect(testArcToCurve('M0 0 a 0 80 0 0 0 125 125')).toEqual('M0 0c41.6666666666667 41.6666666666667 83.3333333333333 83.3333333333333 125 125');
});
test('should convert to correct arc', () => {
expect(testArcToCurve('M 30 30 A 30 30 90 0 0 30 80 Z')).toEqual('M30 30C21.6206442862417 35.5582357774914 16.583123951777 44.9447731434901 16.583123951777 55C16.583123951777 65.0552268565099 21.6206442862417 74.4417642225086 30 80z');
});
test('should correctly handle rotated arcs', () => {
// 45 degree rotation (absolute)
expect(testArcToCurve('M 50 50 A 30 15 45 0 1 100 100')).toEqual('M50 50C56.9035593728849 43.0964406271151 73.6928812542302 48.6928812542302 87.5 62.5C101.3071187457698 76.3071187457698 106.9035593728849 93.0964406271151 100 100');
// 90 degree rotation (absolute)
expect(testArcToCurve('M 30 30 A 30 15 90 0 1 80 80')).toEqual('M30 30C36.9035593728849 2.3857625084603 53.6928812542302 -8.8071187457698 67.5 5C81.3071187457698 18.8071187457698 86.9035593728849 52.3857625084603 80 80');
// 180 degree rotation (absolute, equivalent to flipping x and y radii)
expect(testArcToCurve('M 30 30 A 30 15 180 0 1 80 80')).toEqual('M30 30C57.6142374125551 23.0964408852183 91.1928806985313 28.6928815616988 104.999999309466 42.5000001726335C118.8071179204008 56.3071187835682 107.6142370311837 73.096440503847 80 80');
// 45 degree rotation (relative)
expect(testArcToCurve('M 50 50 a 30 15 45 0 1 50 50')).toEqual('M50 50c6.9035593728849 -6.9035593728849 23.6928812542302 -1.3071187457698 37.5 12.5c13.8071187457698 13.8071187457698 19.4035593728849 30.5964406271151 12.5 37.5');
// 90 degree rotation (relative)
expect(testArcToCurve('M 30 30 a 30 15 90 0 1 50 50')).toEqual('M30 30c6.9035593728849 -27.6142374915397 23.6928812542302 -38.8071187457698 37.5 -25c13.8071187457698 13.8071187457698 19.4035593728849 47.3857625084603 12.5 75');
});
test('should handle different radius combinations', () => {
// Very different radius values
expect(testArcToCurve('M 50 50 A 50 10 0 0 1 100 60')).toEqual('M50 50C77.6142374915397 50 100 54.4771525016921 100 60');
// Equal radius values (circle)
expect(testArcToCurve('M 50 50 A 30 30 0 0 1 100 80')).toEqual('M50 50C59.7206056797015 40.1416040718891 74.9250308199694 38.2155183741452 86.7968382872808 45.338602854532C98.6686457545922 52.4616873349189 104.1241819696502 66.7837498458583 100 80');
});
test('should handle full and nearly-full ellipses', () => {
// Nearly full ellipse with large-arc flag
expect(testArcToCurve('M 100 50 A 50 30 0 1 1 99 50')).toEqual('M100 50C127.5152793990511 50.1650999315973 149.636959976665 63.6387150592171 149.4993749804677 80.1485018375802C149.3617899842705 96.6582886159432 127.0166552662119 109.9984999624981 99.5 109.9984999624981C71.9833447337882 109.9984999624981 49.6382100157296 96.6582886159433 49.5006250195323 80.1485018375802C49.363040023335 63.6387150592171 71.4847206009489 50.1650999315973 99 50');
// Arc crossing coordinate axes
expect(testArcToCurve('M 0 0 A 50 50 0 0 1 100 0')).toEqual('M0 0C0 -27.6142374915397 22.3857625084603 -50 50 -50C77.6142374915397 -50 100 -27.6142374915397 100 0');
});
test('should correctly handle special cases with precise angles', () => {
// Vertical line with 90 degree rotation
expect(testArcToCurve('M 30 30 A 30 30 90 0 0 30 80')).toEqual('M30 30C21.6206442862417 35.5582357774914 16.583123951777 44.9447731434901 16.583123951777 55C16.583123951777 65.0552268565099 21.6206442862417 74.4417642225086 30 80');
// Same vertical line but in opposite direction (sweepFlag changed from 0 to 1)
expect(testArcToCurve('M 30 30 A 30 30 90 0 1 30 80')).toEqual('M30 30C38.3793557137583 35.5582357774914 43.416876048223 44.9447731434901 43.416876048223 55C43.416876048223 65.0552268565099 38.3793557137583 74.4417642225086 30 80');
// 90 degree arc segment
expect(testArcToCurve('M 50 50 A 30 30 0 0 1 80 80')).toEqual('M50 50C66.5685424949238 50 80 63.4314575050762 80 80');
});
test('should handle extreme values correctly', () => {
// Very small but non-zero radius
expect(testArcToCurve('M 50 50 A 0.1 0.1 0 0 1 51 51')).toEqual('M50 50C50.2761423749154 49.7238576250846 50.7238576250846 49.7238576250846 51 50C51.2761423749154 50.2761423749154 51.2761423749154 50.7238576250846 51 51');
// Very large radius
expect(testArcToCurve('M 0 0 A 1000 1000 0 0 1 10 10')).toEqual('M0 0C3.3568621862997 3.3097211449502 6.6902788550498 6.6431378137003 10 10');
});
test('should split arcs to mutiple curves', () => {
// Half circle (relative)
expect(testArcToCurve('M4.6 20 a 1.556 1.556 0 1 0 -2.2 -2.2z')).toEqual('M4.6 20c0.4073109040792 -0.3900354880021 0.5717037274325 -0.9699266164694 0.4296821520769 -1.5156918834261c-0.1420215753557 -0.5457652669567 -0.568225001694 -0.9719686932951 -1.1139902686508 -1.1139902686508c-0.5457652669567 -0.1420215753557 -1.125656395424 0.0223712479977 -1.5156918834261 0.4296821520769z');
// Almost full circle (absolute)
expect(testArcToCurve('M100 100 A50 50 0 1 1 101 100')).toEqual('M100 100C72.484720600949 99.7248334473379 50.363040023335 77.2688082346382 50.5006250195323 49.7524969373664C50.6382100157295 22.2361856400946 72.9833447337881 0.0025000625031 100.5 0.0025000625031C128.0166552662119 0.0025000625031 150.3617899842705 22.2361856400946 150.4993749804677 49.7524969373664C150.636959976665 77.2688082346382 128.5152793990511 99.7248334473379 101 100');
// Almost full circle (relative)
expect(testArcToCurve('M100 100 a50 50 0 1 1 1 0')).toEqual('M100 100c-27.515279399051 -0.2751665526621 -49.636959976665 -22.7311917653618 -49.4993749804677 -50.2475030626336c0.1375849961973 -27.5163112972718 22.4827197142558 -49.7499968748633 49.9993749804677 -49.7499968748633c27.5166552662119 0 49.8617899842705 22.2336855775915 49.9993749804677 49.7499968748633c0.1375849961973 27.5163112972718 -21.9840955814167 49.9723365099715 -49.4993749804677 50.2475030626336');
});
});
//# sourceMappingURL=arctocurve.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"arctocurve.test.js","sourceRoot":"","sources":["../../src/tests/arctocurve.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,yBAAyB;AACzB,kEAAkE;AAClE,sEAAsE;AACtE,+BAA+B;AAE/B,SAAS,cAAc,CAAC,KAAa;IACnC,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;AAC/B,CAAC;AAED,QAAQ,CAAC,8CAA8C,EAAE,GAAG,EAAE;IAC5D,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,WAAW;QACX,MAAM,CACJ,cAAc,CAAC,6CAA6C,CAAC,CAC9D,CAAC,OAAO,CACP,mEAAmE,CACpE,CAAC;QAEF,WAAW;QACX,MAAM,CACJ,cAAc,CAAC,6CAA6C,CAAC,CAC9D,CAAC,OAAO,CACP,sKAAsK,CACvK,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,WAAW;QACX,MAAM,CACJ,cAAc,CAAC,8CAA8C,CAAC,CAC/D,CAAC,OAAO,CACP,4KAA4K,CAC7K,CAAC;QAEF,WAAW;QACX,MAAM,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAC3D,qJAAqJ,CACtJ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,WAAW;QACX,MAAM,CACJ,cAAc,CAAC,+CAA+C,CAAC,CAChE,CAAC,OAAO,CACP,sEAAsE,CACvE,CAAC;QAEF,WAAW;QACX,MAAM,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAC3D,sDAAsD,CACvD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC5D,WAAW;QACX,MAAM,CACJ,cAAc,CAAC,gDAAgD,CAAC,CACjE,CAAC,OAAO,CACP,+KAA+K,CAChL,CAAC;QAEF,WAAW;QACX,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,sJAAsJ,CACvJ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uDAAuD,EAAE,GAAG,EAAE;QACjE,2BAA2B;QAC3B,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,OAAO,CACvD,kFAAkF,CACnF,CAAC;QAEF,2BAA2B;QAC3B,MAAM,CAAC,cAAc,CAAC,yBAAyB,CAAC,CAAC,CAAC,OAAO,CACvD,kFAAkF,CACnF,CAAC;QAEF,sCAAsC;QACtC,MAAM,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC,CAAC,OAAO,CACtD,kFAAkF,CACnF,CAAC;QAEF,sCAAsC;QACtC,MAAM,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CACzD,kFAAkF,CACnF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACzC,MAAM,CAAC,cAAc,CAAC,gCAAgC,CAAC,CAAC,CAAC,OAAO,CAC9D,wKAAwK,CACzK,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAChD,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,gCAAgC,CAAC,CAAC,CAAC,OAAO,CAC9D,oKAAoK,CACrK,CAAC;QAEF,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,4JAA4J,CAC7J,CAAC;QAEF,uEAAuE;QACvE,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAC7D,yLAAyL,CAC1L,CAAC;QAEF,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,mKAAmK,CACpK,CAAC;QAEF,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,kKAAkK,CACnK,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,+BAA+B;QAC/B,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,wDAAwD,CACzD,CAAC;QAEF,+BAA+B;QAC/B,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,yLAAyL,CAC1L,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,0CAA0C;QAC1C,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,+XAA+X,CAChY,CAAC;QAEF,+BAA+B;QAC/B,MAAM,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CACzD,uGAAuG,CACxG,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACrE,wCAAwC;QACxC,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,uKAAuK,CACxK,CAAC;QAEF,+EAA+E;QAC/E,MAAM,CAAC,cAAc,CAAC,8BAA8B,CAAC,CAAC,CAAC,OAAO,CAC5D,uKAAuK,CACxK,CAAC;QAEF,wBAAwB;QACxB,MAAM,CAAC,cAAc,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAC3D,sDAAsD,CACvD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,iCAAiC;QACjC,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAC7D,4JAA4J,CAC7J,CAAC;QAEF,oBAAoB;QACpB,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAC7D,4EAA4E,CAC7E,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC/C,yBAAyB;QACzB,MAAM,CAAC,cAAc,CAAC,wCAAwC,CAAC,CAAC,CAAC,OAAO,CACtE,qTAAqT,CACtT,CAAC;QAEF,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC,CAAC,OAAO,CAC7D,4XAA4X,CAC7X,CAAC;QAEF,gCAAgC;QAChC,MAAM,CAAC,cAAc,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CACzD,yZAAyZ,CAC1Z,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,36 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
function testBounds(svgString, minX, minY, maxX, maxY) {
const path = new SVGPathData(svgString);
const bounds = path.getBounds();
expect(bounds.minX).toBeCloseTo(minX, 1e-4);
expect(bounds.minY).toBeCloseTo(minY, 1e-4);
expect(bounds.maxX).toBeCloseTo(maxX, 1e-4);
expect(bounds.maxY).toBeCloseTo(maxY, 1e-4);
}
describe('Calculating bounds', () => {
test('should work with L', () => {
testBounds('M10 10 L 20 30', 10, 10, 20, 30);
});
test('should work with A', () => {
testBounds('M80 80 A 45 45, 0, 0, 1, 125 125', 80, 80, 125, 125);
testBounds('M80 80 A 45 60, 20, 0, 0, 125 125', 80, 80, 125, 125.59961858400797);
testBounds('M80 80 A 45 60, 20, 0, 1, 125 125', 80, 79.40038141599203, 125, 125);
testBounds('M80 80 A 45 60, 20, 1, 0, 125 125', 31.02578911052131, 80, 125.03089537701737, 196.28938858015945);
testBounds('M80 80 A 45 60, -20, 1, 1, 120 120', 74.66880591967042, 4.531089272703177, 168.6739121861665, 121.4200964368706);
});
test('should work with C', () => {
testBounds('M20 10 C 0 40, 80 100, 100 40', 16.81700272781236, 10, 100, 65.67265229607777);
});
test('should work with a combined example', () => {
testBounds('M100,100L150,100a50,25 0 0,0 150,100q100,-50 70,-170Z', 100, 30, 376.9230769230769, 212.5);
});
test("shouldn't change the original commands", () => {
const path = new SVGPathData('M100,100L150,100a50,25 0 0,0 150,100q100,-50 70,-170Z');
const originalPath = path.encode();
path.getBounds();
const afterPath = path.encode();
expect(afterPath).toEqual(originalPath);
});
});
//# sourceMappingURL=bounds.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"bounds.test.js","sourceRoot":"","sources":["../../src/tests/bounds.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,SAAS,UAAU,CACjB,SAAiB,EACjB,IAAY,EACZ,IAAY,EACZ,IAAY,EACZ,IAAY;IAEZ,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAEhC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC9B,UAAU,CAAC,gBAAgB,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC9B,UAAU,CAAC,kCAAkC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACjE,UAAU,CACR,mCAAmC,EACnC,EAAE,EACF,EAAE,EACF,GAAG,EACH,kBAAkB,CACnB,CAAC;QACF,UAAU,CACR,mCAAmC,EACnC,EAAE,EACF,iBAAiB,EACjB,GAAG,EACH,GAAG,CACJ,CAAC;QACF,UAAU,CACR,mCAAmC,EACnC,iBAAiB,EACjB,EAAE,EACF,kBAAkB,EAClB,kBAAkB,CACnB,CAAC;QACF,UAAU,CACR,oCAAoC,EACpC,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,CAClB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC9B,UAAU,CACR,+BAA+B,EAC/B,iBAAiB,EACjB,EAAE,EACF,GAAG,EACH,iBAAiB,CAClB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC/C,UAAU,CACR,uDAAuD,EACvD,GAAG,EACH,EAAE,EACF,iBAAiB,EACjB,KAAK,CACN,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,MAAM,IAAI,GAAG,IAAI,WAAW,CAC1B,uDAAuD,CACxD,CAAC;QACF,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAEnC,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAEhC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,37 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing close path commands', () => {
test('should work', () => {
const commands = new SVGPathData('Z').commands;
expect(commands[0].type).toEqual(SVGPathData.CLOSE_PATH);
});
test('should work with spaces before', () => {
const commands = new SVGPathData(' Z').commands;
expect(commands[0].type).toEqual(SVGPathData.CLOSE_PATH);
});
test('should work with spaces after', () => {
const commands = new SVGPathData('Z ').commands;
expect(commands[0].type).toEqual(SVGPathData.CLOSE_PATH);
});
test('should work before a command sequence', () => {
const commands = new SVGPathData(' Z M10,10 L10,10 H10 V10').commands;
expect(commands[0].type).toEqual(SVGPathData.CLOSE_PATH);
});
test('should work after a command sequence', () => {
const commands = new SVGPathData('M10,10 L10,10 H10 V10 Z').commands;
expect(commands[4].type).toEqual(SVGPathData.CLOSE_PATH);
});
test('should work in a command sequence', () => {
const commands = new SVGPathData('M10,10 L10,10 H10 V10 Z M10,10 L10,10 H10 V10').commands;
expect(commands[4].type).toEqual(SVGPathData.CLOSE_PATH);
});
});
describe('Encoding close path commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('z').encode()).toEqual('z');
});
test('should work with several commands', () => {
expect(new SVGPathData('zzzz').encode()).toEqual('zzzz');
});
});
//# sourceMappingURL=closepath.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"closepath.test.js","sourceRoot":"","sources":["../../src/tests/closepath.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE;QACvB,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QAE/C,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC1C,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;QAElD,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACzC,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;QAEnD,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACjD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,0BAA0B,CAAC,CAAC,QAAQ,CAAC;QAEtE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAChD,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC;QAErE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,MAAM,QAAQ,GAAG,IAAI,WAAW,CAC9B,+CAA+C,CAChD,CAAC,QAAQ,CAAC;QAEX,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,118 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing curve to commands', () => {
test('should not work when badly declared', () => {
expect(() => new SVGPathData('C')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10 10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10 10 10 10 10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('C10 10 10C10 10 10 10 10 10')).toThrow(new SyntaxError('Unterminated command at index 9.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('C123,456 789,987 654,321').commands;
expect(commands[0].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(123);
expect(commands[0].y1).toEqual(456);
expect(commands[0].x2).toEqual(789);
expect(commands[0].y2).toEqual(987);
expect(commands[0].x).toEqual(654);
expect(commands[0].y).toEqual(321);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('C123 456 789 987 654 321').commands;
expect(commands[0].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(123);
expect(commands[0].y1).toEqual(456);
expect(commands[0].x2).toEqual(789);
expect(commands[0].y2).toEqual(987);
expect(commands[0].x).toEqual(654);
expect(commands[0].y).toEqual(321);
});
test('should work with nested separated complexer coordinate pairs', () => {
const commands = new SVGPathData('C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x2).toEqual(-30.0032e-5);
expect(commands[0].y2).toEqual(-40.0032e-5);
expect(commands[0].x).toEqual(-50.0032e-5);
expect(commands[0].y).toEqual(-60.0032e-5);
});
test('should work with multiple pairs of coordinates', () => {
const commands = new SVGPathData(`
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x2).toEqual(-30.0032e-5);
expect(commands[0].y2).toEqual(-40.0032e-5);
expect(commands[0].x).toEqual(-50.0032e-5);
expect(commands[0].y).toEqual(-60.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x1).toEqual(-10.0032e-5);
expect(commands[1].y1).toEqual(-20.0032e-5);
expect(commands[1].x2).toEqual(-30.0032e-5);
expect(commands[1].y2).toEqual(-40.0032e-5);
expect(commands[1].x).toEqual(-50.0032e-5);
expect(commands[1].y).toEqual(-60.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x1).toEqual(-10.0032e-5);
expect(commands[2].y1).toEqual(-20.0032e-5);
expect(commands[2].x2).toEqual(-30.0032e-5);
expect(commands[2].y2).toEqual(-40.0032e-5);
expect(commands[2].x).toEqual(-50.0032e-5);
expect(commands[2].y).toEqual(-60.0032e-5);
});
test('should work with multiple declared pairs of coordinates', () => {
const commands = new SVGPathData(`
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
c-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x2).toEqual(-30.0032e-5);
expect(commands[0].y2).toEqual(-40.0032e-5);
expect(commands[0].x).toEqual(-50.0032e-5);
expect(commands[0].y).toEqual(-60.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[1].relative).toEqual(true);
expect(commands[1].x1).toEqual(-10.0032e-5);
expect(commands[1].y1).toEqual(-20.0032e-5);
expect(commands[1].x2).toEqual(-30.0032e-5);
expect(commands[1].y2).toEqual(-40.0032e-5);
expect(commands[1].x).toEqual(-50.0032e-5);
expect(commands[1].y).toEqual(-60.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.CURVE_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x1).toEqual(-10.0032e-5);
expect(commands[2].y1).toEqual(-20.0032e-5);
expect(commands[2].x2).toEqual(-30.0032e-5);
expect(commands[2].y2).toEqual(-40.0032e-5);
expect(commands[2].x).toEqual(-50.0032e-5);
expect(commands[2].y).toEqual(-60.0032e-5);
});
});
describe('Encoding curve to commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5').encode()).toEqual('C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032');
});
test('should work with several commands', () => {
expect(new SVGPathData('C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5').encode()).toEqual('C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032');
});
});
//# sourceMappingURL=curveto.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,15 @@
import { describe, test, expect } from '@jest/globals';
import { encodeSVGPath } from '../index.js';
describe('SVGPathDataEncoder', () => {
test('should not work when the command is forgotten', () => {
expect(() => encodeSVGPath(undefined)).toThrow(new TypeError("Cannot read properties of undefined (reading 'type')"));
});
test('should fail when a bad command is given', () => {
expect(() => encodeSVGPath({
type: 'plop',
x: 0,
y: 0,
})).toThrow(new Error('Unexpected command type "plop" at index 0.'));
});
});
//# sourceMappingURL=encoder.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"encoder.test.js","sourceRoot":"","sources":["../../src/tests/encoder.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,SAAkC,CAAC,CAAC,CAAC,OAAO,CACrE,IAAI,SAAS,CAAC,sDAAsD,CAAC,CACtE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CACV,aAAa,CAAC;YACZ,IAAI,EAAE,MAAM;YACZ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;SACoB,CAAC,CAC5B,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,203 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing horizontal commands', () => {
test('should work with single coordinate', () => {
const commands = new SVGPathData('H100').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(100);
});
test('should work with single complexer coordinate', () => {
const commands = new SVGPathData('H-10e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(-10e-5);
});
test('should work with single even more complexer coordinate', () => {
const commands = new SVGPathData('H-10.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(-10.0032e-5);
});
test('should work with single relative coordinate', () => {
const commands = new SVGPathData('h100').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(100);
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('H123,456,7890,9876').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].x).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[1].x).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[2].x).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[3].x).toEqual(9876);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('H123 456 7890 9876').commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].x).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[1].x).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[2].x).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[3].x).toEqual(9876);
});
test('should work with nested separated coordinates', () => {
const commands = new SVGPathData('H123 , 456 \t,\n7890 \r\n 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].x).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[1].x).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[2].x).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[3].x).toEqual(9876);
});
test('should work with multiple command declarations', () => {
const commands = new SVGPathData(`
H123 , 456 \t,\n7890 \r\n 9876H123 ,
456 \t,\n7890 \r\n 9876
`).commands;
expect(commands[0].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[0].x).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[1].x).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[2].x).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[3].x).toEqual(9876);
expect(commands[4].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[4].x).toEqual(123);
expect(commands[5].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[5].x).toEqual(456);
expect(commands[6].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[6].x).toEqual(7890);
expect(commands[7].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[7].x).toEqual(9876);
});
});
describe('Parsing vertical commands', () => {
test('should work with single coordinate', () => {
const commands = new SVGPathData('V100').commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].y).toEqual(100);
});
test('should work with single complexer coordinate', () => {
const commands = new SVGPathData('V-10e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].y).toEqual(-10e-5);
});
test('should work with single even more complexer coordinate', () => {
const commands = new SVGPathData('V-10.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].y).toEqual(-10.0032e-5);
});
test('should work with single relative coordinate', () => {
const commands = new SVGPathData('v100').commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].y).toEqual(100);
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('V123,456,7890,9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].y).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[1].y).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[2].y).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[3].y).toEqual(9876);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('V123 456 7890 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].y).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[1].y).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[2].y).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[3].y).toEqual(9876);
});
test('should work with nested separated coordinates', () => {
const commands = new SVGPathData('V123 , 456 \t,\n7890 \r\n 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].y).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[1].y).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[2].y).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[3].y).toEqual(9876);
});
test('should work with multiple command declarations', () => {
const commands = new SVGPathData(`
V123 , 456 \t,\n7890 \r\n
9876V123 , 456 \t,\n7890 \r\n 9876
`).commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].y).toEqual(123);
expect(commands[1].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[1].y).toEqual(456);
expect(commands[2].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[2].y).toEqual(7890);
expect(commands[3].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[3].y).toEqual(9876);
expect(commands[4].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[4].y).toEqual(123);
expect(commands[5].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[5].y).toEqual(456);
expect(commands[6].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[6].y).toEqual(7890);
expect(commands[7].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[7].y).toEqual(9876);
});
});
describe('Parsing nested vertical/horizontal commands', () => {
test('should work', () => {
const commands = new SVGPathData('V100H100v0.12h0.12V100h100v-10e-5 H-10e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].y).toEqual(100);
expect(commands[1].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(100);
expect(commands[2].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[2].relative).toEqual(true);
expect(commands[2].y).toEqual(0.12);
expect(commands[3].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[3].relative).toEqual(true);
expect(commands[3].x).toEqual(0.12);
expect(commands[4].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[4].relative).toEqual(false);
expect(commands[4].y).toEqual(100);
expect(commands[5].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[5].relative).toEqual(true);
expect(commands[5].x).toEqual(100);
expect(commands[6].type).toEqual(SVGPathData.VERT_LINE_TO);
expect(commands[6].relative).toEqual(true);
expect(commands[6].y).toEqual(-10e-5);
expect(commands[7].type).toEqual(SVGPathData.HORIZ_LINE_TO);
expect(commands[7].relative).toEqual(false);
expect(commands[7].x).toEqual(-10e-5);
});
});
describe('Encoding nested vertical/horizontal commands', () => {
test('should work', () => {
expect(new SVGPathData('V100H100v0.12h0.12V100h100v-10e-5H-10e-5').encode()).toEqual('V100H100v0.12h0.12V100h100v-0.0001H-0.0001');
});
});
//# sourceMappingURL=hv.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,109 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing line to commands', () => {
test('should not work with single coordinate', () => {
expect(() => new SVGPathData('L100')).toThrow(new SyntaxError('Unterminated command at the path end.'));
});
test('should not work with single complexer coordinate', () => {
expect(() => new SVGPathData('l-10e-5')).toThrow(new SyntaxError('Unterminated command at the path end.'));
});
test('should work with single coordinate followed by another', () => {
expect(() => new SVGPathData('l-10l10 10')).toThrow(new SyntaxError('Unterminated command at index 4.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('L100,100').commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(100);
expect(commands[0].y).toEqual(100);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('l100 \t 100').commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(100);
expect(commands[0].y).toEqual(100);
});
test('should work with complexer coordinates', () => {
const commands = new SVGPathData('l-10e-5 -10e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(-10e-5);
expect(commands[0].y).toEqual(-10e-5);
});
test('should work with single even more complexer coordinates', () => {
const commands = new SVGPathData('L-10.0032e-5 -10.0032e-5')
.commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(-10.0032e-5);
expect(commands[0].y).toEqual(-10.0032e-5);
});
test('should work with comma separated coordinate pairs', () => {
const commands = new SVGPathData('L123,456 7890,9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with space separated coordinate pairs', () => {
const commands = new SVGPathData('l123 \t 456 \n 7890 \r 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(true);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with nested separated coordinates', () => {
const commands = new SVGPathData('L123 , 456 \t,\n7890 \r\n 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with multiple command declarations', () => {
const commands = new SVGPathData(`
L123 , 456 \t,\n7890 \r\n 9876l123 ,
456 \t,\n7890 \r\n 9876
`).commands;
expect(commands[0].type).toEqual(SVGPathData.LINE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
expect(commands[2].type).toEqual(SVGPathData.LINE_TO);
expect(commands[2].relative).toEqual(true);
expect(commands[2].x).toEqual(123);
expect(commands[2].y).toEqual(456);
expect(commands[3].type).toEqual(SVGPathData.LINE_TO);
expect(commands[3].relative).toEqual(true);
expect(commands[3].x).toEqual(7890);
expect(commands[3].y).toEqual(9876);
});
});
describe('Encoding line to commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('L-0.000500032 -0.000600032').encode()).toEqual('L-0.000500032 -0.000600032');
});
test('should work with several commands', () => {
expect(new SVGPathData('L-50.0032e-5 -60.0032e-5L-50.0032e-5 -60.0032e-5L-50.0032e-5 -60.0032e-5').encode()).toEqual('L-0.000500032 -0.000600032L-0.000500032 -0.000600032L-0.000500032 -0.000600032');
});
});
//# sourceMappingURL=lineto.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,30 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe("Matrix transformation should be the same than it's equivalent transformation", () => {
test('should fail with bad args', () => {
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[])
.encode()).toThrow(new Error('assertNumbers arguments[0] is not a number. undefined == typeof undefined'));
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[1])
.encode()).toThrow(new Error('assertNumbers arguments[1] is not a number. undefined == typeof undefined'));
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[1, 1])
.encode()).toThrow(new Error('assertNumbers arguments[2] is not a number. undefined == typeof undefined'));
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[1, 1, 1])
.encode()).toThrow(new Error('assertNumbers arguments[3] is not a number. undefined == typeof undefined'));
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[1, 1, 1, 1])
.encode()).toThrow(new Error('assertNumbers arguments[4] is not a number. undefined == typeof undefined'));
expect(() => new SVGPathData('m20,30l10,10z')
.matrix(...[1, 1, 1, 1, 1])
.encode()).toThrow(new Error('assertNumbers arguments[5] is not a number. undefined == typeof undefined'));
});
test('for scale', () => {
expect(new SVGPathData('m20 30c0 0 10 20 15 30z').scale(10, 10).encode()).toEqual(new SVGPathData('m20 30c0 0 10 20 15 30z')
.matrix(10, 0, 0, 10, 0, 0)
.encode());
});
});
//# sourceMappingURL=matrix.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"matrix.test.js","sourceRoot":"","sources":["../../src/tests/matrix.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,8EAA8E,EAAE,GAAG,EAAE;IAC5F,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CAAC,GAAI,EAAmD,CAAC;aAC/D,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;QAEF,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CAAC,GAAI,CAAC,CAAC,CAAkD,CAAC;aAChE,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;QAEF,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CAAC,GAAI,CAAC,CAAC,EAAE,CAAC,CAAkD,CAAC;aACnE,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;QAEF,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CAAC,GAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAkD,CAAC;aACtE,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;QAEF,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CACL,GAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAkD,CAClE;aACA,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;QAEF,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,MAAM,CACL,GAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAkD,CACrE;aACA,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;QACrB,MAAM,CACJ,IAAI,WAAW,CAAC,yBAAyB,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAClE,CAAC,OAAO,CACP,IAAI,WAAW,CAAC,yBAAyB,CAAC;aACvC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;aAC1B,MAAM,EAAE,CACZ,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,109 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing move to commands', () => {
test('should not work with single coordinate', () => {
expect(() => new SVGPathData('M100')).toThrow(new SyntaxError('Unterminated command at the path end.'));
});
test('should not work with single complexer coordinate', () => {
expect(() => new SVGPathData('m-10e-5')).toThrow(new SyntaxError('Unterminated command at the path end.'));
});
test('should work with single coordinate followed by another', () => {
expect(() => new SVGPathData('m-10m10 10')).toThrow(new SyntaxError('Unterminated command at index 4.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('M100,100').commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(100);
expect(commands[0].y).toEqual(100);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('m100 \t 100').commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(100);
expect(commands[0].y).toEqual(100);
});
test('should work with complexer coordinates', () => {
const commands = new SVGPathData('m-10e-5 -10e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(-10e-5);
expect(commands[0].y).toEqual(-10e-5);
});
test('should work with even more complexer coordinates', () => {
const commands = new SVGPathData('M-10.0032e-5 -10.0032e-5')
.commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(-10.0032e-5);
expect(commands[0].y).toEqual(-10.0032e-5);
});
test('should work with comma separated coordinate pairs', () => {
const commands = new SVGPathData('M123,456 7890,9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with space separated coordinate pairs', () => {
const commands = new SVGPathData('m123 \t 456 \n 7890 \r 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(true);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(true);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with nested separated coordinates', () => {
const commands = new SVGPathData('M123 , 456 \t,\n7890 \r\n 9876')
.commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
});
test('should work with multiple command declarations', () => {
const commands = new SVGPathData(`
M123 , 456 \t,\n7890 \r\n 9876m123
, 456 \t,\n7890 \r\n 9876
`).commands;
expect(commands[0].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x).toEqual(123);
expect(commands[0].y).toEqual(456);
expect(commands[1].type).toEqual(SVGPathData.LINE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x).toEqual(7890);
expect(commands[1].y).toEqual(9876);
expect(commands[2].type).toEqual(SVGPathData.MOVE_TO);
expect(commands[2].relative).toEqual(true);
expect(commands[2].x).toEqual(123);
expect(commands[2].y).toEqual(456);
expect(commands[3].type).toEqual(SVGPathData.LINE_TO);
expect(commands[3].relative).toEqual(true);
expect(commands[3].x).toEqual(7890);
expect(commands[3].y).toEqual(9876);
});
});
describe('Encoding move to commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('M-50.0032e-5 -60.0032e-5').encode()).toEqual('M-0.000500032 -0.000600032');
});
test('should work with several commands', () => {
expect(new SVGPathData('M-50.0032e-5 -60.0032e-5M-50.0032e-5 -60.0032e-5M-50.0032e-5 -60.0032e-5').encode()).toEqual('M-0.000500032 -0.000600032M-0.000500032 -0.000600032M-0.000500032 -0.000600032');
});
});
//# sourceMappingURL=moveto.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,20 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('normalization of curves', () => {
test("should ignore everything which isn't S s T t", () => {
expect(new SVGPathData('m20,30c0 0 10 20 15 30q10 20 15 30h10v10a10 10 5 1 0 10 10z')
.normalizeST()
.encode()).toEqual(new SVGPathData('m20,30c0 0 10 20 15 30q10 20 15 30h10v10a10 10 5 1 0 10 10z').encode());
});
test("should take the previous point as the curve parameter if the previous curve isn't of the same type", () => {
expect(new SVGPathData('M 10 10 h 100 s 10 20 15 30 t 20 15')
.normalizeST()
.encode()).toEqual(new SVGPathData('M 10 10 h 100 c 0 0 10 20 15 30 q 0 0 20 15').encode());
});
test('should mirror the previous control point', () => {
expect(new SVGPathData('M 10 10 s 10 20 15 30 S 90 80 100 100')
.normalizeST()
.encode()).toEqual(new SVGPathData('M 10 10 c 0 0 10 20 15 30 C 30 50 90 80 100 100').encode());
});
});
//# sourceMappingURL=normalize_curves.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"normalize_curves.test.js","sourceRoot":"","sources":["../../src/tests/normalize_curves.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACxD,MAAM,CACJ,IAAI,WAAW,CACb,6DAA6D,CAC9D;aACE,WAAW,EAAE;aACb,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CACb,6DAA6D,CAC9D,CAAC,MAAM,EAAE,CACX,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oGAAoG,EAAE,GAAG,EAAE;QAC9G,MAAM,CACJ,IAAI,WAAW,CAAC,qCAAqC,CAAC;aACnD,WAAW,EAAE;aACb,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CAAC,6CAA6C,CAAC,CAAC,MAAM,EAAE,CACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACpD,MAAM,CACJ,IAAI,WAAW,CAAC,uCAAuC,CAAC;aACrD,WAAW,EAAE;aACb,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CACb,iDAAiD,CAClD,CAAC,MAAM,EAAE,CACX,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,60 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
function testNormalizeHVZ(input, expected) {
expect(new SVGPathData(input).normalizeHVZ().encode()).toEqual(new SVGPathData(expected).encode());
}
describe('HVZA normalization', () => {
// currently z/Z is always absolute
test('should transform relative h v z', () => {
testNormalizeHVZ('m 10 10 h 100 v 100 z', 'm 10 10 l 100 0 l 0 100 L 10 10');
});
test('should transform absolute h v z', () => {
testNormalizeHVZ('M 10 10 H 100 V 100 Z', 'M 10 10 L 100 10 L 100 100 L 10 10');
});
test('should transform degenerate arcs', () => {
testNormalizeHVZ('M 10 10 A 0 10 0 0 0 100 100 a 20 0 0 0 0 20 0', 'M 10 10 L 100 100 l 20 0');
});
test('should transform bezier curves that are lines', () => {
// Cubic bezier with all control points on the same line
// M10,10 C40,20 70,30 100,40 (start at 10,10, end at 100,40, control points at 40,20 and 70,30)
// This is a straight line because all points are collinear
testNormalizeHVZ('M 10 10 C 40 20 70 30 100 40', 'M 10 10 L 100 40');
// Same test with relative coordinates
testNormalizeHVZ('M 10 10 c 30 10 60 20 90 30', 'M 10 10 l 90 30');
// Simple horizontal line
testNormalizeHVZ('M 10 10 C 40 10 70 10 100 10', 'M 10 10 L 100 10');
// Simple vertical line
testNormalizeHVZ('M 10 10 C 10 40 10 70 10 100', 'M 10 10 L 10 100');
// Control points on line but outside the segment - should NOT be linearized
testNormalizeHVZ('M 50 50 C 0 0 150 150 100 100', 'M 50 50 C 0 0 150 150 100 100');
// Control points not on line - should not be linearized
testNormalizeHVZ('M 10 10 C 40 60 70 -10 100 40', 'M 10 10 C 40 60 70 -10 100 40');
// One control point on line, the other not - should not be linearized
testNormalizeHVZ('M 10 10 C 40 20 70 60 100 40', 'M 10 10 C 40 20 70 60 100 40');
});
test('should transform quad curves that are lines', () => {
// Quadratic bezier with control point on the same line
// M10,10 Q55,25 100,40 (start at 10,10, end at 100,40, control point at 55,25)
// This is a straight line because all points are collinear
testNormalizeHVZ('M 10 10 Q 55 25 100 40', 'M 10 10 L 100 40');
// Same test with relative coordinates
testNormalizeHVZ('M 10 10 q 45 15 90 30', 'M 10 10 l 90 30');
// Simple horizontal line
testNormalizeHVZ('M 10 10 Q 55 10 100 10', 'M 10 10 L 100 10');
// Simple vertical line
testNormalizeHVZ('M 10 10 Q 10 55 10 100', 'M 10 10 L 10 100');
// Control point on line but outside the segment - should NOT be linearized
testNormalizeHVZ('M 50 50 Q 150 150 100 100', 'M 50 50 Q 150 150 100 100');
// Control point not on line - should not be linearized
testNormalizeHVZ('M 10 10 Q 55 60 100 40', 'M 10 10 Q 55 60 100 40');
});
test('should properly handle edge cases for curve normalization', () => {
// Closed shape with multiple curve segments
testNormalizeHVZ('M 10 10 C 20 10 30 10 40 10 C 40 20 40 30 40 40 C 30 40 20 40 10 40 C 10 30 10 20 10 10 Z', 'M 10 10 L 40 10 L 40 40 L 10 40L10 10 L 10 10');
// Nearly collinear but with minor deviation - should not be linearized
testNormalizeHVZ('M 10 10 C 40 20.001 70 30.001 100 40', 'M 10 10 C 40 20.001 70 30.001 100 40');
// Zero-length segment with collinear control points - should be linearized
testNormalizeHVZ('M 50 50 C 50 50 50 50 50 50', 'M 50 50 L 50 50');
});
});
//# sourceMappingURL=normalize_hvz.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"normalize_hvz.test.js","sourceRoot":"","sources":["../../src/tests/normalize_hvz.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,SAAS,gBAAgB,CAAC,KAAK,EAAE,QAAQ;IACvC,MAAM,CAAC,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAC5D,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,mCAAmC;IACnC,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC3C,gBAAgB,CACd,uBAAuB,EACvB,iCAAiC,CAClC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC3C,gBAAgB,CACd,uBAAuB,EACvB,oCAAoC,CACrC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC5C,gBAAgB,CACd,gDAAgD,EAChD,0BAA0B,CAC3B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACzD,wDAAwD;QACxD,gGAAgG;QAChG,2DAA2D;QAC3D,gBAAgB,CAAC,8BAA8B,EAAE,kBAAkB,CAAC,CAAC;QAErE,sCAAsC;QACtC,gBAAgB,CAAC,6BAA6B,EAAE,iBAAiB,CAAC,CAAC;QAEnE,yBAAyB;QACzB,gBAAgB,CAAC,8BAA8B,EAAE,kBAAkB,CAAC,CAAC;QAErE,uBAAuB;QACvB,gBAAgB,CAAC,8BAA8B,EAAE,kBAAkB,CAAC,CAAC;QAErE,4EAA4E;QAC5E,gBAAgB,CACd,+BAA+B,EAC/B,+BAA+B,CAChC,CAAC;QAEF,wDAAwD;QACxD,gBAAgB,CACd,+BAA+B,EAC/B,+BAA+B,CAChC,CAAC;QAEF,sEAAsE;QACtE,gBAAgB,CACd,8BAA8B,EAC9B,8BAA8B,CAC/B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,uDAAuD;QACvD,+EAA+E;QAC/E,2DAA2D;QAC3D,gBAAgB,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,CAAC;QAC/D,sCAAsC;QACtC,gBAAgB,CAAC,uBAAuB,EAAE,iBAAiB,CAAC,CAAC;QAE7D,yBAAyB;QACzB,gBAAgB,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,CAAC;QAE/D,uBAAuB;QACvB,gBAAgB,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,CAAC;QAE/D,2EAA2E;QAC3E,gBAAgB,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,CAAC;QAE3E,uDAAuD;QACvD,gBAAgB,CAAC,wBAAwB,EAAE,wBAAwB,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACrE,4CAA4C;QAC5C,gBAAgB,CACd,2FAA2F,EAC3F,+CAA+C,CAChD,CAAC;QAEF,uEAAuE;QACvE,gBAAgB,CACd,sCAAsC,EACtC,sCAAsC,CACvC,CAAC;QAEF,2EAA2E;QAC3E,gBAAgB,CAAC,6BAA6B,EAAE,iBAAiB,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,73 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing commands with different numbers', () => {
test('should work with a 1 char integer', () => {
expect(new SVGPathData('H0').commands[0].x).toEqual(0);
});
test('should work with a big integer', () => {
expect(new SVGPathData('H1234567890').commands[0].x).toEqual(1234567890);
});
test('should work with a explicitly positive integer', () => {
expect(new SVGPathData('H+1254664').commands[0].x).toEqual(+1254664);
});
test('should work with a negative integer', () => {
expect(new SVGPathData('H-1254664').commands[0].x).toEqual(-1254664);
});
test('should work with a float with left side digits', () => {
expect(new SVGPathData('H123.456').commands[0].x).toEqual(123.456);
});
test('should work with a float without left side digits', () => {
expect(new SVGPathData('H.456').commands[0].x).toEqual(0.456);
});
test('should work with a float without right side digits', () => {
expect(new SVGPathData('H123.').commands[0].x).toEqual(123.0);
});
test('should work with a number with a positive exponent', () => {
expect(new SVGPathData('H123.456e125').commands[0].x).toEqual(123.456e125);
});
test('should work with a number with an explicitly positive exponent', () => {
expect(new SVGPathData('H123.456e+125').commands[0].x).toEqual(123.456e125);
});
test('should work with a number with a negative exponent', () => {
expect(new SVGPathData('H123.456e-125').commands[0].x).toEqual(123.456e-125);
});
test('should work with a negative number with a positive exponent', () => {
expect(new SVGPathData('H-123.456e125').commands[0].x).toEqual(-123.456e125);
});
test('should work with a negative number with an explicitly positive exponent', () => {
expect(new SVGPathData('H-123.456e+125').commands[0].x).toEqual(-123.456e125);
});
test('should work with a negative number with a negative exponent', () => {
expect(new SVGPathData('H-123.456e-125').commands[0].x).toEqual(-123.456e-125);
});
test('should work with sign separated numbers', () => {
const commands = new SVGPathData('M-123.456e-125-1234.456e-125')
.commands;
expect(commands[0].x).toEqual(-123.456e-125);
expect(commands[0].y).toEqual(-1234.456e-125);
});
test('should work with sign separated numbers', () => {
const commands = new SVGPathData('M-1.456e-125-12.456e-125-123.456e-125-1234.456e-125').commands;
expect(commands[0].x).toEqual(-1.456e-125);
expect(commands[0].y).toEqual(-12.456e-125);
expect(commands[1].x).toEqual(-123.456e-125);
expect(commands[1].y).toEqual(-1234.456e-125);
});
test('should work with decpoint separated numbers', () => {
const commands = new SVGPathData('M-123.123e-123.456e-456')
.commands;
expect(commands[0].x).toEqual(-123.123e-123);
expect(commands[0].y).toEqual(0.456e-456);
});
test('should work with decpoint separated numbers', () => {
const commands = new SVGPathData('M-123.123e-123.456e-456.789e-789.123e-123').commands;
expect(commands[0].x).toEqual(-123.123e-123);
expect(commands[0].y).toEqual(0.456e-456);
expect(commands[1].x).toEqual(0.789e-789);
expect(commands[1].y).toEqual(0.123e-123);
});
test('should fail with eE', () => {
expect(() => new SVGPathData('H1ee2')).toThrow(new SyntaxError('Invalid number ending at 0'));
});
});
//# sourceMappingURL=numbers.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"numbers.test.js","sourceRoot":"","sources":["../../src/tests/numbers.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,8 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('SVGPathDataParser', () => {
test('should fail when a bad command is given', () => {
expect(() => SVGPathData.parse('b80,20')).toThrow(new SyntaxError('Unexpected character "b" at index 0.'));
});
});
//# sourceMappingURL=parser.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/tests/parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAC/C,IAAI,WAAW,CAAC,sCAAsC,CAAC,CACxD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,23 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('qt to c', () => {
test('absolute Q and T commands should be converted', () => {
expect(new SVGPathData(`M0 0
Q0,9 9,9
T9,18`)
.qtToC()
.encode()).toEqual(new SVGPathData(`M0 0
C0,6 3,9 9,9
C15,9 15,12 9,18`).encode());
});
test('relative Q and T commands should be converted', () => {
expect(new SVGPathData(`M9 18
q0,9 9,9
t9,18`)
.qtToC()
.encode()).toEqual(new SVGPathData(`M9 18
c0,6 3,9 9,9
c6,0 9,6 9,18`).encode());
});
});
//# sourceMappingURL=qttoc.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"qttoc.test.js","sourceRoot":"","sources":["../../src/tests/qttoc.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACvB,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACzD,MAAM,CACJ,IAAI,WAAW,CAAC;;UAEZ,CAAC;aACF,KAAK,EAAE;aACP,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CAAC;;qBAED,CAAC,CAAC,MAAM,EAAE,CAC1B,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACzD,MAAM,CACJ,IAAI,WAAW,CAAC;;UAEZ,CAAC;aACF,KAAK,EAAE;aACP,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CAAC;;kBAEJ,CAAC,CAAC,MAAM,EAAE,CACvB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,98 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing quadratic bezier curve to commands', () => {
test('should not work when badly declared', () => {
expect(() => new SVGPathData('Q')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('Q10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('Q10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('Q10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('Q10 10 10 10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('Q10 10 10Q10 10 10 10')).toThrow(new SyntaxError('Unterminated command at index 9.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('Q123,456 789,987').commands;
expect(commands[0].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(123);
expect(commands[0].y1).toEqual(456);
expect(commands[0].x).toEqual(789);
expect(commands[0].y).toEqual(987);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('Q123 456 789 987').commands;
expect(commands[0].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(123);
expect(commands[0].y1).toEqual(456);
expect(commands[0].x).toEqual(789);
expect(commands[0].y).toEqual(987);
});
test('should work with nested separated complexer coordinate pairs', () => {
const commands = new SVGPathData('Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
});
test('should work with multiple pairs of coordinates', () => {
const commands = new SVGPathData(`
Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x1).toEqual(-10.0032e-5);
expect(commands[1].y1).toEqual(-20.0032e-5);
expect(commands[1].x).toEqual(-30.0032e-5);
expect(commands[1].y).toEqual(-40.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x1).toEqual(-10.0032e-5);
expect(commands[2].y1).toEqual(-20.0032e-5);
expect(commands[2].x).toEqual(-30.0032e-5);
expect(commands[2].y).toEqual(-40.0032e-5);
});
test('should work with multiple declared pairs of coordinates', () => {
const commands = new SVGPathData(`
Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5
`).commands;
expect(commands[0].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x1).toEqual(-10.0032e-5);
expect(commands[0].y1).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[1].relative).toEqual(true);
expect(commands[1].x1).toEqual(-10.0032e-5);
expect(commands[1].y1).toEqual(-20.0032e-5);
expect(commands[1].x).toEqual(-30.0032e-5);
expect(commands[1].y).toEqual(-40.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.QUAD_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x1).toEqual(-10.0032e-5);
expect(commands[2].y1).toEqual(-20.0032e-5);
expect(commands[2].x).toEqual(-30.0032e-5);
expect(commands[2].y).toEqual(-40.0032e-5);
});
});
describe('Encoding line to commands', () => {
test('should work with one command', () => {
expect(new SVGPathData('Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5').encode()).toEqual('Q-0.000100032 -0.000200032 -0.000300032 -0.000400032');
});
test('should work with several commands', () => {
expect(new SVGPathData('Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5').encode()).toEqual('Q-0.000100032 -0.000200032 -0.000300032 -0.000400032q-0.000100032 -0.000200032 -0.000300032 -0.000400032Q-0.000100032 -0.000200032 -0.000300032 -0.000400032');
});
});
//# sourceMappingURL=quadraticbeziercurveto.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,238 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Dealing with real world commands', () => {
test('and Y axis symmetry with y coords equal to 0', () => {
expect(new SVGPathData('M250,381 C199.119048,381 151.285714,361.164188 115.333333,325.159688 L165.857143,274.653375 C188.333333,297.156188 218.238095,309.5625 250,309.5625 C298.214286,309.5625 341.333333,280.797 359.904762,236.291438 L369.071429,214.3125 L500,214.3125 L500,285.75 L415,285.75 C381.285714,344.304937 318.880952,381 250,381 L250,381 L250,381 L250,381 Z M130.952381,166.6875 L0,166.6875 L0,95.25 L85.047619,95.25 C118.738095,36.6950625 181.142857,0 250,0 C300.880952,0 348.714286,19.8358125 384.690476,55.8403125 L334.166667,106.346625 C311.690476,83.8438125 281.809524,71.4375 250,71.4375 C201.833333,71.4375 158.666667,100.203 140.119048,144.708563 L130.952381,166.6875 L130.952381,166.6875 L130.952381,166.6875 L130.952381,166.6875 Z M130.952381,166.6875')
.ySymmetry(381)
.ySymmetry(381)
.encode()).toEqual('M250 381C199.119048 381 151.285714 361.164188 115.333333 325.159688L165.857143 274.653375C188.333333 297.156188 218.238095 309.5625 250 309.5625C298.214286 309.5625 341.333333 280.797 359.904762 236.291438L369.071429 214.3125L500 214.3125L500 285.75L415 285.75C381.285714 344.304937 318.880952 381 250 381L250 381L250 381L250 381zM130.952381 166.6875L0 166.6875L0 95.25L85.047619 95.25C118.738095 36.695062500000006 181.142857 0 250 0C300.880952 0 348.714286 19.835812499999975 384.690476 55.84031249999998L334.166667 106.34662500000002C311.690476 83.84381250000001 281.809524 71.4375 250 71.4375C201.833333 71.4375 158.666667 100.20299999999997 140.119048 144.708563L130.952381 166.6875L130.952381 166.6875L130.952381 166.6875L130.952381 166.6875zM130.952381 166.6875');
});
test('of sapegin', () => {
expect(new SVGPathData('M77.225 66.837l-18.895-18.895c2.85-4.681 4.49-10.177 4.49-16.058 0-17.081-14.802-31.884-31.888-31.884-17.082 0-30.932 13.85-30.932 30.934s14.803 31.885 31.885 31.885c5.68 0 11-1.538 15.575-4.21l18.996 18.997c1.859 1.859 4.873 1.859 6.73 0l4.713-4.711c1.859-1.86 1.185-4.2-.674-6.058m-67.705-35.903c0-11.828 9.588-21.416 21.412-21.416 11.83 0 22.369 10.539 22.369 22.367s-9.588 21.416-21.417 21.416c-11.825 0-22.364-10.539-22.364-22.367')
.toAbs()
.ySymmetry(79)
.encode()).toEqual('M77.225 12.162999999999997L58.33 31.057999999999993C61.18 35.73899999999999 62.82 41.23499999999999 62.82 47.11599999999999C62.82 64.19699999999999 48.018 79 30.932 79C13.849999999999998 79 0 65.14999999999999 0 48.06599999999999S14.803 16.18099999999999 31.885 16.18099999999999C37.565 16.18099999999999 42.885000000000005 17.718999999999987 47.46 20.39099999999999L66.456 1.3939999999999912C68.315 -0.4650000000000034 71.32900000000001 -0.4650000000000034 73.186 1.3939999999999912L77.899 6.10499999999999C79.758 7.964999999999989 79.084 10.304999999999993 77.225 12.162999999999982M9.519999999999996 48.06599999999998C9.519999999999996 59.89399999999998 19.107999999999997 69.48199999999999 30.931999999999995 69.48199999999999C42.76199999999999 69.48199999999999 53.300999999999995 58.942999999999984 53.300999999999995 47.11499999999998S43.712999999999994 25.698999999999984 31.883999999999993 25.698999999999984C20.058999999999994 25.698999999999984 9.519999999999992 36.237999999999985 9.519999999999992 48.06599999999999');
});
test('of hannesjohansson', () => {
expect(new SVGPathData('M2.25 12.751C2.25 18.265 6.736 22.751 12.25 22.751C14.361 22.751 16.318 22.09 17.933 20.969L25.482 28.518C25.97 29.006 26.61 29.25 27.25 29.25S28.53 29.006 29.018 28.518C29.995 27.542 29.995 25.96 29.018 24.983L21.207 17.172C21.869 15.837 22.251 14.339 22.251 12.751C22.251 7.237 17.765 2.751 12.251 2.751S2.251 7.236 2.25 12.751zM6.251 12.751C6.251 9.442 8.942 6.751 12.251 6.751S18.251 9.442 18.251 12.751S15.56 18.751 12.251 18.751S6.251 16.06 6.251 12.751z')
.ySymmetry(32)
.ySymmetry(32)
.round(10e10)
.encode()).toEqual('M2.25 12.751C2.25 18.265 6.736 22.751 12.25 22.751C14.361 22.751 16.318 22.09 17.933 20.969L25.482 28.518C25.97 29.006 26.61 29.25 27.25 29.25S28.53 29.006 29.018 28.518C29.995 27.542 29.995 25.96 29.018 24.983L21.207 17.172C21.869 15.837 22.251 14.339 22.251 12.751C22.251 7.237 17.765 2.751 12.251 2.751S2.251 7.236 2.25 12.751zM6.251 12.751C6.251 9.442 8.942 6.751 12.251 6.751S18.251 9.442 18.251 12.751S15.56 18.751 12.251 18.751S6.251 16.06 6.251 12.751z');
});
test('of my blog', () => {
expect(new SVGPathData('m 0,100 0,10 5,0 0,-5 15,0 0,15 -20,0 0,30 25,0 0,-50 z m 5,25 15,0 0,20 -15,0 z')
.toAbs()
.encode()).toEqual('M0 100L0 110L5 110L5 105L20 105L20 120L0 120L0 150L25 150L25 100zM5 125L20 125L20 145L5 145z');
});
test('of tremby bug report', () => {
expect(new SVGPathData('M0,250 l20,0 a40,20 0 0,0 40,20 l80,-20 a40,20 0 0,1 40,20 l80,-20 a40,20 0 1,0 40,20 l80,-20 a40,20 0 1,1 40,20 l80,-20 l0,-120 H0 Z')
.scale(1, -1)
.encode()).toEqual('M0 -250l20 0a40 20 0 0 1 40 -20l80 20a40 20 0 0 0 40 -20l80 20a40 20 0 1 1 40 -20l80 20a40 20 0 1 0 40 -20l80 20l0 120H0z');
});
test('of fh1ch bug report', () => {
expect(new SVGPathData('M382.658 327.99c16.71-17.204 26.987-40.676 26.987-66.542 0-52.782-42.792-95.575-95.574-95.575-29.894 0-56.583 13.74-74.104 35.24-17.47-7.164-37.11-9.877-57.725-7.596-44.774 4.964-82.87 38.712-94.42 84.393-2.14 8.447-5.14 13.34-14.276 16.473-26.103 8.952-42.988 35.322-41.446 61.6 1.696 28.703 21.253 52.36 48.917 59.185 1.942.48 3.813.668 5.61 1.048.063 0 .114-.216.166-.216h224.753c.154 0 .31.235.463.216 39.072-1.706 70.56-33.144 71.865-71.815.197-5.66-.225-11.13-1.21-16.472m-63.554 62.75c-2.312.503-4.697.847-7.1 1.166-6.095.83-3.763.156-18.232.156H103.716c-3.113 0-6.207.11-9.29-.044-21.283-1.038-36.646-16.796-37.243-37.185-.617-20.696 13.596-37.283 34.52-39.833 5.365-.646 10.873-.082 16.217-.082 6.186-58.885 31.18-90.46 76.418-96.802 19.834-2.785 38.66.794 56.06 10.825 25.434 14.654 38.69 37.81 44.127 66.47 4.748-1.108 8.355-1.973 11.962-2.796 27.85-6.33 54.868 10.033 61.034 36.958 6.516 28.426-9.844 55.01-38.414 61.168zm8.86-121.502c-4.225-1.07-8.613-1.778-13.125-2.097-1.756-.124-3.35-.34-4.788-.668-6.207-1.4-9.794-4.8-13.124-11.49-.185-.37-.37-.73-.555-1.1-5.333-10.44-11.92-19.68-19.537-27.604l-17.82-14.973-.42-.35c13.616-13.822 34.58-24.47 55.473-24.47 41.363 0 75.02 33.657 75.02 75.022 0 16.452-5.334 31.683-14.357 44.056-9.68-17.788-26.348-31.2-46.768-36.327z')
.toAbs()
.round(1000)
.encode()).toEqual('M382.658 327.99C399.368 310.786 409.645 287.314 409.645 261.448C409.645 208.666 366.853 165.873 314.071 165.873C284.177 165.873 257.488 179.613 239.967 201.113C222.497 193.949 202.857 191.236 182.242 193.517C137.468 198.481 99.372 232.229 87.822 277.91C85.682 286.357 82.682 291.25 73.546 294.383C47.443 303.335 30.558 329.705 32.1 355.983C33.796 384.686 53.353 408.343 81.017 415.168C82.959 415.648 84.83 415.836 86.627 416.216C86.69 416.216 86.741 416 86.793 416H311.546C311.7 416 311.856 416.235 312.009 416.216C351.081 414.51 382.569 383.072 383.874 344.401C384.071 338.741 383.649 333.271 382.664 327.929M319.11 390.679C316.798 391.182 314.413 391.526 312.01 391.845C305.915 392.675 308.247 392.001 293.778 392.001H103.716C100.603 392.001 97.509 392.111 94.426 391.957C73.143 390.919 57.78 375.161 57.183 354.772C56.566 334.076 70.779 317.489 91.703 314.939C97.068 314.293 102.576 314.857 107.92 314.857C114.106 255.972 139.1 224.397 184.338 218.055C204.172 215.27 222.998 218.849 240.398 228.88C265.832 243.534 279.088 266.69 284.525 295.35C289.273 294.242 292.88 293.377 296.487 292.554C324.337 286.224 351.355 302.587 357.521 329.512C364.037 357.938 347.677 384.522 319.107 390.68zM327.97 269.177C323.745 268.107 319.357 267.399 314.845 267.08C313.089 266.956 311.495 266.74 310.057 266.412C303.85 265.012 300.263 261.612 296.933 254.922C296.748 254.552 296.563 254.192 296.378 253.822C291.045 243.382 284.458 234.142 276.841 226.218L259.021 211.245L258.601 210.895C272.217 197.073 293.181 186.425 314.074 186.425C355.437 186.425 389.094 220.082 389.094 261.447C389.094 277.899 383.76 293.13 374.737 305.503C365.057 287.715 348.389 274.303 327.969 269.176z');
});
test('of kaptinlin bug report', () => {
expect(new SVGPathData('M13 5H8V2.6a1 1 0 00.42-.46l5.08-.64a.5.5 0 000-1l-5.22.65a1 1 0 00-.78-.4 1 1 0 00-.92.62L1.5 2a.5.5 0 000 1l5.22-.65c.077.1.172.185.28.25V5H2a1 1 0 00-1 1v7a1 1 0 001 1h11a1 1 0 001-1V6a1 1 0 00-1-1zm-6 6H3V7h4v4zm5 0H8V7h4v4z')
.toAbs()
.round(1000)
.encode()).toEqual('M13 5H8V2.6A1 1 0 0 0 8.42 2.14L13.5 1.5A0.5 0.5 0 0 0 13.5 0.5L8.28 1.15A1 1 0 0 0 7.5 0.75A1 1 0 0 0 6.58 1.37L1.5 2A0.5 0.5 0 0 0 1.5 3L6.72 2.35C6.797 2.45 6.892 2.535 7 2.6V5H2A1 1 0 0 0 1 6V13A1 1 0 0 0 2 14H13A1 1 0 0 0 14 13V6A1 1 0 0 0 13 5zM7 11H3V7H7V11zM12 11H8V7H12V11z');
});
test('of kaptinlin bug report 2', () => {
expect(new SVGPathData('M13 5H8V2.6a1 1 0 00 .42-.46l5.08-.64a.5.5 0 000-1l-5.22.65a1 1 0 00-.78-.4 1 1 0 00-.92.62L1.5 2a.5.5 0 000 1l5.22-.65c.077.1.172.185.28.25V5H2a1 1 0 00-1 1v7a1 1 0 001 1h11a1 1 0 001-1V6a1 1 0 00-1-1zm-6 6H3V7h4v4zm5 0H8V7h4v4z')
.toAbs()
.round(1000)
.encode()).toEqual('M13 5H8V2.6A1 1 0 0 0 8.42 2.14L13.5 1.5A0.5 0.5 0 0 0 13.5 0.5L8.28 1.15A1 1 0 0 0 7.5 0.75A1 1 0 0 0 6.58 1.37L1.5 2A0.5 0.5 0 0 0 1.5 3L6.72 2.35C6.797 2.45 6.892 2.535 7 2.6V5H2A1 1 0 0 0 1 6V13A1 1 0 0 0 2 14H13A1 1 0 0 0 14 13V6A1 1 0 0 0 13 5zM7 11H3V7H7V11zM12 11H8V7H12V11z');
});
describe('bootstrap-icons', () => {
test('box-arrow-up-right', () => {
expect(new SVGPathData('M3.5 15A1.5 1.5 0 005 16.5h8a1.5 1.5 0 001.5-1.5v-4a.5.5 0 00-1 0v4a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V7a.5.5 0 01.5-.5h4a.5.5 0 000-1H5A1.5 1.5 0 003.5 7v8zm7-11a.5.5 0 01.5-.5h5a.5.5 0 01.5.5v5a.5.5 0 01-1 0V4.5H11a.5.5 0 01-.5-.5z')
.toAbs()
.round(1000)
.encode()).toEqual('M3.5 15A1.5 1.5 0 0 0 5 16.5H13A1.5 1.5 0 0 0 14.5 15V11A0.5 0.5 0 0 0 13.5 11V15A0.5 0.5 0 0 1 13 15.5H5A0.5 0.5 0 0 1 4.5 15V7A0.5 0.5 0 0 1 5 6.5H9A0.5 0.5 0 0 0 9 5.5H5A1.5 1.5 0 0 0 3.5 7V15zM10.5 4A0.5 0.5 0 0 1 11 3.5H16A0.5 0.5 0 0 1 16.5 4V9A0.5 0.5 0 0 1 15.5 9V4.5H11A0.5 0.5 0 0 1 10.5 4z');
});
test('bootstrap', () => {
expect(new SVGPathData('M14 3H6a3 3 0 00-3 3v8a3 3 0 003 3h8a3 3 0 003-3V6a3 3 0 00-3-3zM6 2a4 4 0 00-4 4v8a4 4 0 004 4h8a4 4 0 004-4V6a4 4 0 00-4-4H6z')
.toAbs()
.round(1000)
.encode()).toEqual('M14 3H6A3 3 0 0 0 3 6V14A3 3 0 0 0 6 17H14A3 3 0 0 0 17 14V6A3 3 0 0 0 14 3zM6 2A4 4 0 0 0 2 6V14A4 4 0 0 0 6 18H14A4 4 0 0 0 18 14V6A4 4 0 0 0 14 2H6z');
});
test('camera', () => {
expect(new SVGPathData('M16.333 5h-2.015A5.97 5.97 0 0011 4a5.972 5.972 0 00-3.318 1H3.667C2.747 5 2 5.746 2 6.667v6.666C2 14.253 2.746 15 3.667 15h4.015c.95.632 2.091 1 3.318 1a5.973 5.973 0 003.318-1h2.015c.92 0 1.667-.746 1.667-1.667V6.667C18 5.747 17.254 5 16.333 5zM3.5 7a.5.5 0 100-1 .5.5 0 000 1zm7.5 8a5 5 0 100-10 5 5 0 000 10z')
.toAbs()
.round(1000)
.encode()).toEqual('M16.333 5H14.318A5.97 5.97 0 0 0 11 4A5.972 5.972 0 0 0 7.682 5H3.667C2.747 5 2 5.746 2 6.667V13.333C2 14.253 2.746 15 3.667 15H7.682C8.632 15.632 9.773 16 11 16A5.973 5.973 0 0 0 14.318 15H16.333C17.253 15 18 14.254 18 13.333V6.667C18 5.747 17.254 5 16.333 5zM3.5 7A0.5 0.5 0 1 0 3.5 6A0.5 0.5 0 0 0 3.5 7zM11 15A5 5 0 1 0 11 5A5 5 0 0 0 11 15z');
});
test('bucket', () => {
expect(new SVGPathData('M10 3.5A4.5 4.5 0 005.5 8h-1a5.5 5.5 0 1111 0h-1A4.5 4.5 0 0010 3.5z')
.toAbs()
.round(1000)
.encode()).toEqual('M10 3.5A4.5 4.5 0 0 0 5.5 8H4.5A5.5 5.5 0 1 1 15.5 8H14.5A4.5 4.5 0 0 0 10 3.5z');
});
test('check-box', () => {
expect(new SVGPathData('M3.5 15A1.5 1.5 0 005 16.5h10a1.5 1.5 0 001.5-1.5v-5a.5.5 0 00-1 0v5a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V5a.5.5 0 01.5-.5h8a.5.5 0 000-1H5A1.5 1.5 0 003.5 5v10z')
.toAbs()
.round(1000)
.encode()).toEqual('M3.5 15A1.5 1.5 0 0 0 5 16.5H15A1.5 1.5 0 0 0 16.5 15V10A0.5 0.5 0 0 0 15.5 10V15A0.5 0.5 0 0 1 15 15.5H5A0.5 0.5 0 0 1 4.5 15V5A0.5 0.5 0 0 1 5 4.5H13A0.5 0.5 0 0 0 13 3.5H5A1.5 1.5 0 0 0 3.5 5V15z');
});
test('diamond-half', () => {
expect(new SVGPathData('M8.94 2.354a1.5 1.5 0 012.12 0l6.586 6.585a1.5 1.5 0 010 2.122l-6.585 6.585a1.5 1.5 0 01-2.122 0l-6.585-6.585a1.5 1.5 0 010-2.122l6.585-6.585zm1.06.56a.498.498 0 00-.354.147L3.061 9.646a.5.5 0 000 .707l6.585 6.586a.499.499 0 00.354.147V2.914z')
.toAbs()
.round(1000)
.encode()).toEqual('M8.94 2.354A1.5 1.5 0 0 1 11.06 2.354L17.646 8.939A1.5 1.5 0 0 1 17.646 11.061L11.061 17.646A1.5 1.5 0 0 1 8.939 17.646L2.354 11.061A1.5 1.5 0 0 1 2.354 8.939L8.939 2.354zM10 2.914A0.498 0.498 0 0 0 9.646 3.061L3.061 9.646A0.5 0.5 0 0 0 3.061 10.353L9.646 16.939A0.499 0.499 0 0 0 10 17.086V2.914z');
});
test('document', () => {
expect(new SVGPathData('M6 3h8a2 2 0 012 2v10a2 2 0 01-2 2H6a2 2 0 01-2-2V5a2 2 0 012-2zm0 1a1 1 0 00-1 1v10a1 1 0 001 1h8a1 1 0 001-1V5a1 1 0 00-1-1H6z')
.toAbs()
.round(1000)
.encode()).toEqual('M6 3H14A2 2 0 0 1 16 5V15A2 2 0 0 1 14 17H6A2 2 0 0 1 4 15V5A2 2 0 0 1 6 3zM6 4A1 1 0 0 0 5 5V15A1 1 0 0 0 6 16H14A1 1 0 0 0 15 15V5A1 1 0 0 0 14 4H6z');
});
test('trash', () => {
expect(new SVGPathData('M7.5 7.5A.5.5 0 018 8v6a.5.5 0 01-1 0V8a.5.5 0 01.5-.5zm2.5 0a.5.5 0 01.5.5v6a.5.5 0 01-1 0V8a.5.5 0 01.5-.5zm3 .5a.5.5 0 00-1 0v6a.5.5 0 001 0V8z')
.toAbs()
.round(1000)
.encode()).toEqual('M7.5 7.5A0.5 0.5 0 0 1 8 8V14A0.5 0.5 0 0 1 7 14V8A0.5 0.5 0 0 1 7.5 7.5zM10 7.5A0.5 0.5 0 0 1 10.5 8V14A0.5 0.5 0 0 1 9.5 14V8A0.5 0.5 0 0 1 10 7.5zM13 8A0.5 0.5 0 0 0 12 8V14A0.5 0.5 0 0 0 13 14V8z');
});
test('tv-fill', () => {
expect(new SVGPathData('M4.5 15.5A.5.5 0 015 15h10a.5.5 0 010 1H5a.5.5 0 01-.5-.5zM4 4h12s2 0 2 2v6s0 2-2 2H4s-2 0-2-2V6s0-2 2-2z')
.toAbs()
.round(1000)
.encode()).toEqual('M4.5 15.5A0.5 0.5 0 0 1 5 15H15A0.5 0.5 0 0 1 15 16H5A0.5 0.5 0 0 1 4.5 15.5zM4 4H16S18 4 18 6V12S18 14 16 14H4S2 14 2 12V6S2 4 4 4z');
});
test('unlock-fill', () => {
expect(new SVGPathData('M2.5 10a2 2 0 012-2h7a2 2 0 012 2v5a2 2 0 01-2 2h-7a2 2 0 01-2-2v-5z')
.toAbs()
.round(1000)
.encode()).toEqual('M2.5 10A2 2 0 0 1 4.5 8H11.5A2 2 0 0 1 13.5 10V15A2 2 0 0 1 11.5 17H4.5A2 2 0 0 1 2.5 15V10z');
});
test('watch', () => {
expect(new SVGPathData('M6 16.333v-1.86A5.985 5.985 0 014 10c0-1.777.772-3.374 2-4.472V3.667C6 2.747 6.746 2 7.667 2h4.666C13.253 2 14 2.746 14 3.667v1.86A5.985 5.985 0 0116 10a5.985 5.985 0 01-2 4.472v1.861c0 .92-.746 1.667-1.667 1.667H7.667C6.747 18 6 17.254 6 16.333zM15 10a5 5 0 10-10 0 5 5 0 0010 0z')
.toAbs()
.round(1000)
.encode()).toEqual('M6 16.333V14.473A5.985 5.985 0 0 1 4 10C4 8.223 4.772 6.626 6 5.528V3.667C6 2.747 6.746 2 7.667 2H12.333C13.253 2 14 2.746 14 3.667V5.527A5.985 5.985 0 0 1 16 10A5.985 5.985 0 0 1 14 14.472V16.333C14 17.253 13.254 18 12.333 18H7.667C6.747 18 6 17.254 6 16.333zM15 10A5 5 0 1 0 5 10A5 5 0 0 0 15 10z');
});
test('wrench', () => {
expect(new SVGPathData('M2.102 4.223A3.004 3.004 0 005 8c.27 0 .532-.036.78-.103l6.342 6.252A3.003 3.003 0 0015 18a3 3 0 10-.851-5.878L7.897 5.781A3.004 3.004 0 004.223 2.1l2.141 2.142L6 6l-1.757.364-2.141-2.141zm13.37 9.019L15 13l-.471.242-.529.026-.287.445-.445.287-.026.529L13 15l.242.471.026.529.445.287.287.445.529.026L15 17l.471-.242.529-.026.287-.445.445-.287.026-.529L17 15l-.242-.471-.026-.529-.445-.287-.287-.445-.529-.026z')
.toAbs()
.round(1000)
.encode()).toEqual('M2.102 4.223A3.004 3.004 0 0 0 5 8C5.27 8 5.532 7.964 5.78 7.897L12.122 14.149A3.003 3.003 0 0 0 15 18A3 3 0 1 0 14.149 12.122L7.897 5.781A3.004 3.004 0 0 0 4.223 2.1L6.364 4.242L6 6L4.243 6.364L2.102 4.223zM15.472 13.242L15 13L14.529 13.242L14 13.268L13.713 13.713L13.268 14L13.242 14.529L13 15L13.242 15.471L13.268 16L13.713 16.287L14 16.732L14.529 16.758L15 17L15.471 16.758L16 16.732L16.287 16.287L16.732 16L16.758 15.471L17 15L16.758 14.529L16.732 14L16.287 13.713L16 13.268L15.471 13.242z');
});
test('x-octagon', () => {
expect(new SVGPathData('M6.54 2.146A.5.5 0 016.893 2h6.214a.5.5 0 01.353.146l4.394 4.394a.5.5 0 01.146.353v6.214a.5.5 0 01-.146.353l-4.394 4.394a.5.5 0 01-.353.146H6.893a.5.5 0 01-.353-.146L2.146 13.46A.5.5 0 012 13.107V6.893a.5.5 0 01.146-.353L6.54 2.146zM7.1 3L3 7.1v5.8L7.1 17h5.8l4.1-4.1V7.1L12.9 3H7.1z')
.toAbs()
.round(1000)
.encode()).toEqual('M6.54 2.146A0.5 0.5 0 0 1 6.893 2H13.107A0.5 0.5 0 0 1 13.46 2.146L17.854 6.54A0.5 0.5 0 0 1 18 6.893V13.107A0.5 0.5 0 0 1 17.854 13.46L13.46 17.854A0.5 0.5 0 0 1 13.107 18H6.893A0.5 0.5 0 0 1 6.54 17.854L2.146 13.46A0.5 0.5 0 0 1 2 13.107V6.893A0.5 0.5 0 0 1 2.146 6.54L6.54 2.146zM7.1 3L3 7.1V12.9L7.1 17H12.9L17 12.9V7.1L12.9 3H7.1z');
});
test('x-square-fill', () => {
expect(new SVGPathData('M4 2a2 2 0 00-2 2v12a2 2 0 002 2h12a2 2 0 002-2V4a2 2 0 00-2-2H4zm3.354 4.646L10 9.293l2.646-2.647a.5.5 0 01.708.708L10.707 10l2.647 2.646a.5.5 0 01-.708.708L10 10.707l-2.646 2.647a.5.5 0 01-.708-.708L9.293 10 6.646 7.354a.5.5 0 11.708-.708z')
.toAbs()
.round(1000)
.encode()).toEqual('M4 2A2 2 0 0 0 2 4V16A2 2 0 0 0 4 18H16A2 2 0 0 0 18 16V4A2 2 0 0 0 16 2H4zM7.354 6.646L10 9.293L12.646 6.646A0.5 0.5 0 0 1 13.354 7.354L10.707 10L13.354 12.646A0.5 0.5 0 0 1 12.646 13.354L10 10.707L7.354 13.354A0.5 0.5 0 0 1 6.646 12.646L9.293 10L6.646 7.354A0.5 0.5 0 1 1 7.354 6.646z');
});
});
describe('maki-icons', () => {
test('alcohol-shop', () => {
expect(new SVGPathData('M14 4h-4v3.5a2 2 0 001.5 1.93V13H11a.5.5 0 000 1h2a.5.5 0 000-1h-.5V9.43A2 2 0 0014 7.5V4zm-1 3.5a1 1 0 11-2 0V5h2v2.5zm-7.5-5V2a.5.5 0 000-1V.5A.5.5 0 005 0H4a.5.5 0 00-.5.5V1a.5.5 0 000 1v.5C3.5 3.93 1 5.57 1 7v6a1 1 0 001 1h5a1.1 1.1 0 001-1V7c0-1.35-2.5-3.15-2.5-4.5zm-1 9.5a2.5 2.5 0 110-5 2.5 2.5 0 010 5z')
.toAbs()
.round(1000)
.encode()).toEqual('M14 4H10V7.5A2 2 0 0 0 11.5 9.43V13H11A0.5 0.5 0 0 0 11 14H13A0.5 0.5 0 0 0 13 13H12.5V9.43A2 2 0 0 0 14 7.5V4zM13 7.5A1 1 0 1 1 11 7.5V5H13V7.5zM5.5 2.5V2A0.5 0.5 0 0 0 5.5 1V0.5A0.5 0.5 0 0 0 5 0H4A0.5 0.5 0 0 0 3.5 0.5V1A0.5 0.5 0 0 0 3.5 2V2.5C3.5 3.93 1 5.57 1 7V13A1 1 0 0 0 2 14H7A1.1 1.1 0 0 0 8 13V7C8 5.65 5.5 3.85 5.5 2.5zM4.5 12A2.5 2.5 0 1 1 4.5 7A2.5 2.5 0 0 1 4.5 12z');
});
test('baseball', () => {
expect(new SVGPathData('M10 3.5a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0zM7 .28A.28.28 0 006.72 0a.49.49 0 00-.25.16L4 4.59a.48.48 0 000 .13c0 .155.125.28.28.28a.49.49 0 00.26-.16L7 .41a.472.472 0 000-.13zm5.9 13.92L10 6.39A.49.49 0 009.52 6h-5a.5.5 0 000 1H7l1.45 2.51-4.27 4.61a.49.49 0 00-.18.38.5.5 0 00.5.5.49.49 0 00.33-.13l4.45-4.15 2.76 4a.51.51 0 00.44.26c.28 0 .51-.22.52-.5a.5.5 0 00-.1-.28z')
.toAbs()
.round(1000)
.encode()).toEqual('M10 3.5A1.5 1.5 0 1 1 7 3.5A1.5 1.5 0 0 1 10 3.5zM7 0.28A0.28 0.28 0 0 0 6.72 0A0.49 0.49 0 0 0 6.47 0.16L4 4.59A0.48 0.48 0 0 0 4 4.72C4 4.875 4.125 5 4.28 5A0.49 0.49 0 0 0 4.54 4.84L7 0.41A0.472 0.472 0 0 0 7 0.28zM12.9 14.2L10 6.39A0.49 0.49 0 0 0 9.52 6H4.52A0.5 0.5 0 0 0 4.52 7H7L8.45 9.51L4.18 14.12A0.49 0.49 0 0 0 4 14.5A0.5 0.5 0 0 0 4.5 15A0.49 0.49 0 0 0 4.83 14.87L9.28 10.72L12.04 14.72A0.51 0.51 0 0 0 12.48 14.98C12.76 14.98 12.99 14.76 13 14.48A0.5 0.5 0 0 0 12.9 14.2z');
});
test('beach', () => {
expect(new SVGPathData('M5.36 1.67l-.01 4.02a4.452 4.452 0 00-1.1-.11c-.37.1-.74.63-1.1.76a4.202 4.202 0 012.21-4.67zm2.41-.64L9.8 4.48a3.183 3.183 0 01.84-.61c.36-.1.94.17 1.34.11a4.202 4.202 0 00-4.21-2.95zM1 13h13c-.66-.66-2.64-1.11-4.34-1.33l-1.87-7c.52-.05 1.15.03 1.53 0l-2.11-3.6H7.2a6.174 6.174 0 00-.7.14 4.38 4.38 0 00-.64.22l-.01 4.15c.35-.17.84-.54 1.3-.74l1.8 6.74c-.58-.05-1.09-.08-1.45-.08C6.03 11.5 2 12 1 13z')
.toAbs()
.round(1000)
.encode()).toEqual('M5.36 1.67L5.35 5.69A4.452 4.452 0 0 0 4.25 5.58C3.88 5.68 3.51 6.21 3.15 6.34A4.202 4.202 0 0 1 5.36 1.67zM7.77 1.03L9.8 4.48A3.183 3.183 0 0 1 10.64 3.87C11 3.77 11.58 4.04 11.98 3.98A4.202 4.202 0 0 0 7.77 1.03zM1 13H14C13.34 12.34 11.36 11.89 9.66 11.67L7.79 4.67C8.31 4.62 8.94 4.7 9.32 4.67L7.21 1.07H7.2A6.174 6.174 0 0 0 6.5 1.21A4.38 4.38 0 0 0 5.86 1.43L5.85 5.58C6.2 5.41 6.69 5.04 7.15 4.84L8.95 11.58C8.37 11.53 7.86 11.5 7.5 11.5C6.03 11.5 2 12 1 13z');
});
test('cafe', () => {
expect(new SVGPathData('M12 5h-2V3H2v4a4 4 0 007.45 2H12a2 2 0 100-4zm0 3H9.86A4 4 0 0010 7V6h2a1 1 0 110 2zm-2 4.5a.5.5 0 01-.5.5h-7a.5.5 0 010-1h7a.5.5 0 01.5.5z')
.toAbs()
.round(1000)
.encode()).toEqual('M12 5H10V3H2V7A4 4 0 0 0 9.45 9H12A2 2 0 1 0 12 5zM12 8H9.86A4 4 0 0 0 10 7V6H12A1 1 0 1 1 12 8zM10 12.5A0.5 0.5 0 0 1 9.5 13H2.5A0.5 0.5 0 0 1 2.5 12H9.5A0.5 0.5 0 0 1 10 12.5z');
});
test('car', () => {
expect(new SVGPathData('M14 7a1.5 1.5 0 00-1.15-1.45l-1.39-3.24A.5.5 0 0011 2H4a.5.5 0 00-.44.28L2.15 5.54A1.5 1.5 0 001 7v3.5h1v1a1 1 0 102 0v-1h7v1a1 1 0 102 0v-1h1V7zM4.3 3h6.4l1.05 2.5h-8.5L4.3 3zM3 9a1 1 0 110-2 1 1 0 010 2zm9 0a1 1 0 110-2 1 1 0 010 2z')
.toAbs()
.round(1000)
.encode()).toEqual('M14 7A1.5 1.5 0 0 0 12.85 5.55L11.46 2.31A0.5 0.5 0 0 0 11 2H4A0.5 0.5 0 0 0 3.56 2.28L2.15 5.54A1.5 1.5 0 0 0 1 7V10.5H2V11.5A1 1 0 1 0 4 11.5V10.5H11V11.5A1 1 0 1 0 13 11.5V10.5H14V7zM4.3 3H10.7L11.75 5.5H3.25L4.3 3zM3 9A1 1 0 1 1 3 7A1 1 0 0 1 3 9zM12 9A1 1 0 1 1 12 7A1 1 0 0 1 12 9z');
});
test('casino', () => {
expect(new SVGPathData('M8.5 10A2.5 2.5 0 0013 8.5c0-.564-.194-1.079-.509-1.497L12.5 7l-5-6-5 6 .009.003A2.478 2.478 0 002 8.5 2.5 2.5 0 006.5 10l.5-.666V11.5C7 13 4.5 13 4.5 13a.5.5 0 100 1h6a.5.5 0 000-1S8 13 8 11.5V9.334l.5.666z')
.toAbs()
.round(1000)
.encode()).toEqual('M8.5 10A2.5 2.5 0 0 0 13 8.5C13 7.936 12.806 7.421 12.491 7.003L12.5 7L7.5 1L2.5 7L2.509 7.003A2.478 2.478 0 0 0 2 8.5A2.5 2.5 0 0 0 6.5 10L7 9.334V11.5C7 13 4.5 13 4.5 13A0.5 0.5 0 1 0 4.5 14H10.5A0.5 0.5 0 0 0 10.5 13S8 13 8 11.5V9.334L8.5 10z');
});
test('cross', () => {
expect(new SVGPathData('M2.64 1.27L7.5 6.13l4.84-4.84A.92.92 0 0113 1a1 1 0 011 1 .9.9 0 01-.27.66L8.84 7.5l4.89 4.89A.9.9 0 0114 13a1 1 0 01-1 1 .92.92 0 01-.69-.27L7.5 8.87l-4.85 4.85A.92.92 0 012 14a1 1 0 01-1-1 .9.9 0 01.27-.66L6.16 7.5 1.27 2.61A.9.9 0 011 2a1 1 0 011-1c.24.003.47.1.64.27z')
.toAbs()
.round(1000)
.encode()).toEqual('M2.64 1.27L7.5 6.13L12.34 1.29A0.92 0.92 0 0 1 13 1A1 1 0 0 1 14 2A0.9 0.9 0 0 1 13.73 2.66L8.84 7.5L13.73 12.39A0.9 0.9 0 0 1 14 13A1 1 0 0 1 13 14A0.92 0.92 0 0 1 12.31 13.73L7.5 8.87L2.65 13.72A0.92 0.92 0 0 1 2 14A1 1 0 0 1 1 13A0.9 0.9 0 0 1 1.27 12.34L6.16 7.5L1.27 2.61A0.9 0.9 0 0 1 1 2A1 1 0 0 1 2 1C2.24 1.003 2.47 1.1 2.64 1.27z');
});
});
describe('octicons', () => {
test('broadcast', () => {
expect(new SVGPathData('M9 9H8c.55 0 1-.45 1-1V7c0-.55-.45-1-1-1H7c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1H6c-.55 0-1 .45-1 1v2h1v3c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-3h1v-2c0-.55-.45-1-1-1zM7 7h1v1H7V7zm2 4H8v4H7v-4H6v-1h3v1zm2.09-3.5c0-1.98-1.61-3.59-3.59-3.59A3.593 3.593 0 004 8.31v1.98c-.61-.77-1-1.73-1-2.8 0-2.48 2.02-4.5 4.5-4.5S12 5.01 12 7.49c0 1.06-.39 2.03-1 2.8V8.31c.06-.27.09-.53.09-.81zm3.91 0c0 2.88-1.63 5.38-4 6.63v-1.05a6.553 6.553 0 003.09-5.58A6.59 6.59 0 007.5.91 6.59 6.59 0 00.91 7.5c0 2.36 1.23 4.42 3.09 5.58v1.05A7.497 7.497 0 017.5 0C11.64 0 15 3.36 15 7.5z')
.toAbs()
.round(1000)
.encode()).toEqual('M9 9H8C8.55 9 9 8.55 9 8V7C9 6.45 8.55 6 8 6H7C6.45 6 6 6.45 6 7V8C6 8.55 6.45 9 7 9H6C5.45 9 5 9.45 5 10V12H6V15C6 15.55 6.45 16 7 16H8C8.55 16 9 15.55 9 15V12H10V10C10 9.45 9.55 9 9 9zM7 7H8V8H7V7zM9 11H8V15H7V11H6V10H9V11zM11.09 7.5C11.09 5.52 9.48 3.91 7.5 3.91A3.593 3.593 0 0 0 4 8.31V10.29C3.39 9.52 3 8.56 3 7.49C3 5.01 5.02 2.99 7.5 2.99S12 5.01 12 7.49C12 8.55 11.61 9.52 11 10.29V8.31C11.06 8.04 11.09 7.78 11.09 7.5zM15 7.5C15 10.38 13.37 12.88 11 14.13V13.08A6.553 6.553 0 0 0 14.09 7.5A6.59 6.59 0 0 0 7.5 0.91A6.59 6.59 0 0 0 0.91 7.5C0.91 9.86 2.14 11.92 4 13.08V14.13A7.497 7.497 0 0 1 7.5 0C11.64 0 15 3.36 15 7.5z');
});
test('dashboard', () => {
expect(new SVGPathData('M9 5H8V4h1v1zm4 3h-1v1h1V8zM6 5H5v1h1V5zM5 8H4v1h1V8zm11-5.5l-.5-.5L9 7c-.06-.02-1 0-1 0-.55 0-1 .45-1 1v1c0 .55.45 1 1 1h1c.55 0 1-.45 1-1v-.92l6-5.58zm-1.59 4.09c.19.61.3 1.25.3 1.91 0 3.42-2.78 6.2-6.2 6.2-3.42 0-6.21-2.78-6.21-6.2 0-3.42 2.78-6.2 6.2-6.2 1.2 0 2.31.34 3.27.94l.94-.94A7.459 7.459 0 008.51 1C4.36 1 1 4.36 1 8.5 1 12.64 4.36 16 8.5 16c4.14 0 7.5-3.36 7.5-7.5 0-1.03-.2-2.02-.59-2.91l-1 1z')
.toAbs()
.round(1000)
.encode()).toEqual('M9 5H8V4H9V5zM13 8H12V9H13V8zM6 5H5V6H6V5zM5 8H4V9H5V8zM16 2.5L15.5 2L9 7C8.94 6.98 8 7 8 7C7.45 7 7 7.45 7 8V9C7 9.55 7.45 10 8 10H9C9.55 10 10 9.55 10 9V8.08L16 2.5zM14.41 6.59C14.6 7.2 14.71 7.84 14.71 8.5C14.71 11.92 11.93 14.7 8.51 14.7C5.09 14.7 2.3 11.92 2.3 8.5C2.3 5.08 5.08 2.3 8.5 2.3C9.7 2.3 10.81 2.64 11.77 3.24L12.71 2.3A7.459 7.459 0 0 0 8.51 1C4.36 1 1 4.36 1 8.5C1 12.64 4.36 16 8.5 16C12.64 16 16 12.64 16 8.5C16 7.47 15.8 6.48 15.41 5.59L14.41 6.59z');
});
test('dependent', () => {
expect(new SVGPathData('M1 1h7.5l2 2H9L8 2H1v12h10v-1h1v1c0 .55-.45 1-1 1H1c-.55 0-1-.45-1-1V2c0-.55.45-1 1-1zm9 6h3v1h-3V7zm2 2h-2v1h2V9zM8.583 4h4.375L15 6v5.429a.58.58 0 01-.583.571H8.583A.58.58 0 018 11.429V10h1v1h5V6.5L12.5 5H9v1H8V4.571A.58.58 0 018.583 4zM9.5 7H6.667V5l-4 3 4 3V9H9.5V7z')
.toAbs()
.round(1000)
.encode()).toEqual('M1 1H8.5L10.5 3H9L8 2H1V14H11V13H12V14C12 14.55 11.55 15 11 15H1C0.45 15 0 14.55 0 14V2C0 1.45 0.45 1 1 1zM10 7H13V8H10V7zM12 9H10V10H12V9zM8.583 4H12.958L15 6V11.429A0.58 0.58 0 0 1 14.417 12H8.583A0.58 0.58 0 0 1 8 11.429V10H9V11H14V6.5L12.5 5H9V6H8V4.571A0.58 0.58 0 0 1 8.583 4zM9.5 7H6.667V5L2.667 8L6.667 11V9H9.5V7z');
});
test('kebab-vertical', () => {
expect(new SVGPathData('M0 2.5a1.5 1.5 0 103 0 1.5 1.5 0 00-3 0zm0 5a1.5 1.5 0 103 0 1.5 1.5 0 00-3 0zM1.5 14a1.5 1.5 0 110-3 1.5 1.5 0 010 3z')
.toAbs()
.round(1000)
.encode()).toEqual('M0 2.5A1.5 1.5 0 1 0 3 2.5A1.5 1.5 0 0 0 0 2.5zM0 7.5A1.5 1.5 0 1 0 3 7.5A1.5 1.5 0 0 0 0 7.5zM1.5 14A1.5 1.5 0 1 1 1.5 11A1.5 1.5 0 0 1 1.5 14z');
});
test('of kaptinlin bug report unicons voicemail-rectangle', () => {
expect(new SVGPathData('M20 4H4a3 3 0 00-3 3v10a3 3 0 003 3h16a3 3 0 003-3V7a3 3 0 00-3-3zm1 13a1 1 0 01-1 1H4a1 1 0 01-1-1V7a1 1 0 011-1h16a1 1 0 011 1zm-5-8a3 3 0 00-2.82 4h-2.36A3 3 0 108 15h8a3 3 0 000-6zm-8 4a1 1 0 111-1 1 1 0 01-1 1zm8 0a1 1 0 111-1 1 1 0 01-1 1z')
.toAbs()
.round(1000)
.encode()).toEqual('M20 4H4A3 3 0 0 0 1 7V17A3 3 0 0 0 4 20H20A3 3 0 0 0 23 17V7A3 3 0 0 0 20 4zM21 17A1 1 0 0 1 20 18H4A1 1 0 0 1 3 17V7A1 1 0 0 1 4 6H20A1 1 0 0 1 21 7zM16 9A3 3 0 0 0 13.18 13H10.82A3 3 0 1 0 8 15H16A3 3 0 0 0 16 9zM8 13A1 1 0 1 1 9 12A1 1 0 0 1 8 13zM16 13A1 1 0 1 1 17 12A1 1 0 0 1 16 13z');
});
test('of kaptinlin bug report unicons venus', () => {
expect(new SVGPathData('M19 9a7 7 0 10-8 6.92V18h-1a1 1 0 000 2h1v1a1 1 0 002 0v-1h1a1 1 0 000-2h-1v-2.08A7 7 0 0019 9zm-7 5a5 5 0 115-5 5 5 0 01-5 5z')
.toAbs()
.round(1000)
.encode()).toEqual('M19 9A7 7 0 1 0 11 15.92V18H10A1 1 0 0 0 10 20H11V21A1 1 0 0 0 13 21V20H14A1 1 0 0 0 14 18H13V15.92A7 7 0 0 0 19 9zM12 14A5 5 0 1 1 17 9A5 5 0 0 1 12 14z');
});
test('of kaptinlin bug report unicons vector-square', () => {
expect(new SVGPathData('M20 16.18V7.82A3 3 0 1016.18 4H7.82A3 3 0 104 7.82v8.36A3 3 0 107.82 20h8.36A3 3 0 1020 16.18zM19 4a1 1 0 11-1 1 1 1 0 011-1zM5 4a1 1 0 11-1 1 1 1 0 011-1zm0 16a1 1 0 111-1 1 1 0 01-1 1zm11.18-2H7.82A3 3 0 006 16.18V7.82A3 3 0 007.82 6h8.36A3 3 0 0018 7.82v8.36A3 3 0 0016.18 18zM19 20a1 1 0 111-1 1 1 0 01-1 1z')
.toAbs()
.round(1000)
.encode()).toEqual('M20 16.18V7.82A3 3 0 1 0 16.18 4H7.82A3 3 0 1 0 4 7.82V16.18A3 3 0 1 0 7.82 20H16.18A3 3 0 1 0 20 16.18zM19 4A1 1 0 1 1 18 5A1 1 0 0 1 19 4zM5 4A1 1 0 1 1 4 5A1 1 0 0 1 5 4zM5 20A1 1 0 1 1 6 19A1 1 0 0 1 5 20zM16.18 18H7.82A3 3 0 0 0 6 16.18V7.82A3 3 0 0 0 7.82 6H16.18A3 3 0 0 0 18 7.82V16.18A3 3 0 0 0 16.18 18zM19 20A1 1 0 1 1 20 19A1 1 0 0 1 19 20z');
});
test('of kaptinlin bug report unicons user', () => {
expect(new SVGPathData('M15.71 12.71a6 6 0 10-7.42 0 10 10 0 00-6.22 8.18 1 1 0 002 .22 8 8 0 0115.9 0 1 1 0 001 .89h.11a1 1 0 00.88-1.1 10 10 0 00-6.25-8.19zM12 12a4 4 0 114-4 4 4 0 01-4 4z')
.toAbs()
.round(1000)
.encode()).toEqual('M15.71 12.71A6 6 0 1 0 8.29 12.71A10 10 0 0 0 2.07 20.89A1 1 0 0 0 4.07 21.11A8 8 0 0 1 19.97 21.11A1 1 0 0 0 20.97 22H21.08A1 1 0 0 0 21.96 20.9A10 10 0 0 0 15.71 12.71zM12 12A4 4 0 1 1 16 8A4 4 0 0 1 12 12z');
});
test('of kaptinlin bug report unicons user-plus', () => {
expect(new SVGPathData('M21 10.5h-1v-1a1 1 0 00-2 0v1h-1a1 1 0 000 2h1v1a1 1 0 002 0v-1h1a1 1 0 000-2zm-7.7 1.72A4.92 4.92 0 0015 8.5a5 5 0 00-10 0 4.92 4.92 0 001.7 3.72A8 8 0 002 19.5a1 1 0 002 0 6 6 0 0112 0 1 1 0 002 0 8 8 0 00-4.7-7.28zM10 11.5a3 3 0 113-3 3 3 0 01-3 3z')
.toAbs()
.round(1000)
.encode()).toEqual('M21 10.5H20V9.5A1 1 0 0 0 18 9.5V10.5H17A1 1 0 0 0 17 12.5H18V13.5A1 1 0 0 0 20 13.5V12.5H21A1 1 0 0 0 21 10.5zM13.3 12.22A4.92 4.92 0 0 0 15 8.5A5 5 0 0 0 5 8.5A4.92 4.92 0 0 0 6.7 12.22A8 8 0 0 0 2 19.5A1 1 0 0 0 4 19.5A6 6 0 0 1 16 19.5A1 1 0 0 0 18 19.5A8 8 0 0 0 13.3 12.22zM10 11.5A3 3 0 1 1 13 8.5A3 3 0 0 1 10 11.5z');
});
});
});
//# sourceMappingURL=realword.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,187 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('toRel', () => {
test('should work with M commands', () => {
expect(new SVGPathData('M100,100 M110,90 M120,80 M130,70').toRel().encode()).toEqual(new SVGPathData('m100,100 m10,-10 m10,-10 m10,-10').encode());
});
test('should work with m commands', () => {
expect(new SVGPathData('M-100,100m90,-90M20,20M19,19').toRel().encode()).toEqual(new SVGPathData('m-100,100 m90,-90 m30,10 m-1,-1').encode());
});
test('should work with H commands', () => {
expect(new SVGPathData('M0,0 H100 H10 H20 H15').toRel().encode()).toEqual(new SVGPathData('m0,0 h100 h-90 h10 h-5').encode());
});
test('should work with V commands', () => {
expect(new SVGPathData('M0,0 V100 V10 V15V20').toRel().encode()).toEqual(new SVGPathData('m0,0 v100 v-90 v5 v5').encode());
});
test('should work with L commands', () => {
expect(new SVGPathData('M0,0 L100,-100 L1,0 L3,2 L2,1').toRel().encode()).toEqual(new SVGPathData('m0,0 l100,-100 l-99,100 l2,2 l-1,-1').encode());
});
test('should work with C commands', () => {
expect(new SVGPathData(`
M0,0
C100,100 100,100 100,100
C200,200 200,200 200,200
C300,300 300,300 300,300
C400,400 400,400 400,400`)
.toRel()
.encode()).toEqual(new SVGPathData(`
m0,0
c100,100 100,100 100,100
c100,100 100,100 100,100
c100,100 100,100 100,100
c100,100 100,100 100,100`).encode());
});
test('should work with S commands', () => {
expect(new SVGPathData(`
M0,0
S100,100 100,100
S200,200 200,200
S300,300 300,300
S400,400 400,400`)
.toRel()
.encode()).toEqual(new SVGPathData(`
m0,0
s100,100 100,100
s100,100 100,100
s100,100 100,100
s100,100 100,100`).encode());
});
test('should work with Q commands', () => {
expect(new SVGPathData(`M0,0
Q-100,100 -100,100
Q-200,200 -200,200
Q-300,300 -300,300
Q-400,400 -400,400`)
.toRel()
.encode()).toEqual(new SVGPathData(`m0,0
q-100,100 -100,100
q-100,100 -100,100
q-100,100 -100,100
q-100,100 -100,100`).encode());
});
test('should work with T commands', () => {
expect(new SVGPathData(`M0,0 T-100,100 T-200,200 T-190,210 T-180,220`)
.toRel()
.encode()).toEqual(new SVGPathData(`m0,0 t-100,100 t-100,100 t10,10 t10,10`).encode());
});
test('should work with A commands', () => {
expect(new SVGPathData(`
M0,0
A20,20 180 1 0 -100,100
A20,20 180 1 0 -200,200
A20,20 180 1 0 -300,300
A20,20 180 1 0 -400,400`)
.toRel()
.encode()).toEqual(new SVGPathData(`
m0,0
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100`).encode());
});
test('toRel should work with nested commands', () => {
expect(new SVGPathData(`M0 0
A20,20 180 1 0 -100,100
H-90
V110
L-80,120
C-70,130 -60,140 20,220`)
.toRel()
.encode()).toEqual(new SVGPathData(`m0 0
a20,20 180 1 0 -100,100
h10
v10
l10,10
c10,10 20,20 100,100`).encode());
});
test('should work with Z commands', () => {
expect(new SVGPathData(`M10,10 H20 V20 Z V20 H0 Z`).toRel().encode()).toEqual(new SVGPathData(`m10,10 h10 v10 z v10 h-10 z`).encode());
});
});
describe('toAbs', () => {
test('should work with m commands', () => {
expect(new SVGPathData('m-100,100 M10,10 m10,10 m-1,-1').toAbs().encode()).toEqual(new SVGPathData('M-100,100 M10,10 M20,20 M19,19').encode());
});
test('should work with h commands', () => {
expect(new SVGPathData('M0 0 h100 H10 h10 h-5').toAbs().encode()).toEqual(new SVGPathData('M0 0 H100 H10 H20 H15').encode());
});
test('should work with nested commands', () => {
expect(new SVGPathData(`M0 0
a20,20 180 1 0 -100,100
h10
v10
l10,10
c10,10 20,20 100,100`)
.toAbs()
.encode()).toEqual(new SVGPathData(`M0 0
A20,20 180 1 0 -100,100
H-90
V110
L-80,120
C-70,130 -60,140 20,220`).encode());
});
test('should work with a commands', () => {
expect(new SVGPathData(`M0 0
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100
a20,20 180 1 0 -100,100`)
.toAbs()
.encode()).toEqual(new SVGPathData(`M0 0
A20,20 180 1 0 -100,100
A20,20 180 1 0 -200,200
A20,20 180 1 0 -300,300
A20,20 180 1 0 -400,400`).encode());
});
test('should work with t commands', () => {
expect(new SVGPathData('M0 0 t-100,100 t-100,100 t10,10 t10,10')
.toAbs()
.encode()).toEqual(new SVGPathData('M0 0 T-100,100 T-200,200 T-190,210 -180,220').encode());
});
test('should work with v commands', () => {
expect(new SVGPathData('M0 0 v100 V10 v5 v5').toAbs().encode()).toEqual(new SVGPathData('M0 0 V100 V10 V15 V20').encode());
});
test('should work with l commands', () => {
expect(new SVGPathData('M0 0 l100,-100 L1,0 l2,2 l-1,-1').toAbs().encode()).toEqual(new SVGPathData('M0 0 L100,-100 L1,0 L3,2 L2,1').encode());
});
test('should work with c commands', () => {
expect(new SVGPathData(`M0 0
c100,100 100,100 100,100
c100,100 100,100 100,100
c100,100 100,100 100,100
c100,100 100,100 100,100`)
.toAbs()
.encode()).toEqual(new SVGPathData(`M 0 0
C100,100 100,100 100,100
C200,200 200,200 200,200
C300,300 300,300 300,300
C400,400 400,400 400,400`).encode());
});
test('should work with s commands', () => {
expect(new SVGPathData(`M0 0
s100,100 100,100
s100,100 100,100
s100,100 100,100
s100,100 100,100`)
.toAbs()
.encode()).toEqual(new SVGPathData(`M 0 0
S100,100 100,100
S200,200 200,200
S300,300 300,300
S400,400 400,400`).encode());
});
test('should work with q commands', () => {
expect(new SVGPathData(`M0 0
q-100,100 -100,100
q-100,100 -100,100
q-100,100 -100,100
q-100,100 -100,100`)
.toAbs()
.encode()).toEqual(new SVGPathData(`M0 0
Q-100,100 -100,100
Q-200,200 -200,200
Q-300,300 -300,300
Q-400,400 -400,400`).encode());
});
});
//# sourceMappingURL=relabs.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"relabs.test.js","sourceRoot":"","sources":["../../src/tests/relabs.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export declare function testReversePath(input: string, preserveSubpathOrder?: boolean): string;

View File

@@ -0,0 +1,138 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
export function testReversePath(input, preserveSubpathOrder) {
return new SVGPathData(input).reverse(preserveSubpathOrder).encode();
}
describe('Reverse paths', () => {
describe('Valid', () => {
test('empty path', () => {
const input = '';
const expected = '';
expect(testReversePath(input)).toEqual(expected);
});
test('single point path', () => {
const input = 'M10,10';
// A single point path results in just a move command
const expected = 'M10 10';
expect(testReversePath(input)).toEqual(expected);
});
test('simple line path', () => {
const input = 'M10,10 L20,20 L30,10';
const expected = 'M30 10L20 20L10 10';
expect(testReversePath(input)).toEqual(expected);
});
test('horizontal and vertical lines', () => {
const input = 'M10,10 H30 V30 H10';
const expected = 'M10 30H30V10H10';
expect(testReversePath(input)).toEqual(expected);
});
test('closed path (with Z command)', () => {
const input = 'M10,10 L20,20 L30,10 Z';
// The Z command is preserved in the reversed path
const expected = 'M30 10L20 20L10 10z';
expect(testReversePath(input)).toEqual(expected);
});
test('path with cubic bezier curves', () => {
const input = 'M10,10 C20,20 40,20 50,10';
// Reversed path with flipped control points
const expected = 'M50 10C40 20 20 20 10 10';
expect(testReversePath(input)).toEqual(expected);
});
test('path with cubic bezier curve as second command', () => {
const input = 'M10,10 C20,20 30,30 40,10';
const expected = 'M40 10C30 30 20 20 10 10';
expect(testReversePath(input)).toEqual(expected);
});
test('path closed both explicitly and implicitly', () => {
const input = 'M10,10 L20,20 L30,10 L10,10 Z'; // Note: Last point (10,10) matches first point + Z
// Should still reverse correctly and maintain Z
const expected = 'M10 10L30 10L20 20L10 10z';
expect(testReversePath(input)).toEqual(expected);
});
test('path closed only implicitly (without Z command)', () => {
const input = 'M10,10 L20,20 L30,10 L10,10'; // Note: Last point matches first point, but no Z
// Should still reverse correctly and maintain implicit closure
const expected = 'M10 10L30 10L20 20L10 10';
expect(testReversePath(input)).toEqual(expected);
});
test('complex mixed path with multiple command types', () => {
const input = 'M10,10 H30 V30 L40,40 C50,50 60,40 70,30 H80 V20 Z';
const expected = 'M80 20V30H70C60 40 50 50 40 40L30 30V10H10z';
expect(testReversePath(input)).toEqual(expected);
});
test('bezier curve with high precision coordinates (C)', () => {
const input = 'M10.123456789,10.987654321 C20.111222333,20.444555666 40.777888999,20.111222333 50.555666777,10.333222111';
const expected = 'M50.555666777 10.333222111C40.777888999 20.111222333 20.111222333 20.444555666 10.123456789 10.987654321';
expect(testReversePath(input)).toEqual(expected);
});
test('path with multiple subpaths (multiple M and Z commands)', () => {
const input = 'M10,10 L20,20 Z M30,30 L40,40 Z';
const expected = 'M20 20L10 10zM40 40L30 30z';
expect(testReversePath(input)).toEqual(expected);
});
test('path with multiple open subpaths (multiple M commands without Z)', () => {
const input = 'M10,10 L20,20 M30,30 L40,40';
const expected = 'M20 20L10 10M40 40L30 30';
expect(testReversePath(input)).toEqual(expected);
});
test('path with multiple subpaths and reversed subpath order', () => {
const input = 'M10,10 L20,20 Z M30,30 L40,40 Z';
const expected = 'M40 40L30 30zM20 20L10 10z';
expect(testReversePath(input, false)).toEqual(expected);
});
test('path with multiple open subpaths and reversed subpath order', () => {
const input = 'M10,10 L20,20 M30,30 L40,40 M50,50 L60,60';
const expected = 'M60 60L50 50M40 40L30 30M20 20L10 10';
expect(testReversePath(input, false)).toEqual(expected);
});
// New tests for combined HVL commands
test('mixed H, V, and L commands', () => {
const input = 'M10,10 H20 V20 L30,30';
const expected = 'M30 30L20 20V10H10';
expect(testReversePath(input)).toEqual(expected);
});
test('alternating H and V commands', () => {
const input = 'M10,10 H20 V20 H10 V30 H30';
const expected = 'M30 30H10V20H20V10H10';
expect(testReversePath(input)).toEqual(expected);
});
test('H, V, and L commands with varying coordinates', () => {
const input = 'M10,10 H40 V30 L20,40 H10 V10';
const expected = 'M10 10V40H20L40 30V10H10';
expect(testReversePath(input)).toEqual(expected);
});
test('H and L commands in sequence', () => {
const input = 'M10,10 H30 L40,20 H50';
const expected = 'M50 20H40L30 10H10';
expect(testReversePath(input)).toEqual(expected);
});
test('V and L commands in sequence', () => {
const input = 'M10,10 V30 L20,40 V50';
const expected = 'M20 50V40L10 30V10';
expect(testReversePath(input)).toEqual(expected);
});
});
describe('Invalid', () => {
test('throw on relative commands', () => {
const input = 'm10,10 l10,10 l10,-10';
expect(() => testReversePath(input)).toThrow('Relative command are not supported convert first with `toAbs()`');
});
test('throw on quadratic bezier curve (Q)', () => {
const input = 'M10,10 Q25,25 40,10';
expect(() => testReversePath(input)).toThrow('Unsupported command: Q (quadratic bezier)');
});
test('throw on smooth cubic bezier curve (S)', () => {
const input = 'M10,10 S25,25 40,10';
expect(() => testReversePath(input)).toThrow('Unsupported command: S (smooth cubic bezier)');
});
test('throw on smooth quadratic bezier curve (T)', () => {
const input = 'M10,10 T40,10';
expect(() => testReversePath(input)).toThrow('Unsupported command: T (smooth quadratic bezier)');
});
test('throw on arc commands (A)', () => {
const input = 'M10,10 A5,5 0 0 1 20,20';
expect(() => testReversePath(input)).toThrow('Unsupported command: A (arc)');
});
});
});
//# sourceMappingURL=reverse_path.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,62 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Positive rotate from the origin', () => {
test('should fail with no args', () => {
expect(() => new SVGPathData('m20,30l10,10z')
.rotate(undefined)
.encode()).toThrow(new Error('assertNumbers arguments[0] is not a number. undefined == typeof undefined'));
});
test('should work with relative horizontal path', () => {
expect(new SVGPathData('m10 0l60 0z').rotate(Math.PI).round(6).encode()).toEqual('m-10 0l-60 0z');
});
test('should work with relative vertical path', () => {
expect(new SVGPathData('m0 10l0 60z').rotate(Math.PI).round(6).encode()).toEqual('m0 -10l0 -60z');
});
test('should work with relative path', () => {
expect(new SVGPathData('m75 100l0 -50z').rotate(Math.PI).round(6).encode()).toEqual('m-75 -100l0 50z');
});
test('should work with absolute path', () => {
expect(new SVGPathData('M75,100L75,50z').rotate(Math.PI).round(6).encode()).toEqual('M-75 -100L-75 -50z');
});
});
describe('Positive rotate', () => {
test('should work with relative path (Math.PI)', () => {
expect(new SVGPathData('m100 100l100 100z')
.rotate(Math.PI, 150, 150)
.round(6)
.encode()).toEqual('m200 200l-100 -100z');
});
test('should work with relative path (Math.PI/2)', () => {
expect(new SVGPathData('m100 100l100 100z')
.rotate(Math.PI / 2, 150, 150)
.round(6)
.encode()).toEqual('m200 100l-100 100z');
});
test('should work with relative path', () => {
expect(new SVGPathData('m75 100l0 -50z')
.rotate(Math.PI, 75, 75)
.round(6)
.encode()).toEqual('m75 50l0 50z');
});
test('should work with absolute path', () => {
expect(new SVGPathData('M75,100L75,50z')
.rotate(Math.PI, 75, 75)
.round(6)
.encode()).toEqual('M75 50L75 100z');
});
});
describe('360° Positive rotate', () => {
test('should work with relative path', () => {
expect(new SVGPathData('m100 75l-50 -45l0 90z')
.rotate(2 * Math.PI, 75, 75)
.round(6)
.encode()).toEqual('m100 75l-50 -45l0 90z');
});
test('should work with absolute path', () => {
expect(new SVGPathData('M 100,75L50,30L50,120 z')
.rotate(2 * Math.PI, 75, 75)
.round(6)
.encode()).toEqual('M100 75L50 30L50 120z');
});
});
//# sourceMappingURL=rotate.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"rotate.test.js","sourceRoot":"","sources":["../../src/tests/rotate.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,10 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Path rounding', () => {
test('should work', () => {
expect(new SVGPathData('M137.5 140C137.5 140 129.86983 107.04527999999999 120.28202 94.914299C120.28202 94.914299 111.67409 87.871064 107.37013999999999 87.284128C103.06618 86.69719099999999 76.261082 86.11025599999999 76.261082 86.11025599999999L68.239383 76.289234H64.522357L63.348485 60.47904L66.870103 56.762015999999996L68.434791 46.979513L67.847854 42.479433H66.087046C66.087046 42.479433 64.948119 29.761523 65.109294 28.391533000000003C65.269757 27.022253000000003 60.197715 12.206213000000002 47.48052000000001 10.215053000000001C34.76333000000001 8.223173000000001 12.446920000000006 20.723563 12.446920000000006 20.723563L13.652170000000012 42.282593L15.999920000000003 48.935013L14.63064 49.326543C14.63064 49.326543 14.826040000000006 53.043563 16.391449999999992 55.586716C16.391449999999992 55.586716 18.543779999999998 66.34769 24.021619999999984 66.543098L28.326289999999986 79.064881L32.04331999999998 83.174148L34.97799999999998 91.78207L20.30458999999999 109.587L19.130719999999997 119.95644C19.130719999999997 119.95644 10.603379999999987 127.56165 12.812780000000004 139.99858H137.5z')
.round(10e12)
.encode()).toEqual('M137.5 140C137.5 140 129.86983 107.04528 120.28202 94.914299C120.28202 94.914299 111.67409 87.871064 107.37014 87.284128C103.06618 86.697191 76.261082 86.110256 76.261082 86.110256L68.239383 76.289234H64.522357L63.348485 60.47904L66.870103 56.762016L68.434791 46.979513L67.847854 42.479433H66.087046C66.087046 42.479433 64.948119 29.761523 65.109294 28.391533C65.269757 27.022253 60.197715 12.206213 47.48052 10.215053C34.76333 8.223173 12.44692 20.723563 12.44692 20.723563L13.65217 42.282593L15.99992 48.935013L14.63064 49.326543C14.63064 49.326543 14.82604 53.043563 16.39145 55.586716C16.39145 55.586716 18.54378 66.34769 24.02162 66.543098L28.32629 79.064881L32.04332 83.174148L34.978 91.78207L20.30459 109.587L19.13072 119.95644C19.13072 119.95644 10.60338 127.56165 12.81278 139.99858H137.5z');
});
});
//# sourceMappingURL=round.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"round.test.js","sourceRoot":"","sources":["../../src/tests/round.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE;QACvB,MAAM,CACJ,IAAI,WAAW,CACb,2jCAA2jC,CAC5jC;aACE,KAAK,CAAC,KAAK,CAAC;aACZ,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,gyBAAgyB,CACjyB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,44 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('normalization of curves', () => {
test('should clear zero length line segments', () => {
expect(new SVGPathData('M 10 10 h 0 h 10 H 20 H 30 v 0 v 10 V 20 V 30 l 0 0 l 10 10 L 40 40 L 50 50')
.sanitize()
.encode()).toEqual(new SVGPathData('M 10 10 h 10 H 30 v 10 V 30 l 10 10 L 50 50').encode());
});
test('should clear zero length quadratic curves', () => {
expect(new SVGPathData('M 10 10 t 0 0 t 10 10 t 0 0 t 0 0').sanitize().encode()).toEqual(new SVGPathData('M 10 10 t 10 10 t 0 0 t 0 0').encode());
});
test('should clear zero length quadratic curves (absolute)', () => {
expect(new SVGPathData('M 10 10 T 10 10 T 20 20 T 20 20 T 20 20')
.sanitize()
.encode()).toEqual(new SVGPathData('M 10 10 T 20 20 T 20 20 T 20 20').encode());
});
test('should clear zero length cubic curves', () => {
expect(new SVGPathData('M 10 10 C 10 10 10 10 10 10 c 0 0 0 0 0 0 S 10 10 10 10 s 0 0 0 0')
.sanitize()
.encode()).toEqual(new SVGPathData('M 10 10').encode());
});
test('should clear zero length arc curves', () => {
expect(new SVGPathData(`
M 10 10
A 20,30 0 1 0 10,10
A 20,30 0 0 0 10,10
a 20,30 0 0 0 0,0
a 20,30 0 0 0 10,10
`)
.sanitize()
.encode()).toEqual(new SVGPathData(`
M 10 10
A 20,30 0 1 0 10,10
a 20,30 0 0 0 10,10
`).encode());
});
test('should correctly handle first control point from smooth curve', () => {
expect(new SVGPathData('M 10 10 s 10 10 20 10 s 0 0 0 0').sanitize().encode()).toEqual(new SVGPathData('M 10 10 s 10 10 20 10 s 0 0 0 0').encode());
});
test('should remove 0-length Zz', () => {
expect(new SVGPathData('M 10 10 h 10 h -10 Z').sanitize().encode()).toEqual(new SVGPathData('M 10 10 h 10 h -10').encode());
});
});
//# sourceMappingURL=sanitize.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"sanitize.test.js","sourceRoot":"","sources":["../../src/tests/sanitize.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,MAAM,CACJ,IAAI,WAAW,CACb,6EAA6E,CAC9E;aACE,QAAQ,EAAE;aACV,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CAAC,6CAA6C,CAAC,CAAC,MAAM,EAAE,CACxE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACrD,MAAM,CACJ,IAAI,WAAW,CAAC,mCAAmC,CAAC,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,CACzE,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,6BAA6B,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAChE,MAAM,CACJ,IAAI,WAAW,CAAC,yCAAyC,CAAC;aACvD,QAAQ,EAAE;aACV,MAAM,EAAE,CACZ,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,iCAAiC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACjD,MAAM,CACJ,IAAI,WAAW,CACb,mEAAmE,CACpE;aACE,QAAQ,EAAE;aACV,MAAM,EAAE,CACZ,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC/C,MAAM,CACJ,IAAI,WAAW,CAAC;;;;;;OAMf,CAAC;aACC,QAAQ,EAAE;aACV,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,WAAW,CAAC;;;;OAIf,CAAC,CAAC,MAAM,EAAE,CACZ,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACzE,MAAM,CACJ,IAAI,WAAW,CAAC,iCAAiC,CAAC,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,CACvE,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,iCAAiC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,IAAI,WAAW,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CACzE,IAAI,WAAW,CAAC,oBAAoB,CAAC,CAAC,MAAM,EAAE,CAC/C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,16 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Positive scale', () => {
test('should fail with no args', () => {
expect(() => new SVGPathData('m20,30l10,10z')
.scale(undefined)
.encode()).toThrow(new Error('assertNumbers arguments[0] is not a number. undefined == typeof undefined'));
});
test('should work with relative path', () => {
expect(new SVGPathData('m20 30c0 0 10 20 15 30z').scale(10, 10).encode()).toEqual('m200 300c0 0 100 200 150 300z');
});
test('should work with absolute path', () => {
expect(new SVGPathData('M20 30C0 0 10 20 15 30z').scale(10, 10).encode()).toEqual('M200 300C0 0 100 200 150 300z');
});
});
//# sourceMappingURL=scale.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"scale.test.js","sourceRoot":"","sources":["../../src/tests/scale.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,GAAG,EAAE,CACV,IAAI,WAAW,CAAC,eAAe,CAAC;aAC7B,KAAK,CAAC,SAA8B,CAAC;aACrC,MAAM,EAAE,CACZ,CAAC,OAAO,CACP,IAAI,KAAK,CACP,2EAA2E,CAC5E,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC1C,MAAM,CACJ,IAAI,WAAW,CAAC,yBAAyB,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAClE,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC1C,MAAM,CACJ,IAAI,WAAW,CAAC,yBAAyB,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAClE,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,44 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
function degToRad(deg) {
return deg * (Math.PI / 180);
}
describe('X axis skew', () => {
test('should fail with bad args', () => {
expect(() => new SVGPathData('m20,30l10,10z')
.skewX(undefined)
.encode()).toThrow(new Error('assertNumbers arguments[0] is not a number. undefined == typeof undefined'));
});
test('should work with relative path | 90deg', () => {
expect(new SVGPathData('m100 75l-50 -45l0 90z').skewX(degToRad(90)).encode()).toEqual('m1224842951489652700 75l-734905770893791600 -45l1469811541787583200 90z');
});
test('should work with absolute path | 90deg', () => {
expect(new SVGPathData('M 100,75 50,30 50,120 z').skewX(degToRad(90)).encode()).toEqual('M1224842951489652700 75L489937180595861200 30L1959748722383444500 120z');
});
test('should work with relative path | 30deg', () => {
expect(new SVGPathData('m100 75l-50 -45l0 90z').skewX(degToRad(30)).encode()).toEqual('m143.30127018922192 75l-75.98076211353316 -45l51.96152422706631 90z');
});
test('should work with absolute path | 30deg', () => {
expect(new SVGPathData('M 100,75 50,30 50,120 z').skewX(degToRad(30)).encode()).toEqual('M143.30127018922192 75L67.32050807568876 30L119.28203230275508 120z');
});
});
describe('Y axis skew', () => {
test('should fail with bad args', () => {
expect(() => new SVGPathData('m20,30l10,10z')
.skewY(undefined)
.encode()).toThrow(new Error('assertNumbers arguments[0] is not a number. undefined == typeof undefined'));
});
test('should work with relative path | 90deg', () => {
expect(new SVGPathData('m100 75l-50 -45l0 90z').skewY(degToRad(90)).encode()).toEqual('m100 1633123935319537000l-50 -816561967659768400l0 90z');
});
test('should work with absolute path | 90deg', () => {
expect(new SVGPathData('M 100,75 50,30 50,120 z').skewY(degToRad(90)).encode()).toEqual('M100 1633123935319537000L50 816561967659768400L50 816561967659768600z');
});
test('should work with relative path | 30deg', () => {
expect(new SVGPathData('m100 75l-50 -45l0 90z').skewY(degToRad(30)).encode()).toEqual('m100 132.73502691896257l-50 -73.86751345948129l0 90z');
});
test('should work with absolute path | 30deg', () => {
expect(new SVGPathData('M 100,75 50,30 50,120 z').skewY(degToRad(30)).encode()).toEqual('M100 132.73502691896257L50 58.86751345948129L50 148.8675134594813z');
});
});
//# sourceMappingURL=skew.test.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"skew.test.js","sourceRoot":"","sources":["../../src/tests/skew.test.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,82 @@
import { describe, test, expect } from '@jest/globals';
import { SVGPathData } from '../index.js';
describe('Parsing smooth curve to commands', () => {
test('should not work when badly declared', () => {
expect(() => new SVGPathData('S')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('S10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('S10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('S10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('S10 10 10 10 10 10')).toThrow(new SyntaxError('Unterminated command at the path end.'));
expect(() => new SVGPathData('S10 10 10S10 10 10 10')).toThrow(new SyntaxError('Unterminated command at index 9.'));
});
test('should work with comma separated coordinates', () => {
const commands = new SVGPathData('S123,456 789,987').commands;
expect(commands[0].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x2).toEqual(123);
expect(commands[0].y2).toEqual(456);
expect(commands[0].x).toEqual(789);
expect(commands[0].y).toEqual(987);
});
test('should work with space separated coordinates', () => {
const commands = new SVGPathData('S123 456 789 987').commands;
expect(commands[0].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x2).toEqual(123);
expect(commands[0].y2).toEqual(456);
expect(commands[0].x).toEqual(789);
expect(commands[0].y).toEqual(987);
});
test('should work with nested separated complexer coordinate pairs', () => {
const commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x2).toEqual(-10.0032e-5);
expect(commands[0].y2).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
});
test('should work with multiple pairs of coordinates', () => {
const commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x2).toEqual(-10.0032e-5);
expect(commands[0].y2).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[1].relative).toEqual(false);
expect(commands[1].x2).toEqual(-10.0032e-5);
expect(commands[1].y2).toEqual(-20.0032e-5);
expect(commands[1].x).toEqual(-30.0032e-5);
expect(commands[1].y).toEqual(-40.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x2).toEqual(-10.0032e-5);
expect(commands[2].y2).toEqual(-20.0032e-5);
expect(commands[2].x).toEqual(-30.0032e-5);
expect(commands[2].y).toEqual(-40.0032e-5);
});
test('should work with multiple declared pairs of coordinates', () => {
const commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 s-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
expect(commands[0].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[0].relative).toEqual(false);
expect(commands[0].x2).toEqual(-10.0032e-5);
expect(commands[0].y2).toEqual(-20.0032e-5);
expect(commands[0].x).toEqual(-30.0032e-5);
expect(commands[0].y).toEqual(-40.0032e-5);
expect(commands[1].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[1].relative).toEqual(true);
expect(commands[1].x2).toEqual(-10.0032e-5);
expect(commands[1].y2).toEqual(-20.0032e-5);
expect(commands[1].x).toEqual(-30.0032e-5);
expect(commands[1].y).toEqual(-40.0032e-5);
expect(commands[2].type).toEqual(SVGPathData.SMOOTH_CURVE_TO);
expect(commands[2].relative).toEqual(false);
expect(commands[2].x2).toEqual(-10.0032e-5);
expect(commands[2].y2).toEqual(-20.0032e-5);
expect(commands[2].x).toEqual(-30.0032e-5);
expect(commands[2].y).toEqual(-40.0032e-5);
});
});
//# sourceMappingURL=smoothcurveto.test.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export {};

Some files were not shown because too many files have changed in this diff Show More