PathLang = P.createLanguage({
PathString: r => (
P.alt(
P.seq(
P.alt(
r.MoveTo,
r.MoveToDelta
),
P.seq(
r.Sep.atLeast(0),
r.AnyCommand
).many(),
r.Sep.atLeast(0)
).map(([moveto, rest]) =>
Path.path([moveto, ...rest.map(([sep, comm]) => comm)])),
P.string('')
.map(() => Path.path([]))
)
),
AnyCommand: r => (
P.alt(
r.MoveTo,
r.MoveToDelta,
r.ClosePath,
r.LineTo,
r.LineToDelta,
r.LineToH,
r.LineToHDelta,
r.LineToV,
r.LineToVDelta,
r.CubicCurve,
r.CubicCurveDelta,
r.SmoothCurve,
r.SmoothCurveDelta,
r.QuadraticCurve,
r.QuadraticCurveDelta,
r.SmoothQuadraticCurve,
r.SmoothQuadraticCurveDelta,
r.Arc,
r.ArcDelta
)
),
ClosePath: r => (
P.oneOf("zZ")
.map(Path.closePath)
),
MoveTo: r => (
P.seq(
P.string('M'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,coordinates]) => {
const [x,,y] = coordinates[0];
return coordinates.length === 1
? Path.moveTo(x, y)
: Path.path(coordinates.map(([x,,y]) => Path.lineTo(x, y)));
})
),
MoveToDelta: r => (
P.seq(
P.string('m'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,coordinates]) => {
const [dx,,dy] = coordinates[0];
return coordinates.length === 1
? Path.moveToDelta(dx, dy)
: Path.path([Path.moveToDelta(dx, dy),
...coordinates.slice(1).map(([dx,,dy]) => Path.lineToDelta(dx, dy))]);
})
),
LineTo: r => (
P.seq(
P.string('L'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,coordinates]) => {
const [x,,y] = coordinates[0];
return coordinates.length === 1
? Path.lineTo(x, y)
: Path.path(coordinates.map(([x,,y]) => Path.lineTo(x, y)));
})
),
LineToDelta: r => (
P.seq(
P.string('l'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,coordinates]) => {
const [dx,,dy] = coordinates[0];
return coordinates.length === 1
? Path.lineToDelta(dx, dy)
: Path.path(coordinates.map(([dx,,dy]) => Path.lineToDelta(dx, dy)));
})
),
LineToH: r => (
P.seq(
P.string('H'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,xs]) => {
const [x] = xs[0];
return xs.length === 1
? Path.lineToH(x)
: Path.path(xs.map(([x]) => Path.lineToH(x)));
})
),
LineToHDelta: r => (
P.seq(
P.string('h'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,dxs]) => {
const [dx] = dxs[0];
return dxs.length === 1
? Path.lineToHDelta(dx)
: Path.path(dxs.map(([dx]) => Path.lineToHDelta(dx)));
})
),
LineToV: r => (
P.seq(
P.string('V'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,ys]) => {
const [y] = ys[0];
return ys.length === 1
? Path.lineToV(y)
: Path.path(ys.map(([y]) => Path.lineToV(y)));
})
),
LineToVDelta: r => (
P.seq(
P.string('v'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,dys]) => {
const [dy] = dys[0];
return dys.length === 1
? Path.lineToVDelta(dy)
: Path.path(dys.map(([dy]) => Path.lineToVDelta(dy)));
})
),
// C x1 y1, x2 y2, x y
// (or)
// c dx1 dy1, dx2 dy2, dx dy
CubicCurve: r => (
P.seq(
P.string('C'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
return Path.path(parameterSets.map(
([controlP1X,,controlP1Y,,controlP2X,,controlP2Y,,x,,y]) => {
return Path.curve(controlP1X, controlP1Y, controlP2X, controlP2Y, x, y);
}
));
})
),
CubicCurveDelta: r => (
P.seq(
P.string('c'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
return Path.path(parameterSets.map(
([controlP1dX,,controlP1dY,,controlP2dX,,controlP2dY,,dx,,dy]) => (
Path.curveDelta(controlP1dX, controlP1dY, controlP2dX, controlP2dY, dx, dy)
)
));
//return Path.curveDelta(controlP1dX, controlP1dY, controlP2dX, controlP2dY, dx, dy);
})
),
// S x2 y2, x y
// (or)
// s dx2 dy2, dx dy
SmoothCurve: r => (
P.seq(
P.string('S'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [controlP2X,,controlP2Y,,x,,y] = parameterSets[0];
return parameterSets.length === 1
? Path.smoothCurve(controlP2X, controlP2Y, x, y)
: Path.path(parameterSets.map(([controlP2X,,controlP2Y,,x,,y]) => Path.smoothCurve(controlP2X, controlP2Y, x, y)));
})
),
SmoothCurveDelta: r => (
P.seq(
P.string('s'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [controlP2dX,,controlP2dY,,dx,,dy] = parameterSets[0];
return parameterSets.length === 1
? Path.smoothCurveDelta(controlP2dX, controlP2dY, dx, dy)
: Path.path(parameterSets.map(([controlP2dX,,controlP2dY,,dx,,dy]) => Path.smoothCurveDelta(controlP2dX, controlP2dY, dx, dy)));
})
),
QuadraticCurve: r => (
P.seq(
P.string('Q'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [controlPX,,controlPY,,x,,y] = parameterSets[0];
return parameterSets.length === 1
? Path.quadraticCurve(controlPX, controlPY, x, y)
: Path.path(parameterSets.map(([controlPX,,controlPY,,x,,y]) => Path.quadraticCurve(controlPX, controlPY, x, y)));
})
),
QuadraticCurveDelta: r => (
P.seq(
P.string('q'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [controlPdX,,controlPdY,,dx,,dy] = parameterSets[0];
return parameterSets.length === 1
? Path.quadraticCurveDelta(controlPdX, controlPdY, dx, dy)
: Path.path(parameterSets.map(([controlPdX,,controlPdY,,dx,,dy]) => Path.quadraticCurveDelta(controlPdX, controlPdY, dx, dy)));
})
),
SmoothQuadraticCurve: r => (
P.seq(
P.string('T'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [x,,y] = parameterSets[0];
return parameterSets.length === 1
? Path.smoothQuadraticCurve(x, y)
: Path.path(parameterSets.map(([x,,y]) => Path.smoothQuadraticCurve(x, y)))
})
),
SmoothQuadraticCurveDelta: r => (
P.seq(
P.string('t'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [dx,,dy] = parameterSets[0];
return parameterSets.length === 1
? Path.smoothQuadraticCurveDelta(dx, dy)
: Path.path(parameterSets.map(([dx,,dy]) => Path.smoothQuadraticCurveDelta(dx, dy)));
})
),
// A rx ry x-axis-rotation large-arc-flag sweep-flag x y
// a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy
// ADT.Product('arc', ['xRadius', 'yRadius', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'x', 'y']),
// ADT.Product('arcDelta', ['xRadius', 'yRadius', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'dx', 'dy'])
Arc: r => (
P.seq(
P.string('A'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Flag, r.Sep.times(0, 1),
r.Flag, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [xRadius,,yRadius,,xAxisRotation,,largeArcFlag,,sweepFlag,,x,,y] = parameterSets[0];
return parameterSets.length === 1
? Path.arc(xRadius, yRadius, xAxisRotation, largeArcFlag, sweepFlag, x, y)
: Path.path(parameterSets.map(([xRadius,,yRadius,,xAxisRotation,,largeArcFlag,,sweepFlag,,x,,y]) => {
Path.arc(xRadius, yRadius, xAxisRotation, largeArcFlag, sweepFlag, x, y)
}))
})
),
ArcDelta: r => (
P.seq(
P.string('a'), r.Sep.times(0, 1),
P.seq(
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Flag, r.Sep.times(0, 1),
r.Flag, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1),
r.Num, r.Sep.times(0, 1)
).atLeast(1)
).map(([,,parameterSets]) => {
const [xRadius,,yRadius,,xAxisRotation,,largeArcFlag,,sweepFlag,,dx,,dy] = parameterSets[0];
return parameterSets.length === 1
? Path.arcDelta(xRadius, yRadius, xAxisRotation, largeArcFlag, sweepFlag, dx, dy)
: Path.path(parameterSets.map(([xRadius,,yRadius,,xAxisRotation,,largeArcFlag,,sweepFlag,,dx,,dy]) => Path.arcDelta(xRadius, yRadius, xAxisRotation, largeArcFlag, sweepFlag, dx, dy)))
})
),
Num: r => (
// P.regexp(/[+-]?([0-9]*[.])?[0-9]+/)
P.regexp(/((?:-?\d*)\.?\d+[eE][-\+]?\d+)|[+-]?([0-9]*[.])?[0-9]+/)
.map(Number)
),
Sep: r => (
P.regexp(/(\s)|(\s+,\s*)|(\s*,\s+)|(,)/)
),
Flag: r => (
P.oneOf('01')
)
})