mirror of
https://github.com/rough-stuff/rough.git
synced 2026-01-14 17:07:58 -05:00
598 lines
25 KiB
JavaScript
598 lines
25 KiB
JavaScript
import { RoughPath, RoughArcConverter, PathFitter } from './path.js';
|
|
import { getFiller } from './fillers/filler';
|
|
export class RoughRenderer {
|
|
line(x1, y1, x2, y2, o) {
|
|
const ops = this.doubleLine(x1, y1, x2, y2, o);
|
|
return { type: 'path', ops };
|
|
}
|
|
linearPath(points, close, o) {
|
|
const len = (points || []).length;
|
|
if (len > 2) {
|
|
let ops = [];
|
|
for (let i = 0; i < (len - 1); i++) {
|
|
ops = ops.concat(this.doubleLine(points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], o));
|
|
}
|
|
if (close) {
|
|
ops = ops.concat(this.doubleLine(points[len - 1][0], points[len - 1][1], points[0][0], points[0][1], o));
|
|
}
|
|
return { type: 'path', ops };
|
|
}
|
|
else if (len === 2) {
|
|
return this.line(points[0][0], points[0][1], points[1][0], points[1][1], o);
|
|
}
|
|
return { type: 'path', ops: [] };
|
|
}
|
|
polygon(points, o) {
|
|
return this.linearPath(points, true, o);
|
|
}
|
|
rectangle(x, y, width, height, o) {
|
|
const points = [
|
|
[x, y], [x + width, y], [x + width, y + height], [x, y + height]
|
|
];
|
|
return this.polygon(points, o);
|
|
}
|
|
curve(points, o) {
|
|
const o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o);
|
|
const o2 = this._curveWithOffset(points, 1.5 * (1 + o.roughness * 0.22), o);
|
|
return { type: 'path', ops: o1.concat(o2) };
|
|
}
|
|
ellipse(x, y, width, height, o) {
|
|
const increment = (Math.PI * 2) / o.curveStepCount;
|
|
let rx = Math.abs(width / 2);
|
|
let ry = Math.abs(height / 2);
|
|
rx += this.getOffset(-rx * 0.05, rx * 0.05, o);
|
|
ry += this.getOffset(-ry * 0.05, ry * 0.05, o);
|
|
const o1 = this._ellipse(increment, x, y, rx, ry, 1, increment * this.getOffset(0.1, this.getOffset(0.4, 1, o), o), o);
|
|
const o2 = this._ellipse(increment, x, y, rx, ry, 1.5, 0, o);
|
|
return { type: 'path', ops: o1.concat(o2) };
|
|
}
|
|
arc(x, y, width, height, start, stop, closed, roughClosure, o) {
|
|
const cx = x;
|
|
const cy = y;
|
|
let rx = Math.abs(width / 2);
|
|
let ry = Math.abs(height / 2);
|
|
rx += this.getOffset(-rx * 0.01, rx * 0.01, o);
|
|
ry += this.getOffset(-ry * 0.01, ry * 0.01, o);
|
|
let strt = start;
|
|
let stp = stop;
|
|
while (strt < 0) {
|
|
strt += Math.PI * 2;
|
|
stp += Math.PI * 2;
|
|
}
|
|
if ((stp - strt) > (Math.PI * 2)) {
|
|
strt = 0;
|
|
stp = Math.PI * 2;
|
|
}
|
|
const ellipseInc = (Math.PI * 2) / o.curveStepCount;
|
|
const arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2);
|
|
const o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o);
|
|
const o2 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1.5, o);
|
|
let ops = o1.concat(o2);
|
|
if (closed) {
|
|
if (roughClosure) {
|
|
ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(strt), cy + ry * Math.sin(strt), o));
|
|
ops = ops.concat(this.doubleLine(cx, cy, cx + rx * Math.cos(stp), cy + ry * Math.sin(stp), o));
|
|
}
|
|
else {
|
|
ops.push({ op: 'lineTo', data: [cx, cy] });
|
|
ops.push({ op: 'lineTo', data: [cx + rx * Math.cos(strt), cy + ry * Math.sin(strt)] });
|
|
}
|
|
}
|
|
return { type: 'path', ops };
|
|
}
|
|
svgPath(path, o) {
|
|
path = (path || '').replace(/\n/g, ' ').replace(/(-\s)/g, '-').replace('/(\s\s)/g', ' ');
|
|
let p = new RoughPath(path);
|
|
if (o.simplification) {
|
|
const fitter = new PathFitter(p.linearPoints, p.closed);
|
|
const d = fitter.fit(o.simplification);
|
|
p = new RoughPath(d);
|
|
}
|
|
let ops = [];
|
|
const segments = p.segments || [];
|
|
for (let i = 0; i < segments.length; i++) {
|
|
const s = segments[i];
|
|
const prev = i > 0 ? segments[i - 1] : null;
|
|
const opList = this._processSegment(p, s, prev, o);
|
|
if (opList && opList.length) {
|
|
ops = ops.concat(opList);
|
|
}
|
|
}
|
|
return { type: 'path', ops };
|
|
}
|
|
solidFillPolygon(points, o) {
|
|
const ops = [];
|
|
if (points.length) {
|
|
const offset = o.maxRandomnessOffset || 0;
|
|
const len = points.length;
|
|
if (len > 2) {
|
|
ops.push({ op: 'move', data: [points[0][0] + this.getOffset(-offset, offset, o), points[0][1] + this.getOffset(-offset, offset, o)] });
|
|
for (let i = 1; i < len; i++) {
|
|
ops.push({ op: 'lineTo', data: [points[i][0] + this.getOffset(-offset, offset, o), points[i][1] + this.getOffset(-offset, offset, o)] });
|
|
}
|
|
}
|
|
}
|
|
return { type: 'fillPath', ops };
|
|
}
|
|
patternFillPolygon(points, o) {
|
|
const filler = getFiller(this, o);
|
|
return filler.fillPolygon(points, o);
|
|
}
|
|
patternFillEllipse(cx, cy, width, height, o) {
|
|
const filler = getFiller(this, o);
|
|
return filler.fillEllipse(cx, cy, width, height, o);
|
|
}
|
|
patternFillArc(x, y, width, height, start, stop, o) {
|
|
const cx = x;
|
|
const cy = y;
|
|
let rx = Math.abs(width / 2);
|
|
let ry = Math.abs(height / 2);
|
|
rx += this.getOffset(-rx * 0.01, rx * 0.01, o);
|
|
ry += this.getOffset(-ry * 0.01, ry * 0.01, o);
|
|
let strt = start;
|
|
let stp = stop;
|
|
while (strt < 0) {
|
|
strt += Math.PI * 2;
|
|
stp += Math.PI * 2;
|
|
}
|
|
if ((stp - strt) > (Math.PI * 2)) {
|
|
strt = 0;
|
|
stp = Math.PI * 2;
|
|
}
|
|
const increment = (stp - strt) / o.curveStepCount;
|
|
const points = [];
|
|
for (let angle = strt; angle <= stp; angle = angle + increment) {
|
|
points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]);
|
|
}
|
|
points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]);
|
|
points.push([cx, cy]);
|
|
return this.patternFillPolygon(points, o);
|
|
}
|
|
///
|
|
getOffset(min, max, ops) {
|
|
return ops.roughness * ((Math.random() * (max - min)) + min);
|
|
}
|
|
doubleLine(x1, y1, x2, y2, o) {
|
|
const o1 = this._line(x1, y1, x2, y2, o, true, false);
|
|
const o2 = this._line(x1, y1, x2, y2, o, true, true);
|
|
return o1.concat(o2);
|
|
}
|
|
_line(x1, y1, x2, y2, o, move, overlay) {
|
|
const lengthSq = Math.pow((x1 - x2), 2) + Math.pow((y1 - y2), 2);
|
|
let offset = o.maxRandomnessOffset || 0;
|
|
if ((offset * offset * 100) > lengthSq) {
|
|
offset = Math.sqrt(lengthSq) / 10;
|
|
}
|
|
const halfOffset = offset / 2;
|
|
const divergePoint = 0.2 + Math.random() * 0.2;
|
|
let midDispX = o.bowing * o.maxRandomnessOffset * (y2 - y1) / 200;
|
|
let midDispY = o.bowing * o.maxRandomnessOffset * (x1 - x2) / 200;
|
|
midDispX = this.getOffset(-midDispX, midDispX, o);
|
|
midDispY = this.getOffset(-midDispY, midDispY, o);
|
|
const ops = [];
|
|
if (move) {
|
|
if (overlay) {
|
|
ops.push({
|
|
op: 'move', data: [
|
|
x1 + this.getOffset(-halfOffset, halfOffset, o),
|
|
y1 + this.getOffset(-halfOffset, halfOffset, o)
|
|
]
|
|
});
|
|
}
|
|
else {
|
|
ops.push({
|
|
op: 'move', data: [
|
|
x1 + this.getOffset(-offset, offset, o),
|
|
y1 + this.getOffset(-offset, offset, o)
|
|
]
|
|
});
|
|
}
|
|
}
|
|
if (overlay) {
|
|
ops.push({
|
|
op: 'bcurveTo', data: [
|
|
midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o),
|
|
midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o),
|
|
midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o),
|
|
midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-halfOffset, halfOffset, o),
|
|
x2 + this.getOffset(-halfOffset, halfOffset, o),
|
|
y2 + this.getOffset(-halfOffset, halfOffset, o)
|
|
]
|
|
});
|
|
}
|
|
else {
|
|
ops.push({
|
|
op: 'bcurveTo', data: [
|
|
midDispX + x1 + (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o),
|
|
midDispY + y1 + (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o),
|
|
midDispX + x1 + 2 * (x2 - x1) * divergePoint + this.getOffset(-offset, offset, o),
|
|
midDispY + y1 + 2 * (y2 - y1) * divergePoint + this.getOffset(-offset, offset, o),
|
|
x2 + this.getOffset(-offset, offset, o),
|
|
y2 + this.getOffset(-offset, offset, o)
|
|
]
|
|
});
|
|
}
|
|
return ops;
|
|
}
|
|
_curve(points, closePoint, o) {
|
|
const len = points.length;
|
|
let ops = [];
|
|
if (len > 3) {
|
|
const b = [];
|
|
const s = 1 - o.curveTightness;
|
|
ops.push({ op: 'move', data: [points[1][0], points[1][1]] });
|
|
for (let i = 1; (i + 2) < len; i++) {
|
|
const cachedVertArray = points[i];
|
|
b[0] = [cachedVertArray[0], cachedVertArray[1]];
|
|
b[1] = [cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6, cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6];
|
|
b[2] = [points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6, points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6];
|
|
b[3] = [points[i + 1][0], points[i + 1][1]];
|
|
ops.push({ op: 'bcurveTo', data: [b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1]] });
|
|
}
|
|
if (closePoint && closePoint.length === 2) {
|
|
const ro = o.maxRandomnessOffset;
|
|
ops.push({ op: 'lineTo', data: [closePoint[0] + this.getOffset(-ro, ro, o), closePoint[1] + +this.getOffset(-ro, ro, o)] });
|
|
}
|
|
}
|
|
else if (len === 3) {
|
|
ops.push({ op: 'move', data: [points[1][0], points[1][1]] });
|
|
ops.push({
|
|
op: 'bcurveTo', data: [
|
|
points[1][0], points[1][1],
|
|
points[2][0], points[2][1],
|
|
points[2][0], points[2][1]
|
|
]
|
|
});
|
|
}
|
|
else if (len === 2) {
|
|
ops = ops.concat(this.doubleLine(points[0][0], points[0][1], points[1][0], points[1][1], o));
|
|
}
|
|
return ops;
|
|
}
|
|
_ellipse(increment, cx, cy, rx, ry, offset, overlap, o) {
|
|
const radOffset = this.getOffset(-0.5, 0.5, o) - (Math.PI / 2);
|
|
const points = [];
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
|
|
this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
|
|
]);
|
|
for (let angle = radOffset; angle < (Math.PI * 2 + radOffset - 0.01); angle = angle + increment) {
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle),
|
|
this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle)
|
|
]);
|
|
}
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + rx * Math.cos(radOffset + Math.PI * 2 + overlap * 0.5),
|
|
this.getOffset(-offset, offset, o) + cy + ry * Math.sin(radOffset + Math.PI * 2 + overlap * 0.5)
|
|
]);
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + 0.98 * rx * Math.cos(radOffset + overlap),
|
|
this.getOffset(-offset, offset, o) + cy + 0.98 * ry * Math.sin(radOffset + overlap)
|
|
]);
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset + overlap * 0.5),
|
|
this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset + overlap * 0.5)
|
|
]);
|
|
return this._curve(points, null, o);
|
|
}
|
|
_curveWithOffset(points, offset, o) {
|
|
const ps = [];
|
|
ps.push([
|
|
points[0][0] + this.getOffset(-offset, offset, o),
|
|
points[0][1] + this.getOffset(-offset, offset, o),
|
|
]);
|
|
ps.push([
|
|
points[0][0] + this.getOffset(-offset, offset, o),
|
|
points[0][1] + this.getOffset(-offset, offset, o),
|
|
]);
|
|
for (let i = 1; i < points.length; i++) {
|
|
ps.push([
|
|
points[i][0] + this.getOffset(-offset, offset, o),
|
|
points[i][1] + this.getOffset(-offset, offset, o),
|
|
]);
|
|
if (i === (points.length - 1)) {
|
|
ps.push([
|
|
points[i][0] + this.getOffset(-offset, offset, o),
|
|
points[i][1] + this.getOffset(-offset, offset, o),
|
|
]);
|
|
}
|
|
}
|
|
return this._curve(ps, null, o);
|
|
}
|
|
_arc(increment, cx, cy, rx, ry, strt, stp, offset, o) {
|
|
const radOffset = strt + this.getOffset(-0.1, 0.1, o);
|
|
const points = [];
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
|
|
this.getOffset(-offset, offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
|
|
]);
|
|
for (let angle = radOffset; angle <= stp; angle = angle + increment) {
|
|
points.push([
|
|
this.getOffset(-offset, offset, o) + cx + rx * Math.cos(angle),
|
|
this.getOffset(-offset, offset, o) + cy + ry * Math.sin(angle)
|
|
]);
|
|
}
|
|
points.push([
|
|
cx + rx * Math.cos(stp),
|
|
cy + ry * Math.sin(stp)
|
|
]);
|
|
points.push([
|
|
cx + rx * Math.cos(stp),
|
|
cy + ry * Math.sin(stp)
|
|
]);
|
|
return this._curve(points, null, o);
|
|
}
|
|
_bezierTo(x1, y1, x2, y2, x, y, path, o) {
|
|
const ops = [];
|
|
const ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5];
|
|
let f = [0, 0];
|
|
for (let i = 0; i < 2; i++) {
|
|
if (i === 0) {
|
|
ops.push({ op: 'move', data: [path.x, path.y] });
|
|
}
|
|
else {
|
|
ops.push({ op: 'move', data: [path.x + this.getOffset(-ros[0], ros[0], o), path.y + this.getOffset(-ros[0], ros[0], o)] });
|
|
}
|
|
f = [x + this.getOffset(-ros[i], ros[i], o), y + this.getOffset(-ros[i], ros[i], o)];
|
|
ops.push({
|
|
op: 'bcurveTo', data: [
|
|
x1 + this.getOffset(-ros[i], ros[i], o), y1 + this.getOffset(-ros[i], ros[i], o),
|
|
x2 + this.getOffset(-ros[i], ros[i], o), y2 + this.getOffset(-ros[i], ros[i], o),
|
|
f[0], f[1]
|
|
]
|
|
});
|
|
}
|
|
path.setPosition(f[0], f[1]);
|
|
return ops;
|
|
}
|
|
_processSegment(path, seg, prevSeg, o) {
|
|
let ops = [];
|
|
switch (seg.key) {
|
|
case 'M':
|
|
case 'm': {
|
|
const delta = seg.key === 'm';
|
|
if (seg.data.length >= 2) {
|
|
let x = +seg.data[0];
|
|
let y = +seg.data[1];
|
|
if (delta) {
|
|
x += path.x;
|
|
y += path.y;
|
|
}
|
|
const ro = 1 * (o.maxRandomnessOffset || 0);
|
|
x = x + this.getOffset(-ro, ro, o);
|
|
y = y + this.getOffset(-ro, ro, o);
|
|
path.setPosition(x, y);
|
|
ops.push({ op: 'move', data: [x, y] });
|
|
}
|
|
break;
|
|
}
|
|
case 'L':
|
|
case 'l': {
|
|
const delta = seg.key === 'l';
|
|
if (seg.data.length >= 2) {
|
|
let x = +seg.data[0];
|
|
let y = +seg.data[1];
|
|
if (delta) {
|
|
x += path.x;
|
|
y += path.y;
|
|
}
|
|
ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o));
|
|
path.setPosition(x, y);
|
|
}
|
|
break;
|
|
}
|
|
case 'H':
|
|
case 'h': {
|
|
const delta = seg.key === 'h';
|
|
if (seg.data.length) {
|
|
let x = +seg.data[0];
|
|
if (delta) {
|
|
x += path.x;
|
|
}
|
|
ops = ops.concat(this.doubleLine(path.x, path.y, x, path.y, o));
|
|
path.setPosition(x, path.y);
|
|
}
|
|
break;
|
|
}
|
|
case 'V':
|
|
case 'v': {
|
|
const delta = seg.key === 'v';
|
|
if (seg.data.length) {
|
|
let y = +seg.data[0];
|
|
if (delta) {
|
|
y += path.y;
|
|
}
|
|
ops = ops.concat(this.doubleLine(path.x, path.y, path.x, y, o));
|
|
path.setPosition(path.x, y);
|
|
}
|
|
break;
|
|
}
|
|
case 'Z':
|
|
case 'z': {
|
|
if (path.first) {
|
|
ops = ops.concat(this.doubleLine(path.x, path.y, path.first[0], path.first[1], o));
|
|
path.setPosition(path.first[0], path.first[1]);
|
|
path.first = null;
|
|
}
|
|
break;
|
|
}
|
|
case 'C':
|
|
case 'c': {
|
|
const delta = seg.key === 'c';
|
|
if (seg.data.length >= 6) {
|
|
let x1 = +seg.data[0];
|
|
let y1 = +seg.data[1];
|
|
let x2 = +seg.data[2];
|
|
let y2 = +seg.data[3];
|
|
let x = +seg.data[4];
|
|
let y = +seg.data[5];
|
|
if (delta) {
|
|
x1 += path.x;
|
|
x2 += path.x;
|
|
x += path.x;
|
|
y1 += path.y;
|
|
y2 += path.y;
|
|
y += path.y;
|
|
}
|
|
const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o);
|
|
ops = ops.concat(ob);
|
|
path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)];
|
|
}
|
|
break;
|
|
}
|
|
case 'S':
|
|
case 's': {
|
|
const delta = seg.key === 's';
|
|
if (seg.data.length >= 4) {
|
|
let x2 = +seg.data[0];
|
|
let y2 = +seg.data[1];
|
|
let x = +seg.data[2];
|
|
let y = +seg.data[3];
|
|
if (delta) {
|
|
x2 += path.x;
|
|
x += path.x;
|
|
y2 += path.y;
|
|
y += path.y;
|
|
}
|
|
let x1 = x2;
|
|
let y1 = y2;
|
|
const prevKey = prevSeg ? prevSeg.key : '';
|
|
let ref = null;
|
|
if (prevKey === 'c' || prevKey === 'C' || prevKey === 's' || prevKey === 'S') {
|
|
ref = path.bezierReflectionPoint;
|
|
}
|
|
if (ref) {
|
|
x1 = ref[0];
|
|
y1 = ref[1];
|
|
}
|
|
const ob = this._bezierTo(x1, y1, x2, y2, x, y, path, o);
|
|
ops = ops.concat(ob);
|
|
path.bezierReflectionPoint = [x + (x - x2), y + (y - y2)];
|
|
}
|
|
break;
|
|
}
|
|
case 'Q':
|
|
case 'q': {
|
|
const delta = seg.key === 'q';
|
|
if (seg.data.length >= 4) {
|
|
let x1 = +seg.data[0];
|
|
let y1 = +seg.data[1];
|
|
let x = +seg.data[2];
|
|
let y = +seg.data[3];
|
|
if (delta) {
|
|
x1 += path.x;
|
|
x += path.x;
|
|
y1 += path.y;
|
|
y += path.y;
|
|
}
|
|
const offset1 = 1 * (1 + o.roughness * 0.2);
|
|
const offset2 = 1.5 * (1 + o.roughness * 0.22);
|
|
ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] });
|
|
let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)];
|
|
ops.push({
|
|
op: 'qcurveTo', data: [
|
|
x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o),
|
|
f[0], f[1]
|
|
]
|
|
});
|
|
ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] });
|
|
f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)];
|
|
ops.push({
|
|
op: 'qcurveTo', data: [
|
|
x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o),
|
|
f[0], f[1]
|
|
]
|
|
});
|
|
path.setPosition(f[0], f[1]);
|
|
path.quadReflectionPoint = [x + (x - x1), y + (y - y1)];
|
|
}
|
|
break;
|
|
}
|
|
case 'T':
|
|
case 't': {
|
|
const delta = seg.key === 't';
|
|
if (seg.data.length >= 2) {
|
|
let x = +seg.data[0];
|
|
let y = +seg.data[1];
|
|
if (delta) {
|
|
x += path.x;
|
|
y += path.y;
|
|
}
|
|
let x1 = x;
|
|
let y1 = y;
|
|
const prevKey = prevSeg ? prevSeg.key : '';
|
|
let ref = null;
|
|
if (prevKey === 'q' || prevKey === 'Q' || prevKey === 't' || prevKey === 'T') {
|
|
ref = path.quadReflectionPoint;
|
|
}
|
|
if (ref) {
|
|
x1 = ref[0];
|
|
y1 = ref[1];
|
|
}
|
|
const offset1 = 1 * (1 + o.roughness * 0.2);
|
|
const offset2 = 1.5 * (1 + o.roughness * 0.22);
|
|
ops.push({ op: 'move', data: [path.x + this.getOffset(-offset1, offset1, o), path.y + this.getOffset(-offset1, offset1, o)] });
|
|
let f = [x + this.getOffset(-offset1, offset1, o), y + this.getOffset(-offset1, offset1, o)];
|
|
ops.push({
|
|
op: 'qcurveTo', data: [
|
|
x1 + this.getOffset(-offset1, offset1, o), y1 + this.getOffset(-offset1, offset1, o),
|
|
f[0], f[1]
|
|
]
|
|
});
|
|
ops.push({ op: 'move', data: [path.x + this.getOffset(-offset2, offset2, o), path.y + this.getOffset(-offset2, offset2, o)] });
|
|
f = [x + this.getOffset(-offset2, offset2, o), y + this.getOffset(-offset2, offset2, o)];
|
|
ops.push({
|
|
op: 'qcurveTo', data: [
|
|
x1 + this.getOffset(-offset2, offset2, o), y1 + this.getOffset(-offset2, offset2, o),
|
|
f[0], f[1]
|
|
]
|
|
});
|
|
path.setPosition(f[0], f[1]);
|
|
path.quadReflectionPoint = [x + (x - x1), y + (y - y1)];
|
|
}
|
|
break;
|
|
}
|
|
case 'A':
|
|
case 'a': {
|
|
const delta = seg.key === 'a';
|
|
if (seg.data.length >= 7) {
|
|
const rx = +seg.data[0];
|
|
const ry = +seg.data[1];
|
|
const angle = +seg.data[2];
|
|
const largeArcFlag = +seg.data[3];
|
|
const sweepFlag = +seg.data[4];
|
|
let x = +seg.data[5];
|
|
let y = +seg.data[6];
|
|
if (delta) {
|
|
x += path.x;
|
|
y += path.y;
|
|
}
|
|
if (x === path.x && y === path.y) {
|
|
break;
|
|
}
|
|
if (rx === 0 || ry === 0) {
|
|
ops = ops.concat(this.doubleLine(path.x, path.y, x, y, o));
|
|
path.setPosition(x, y);
|
|
}
|
|
else {
|
|
for (let i = 0; i < 1; i++) {
|
|
const arcConverter = new RoughArcConverter([path.x, path.y], [x, y], [rx, ry], angle, largeArcFlag ? true : false, sweepFlag ? true : false);
|
|
let segment = arcConverter.getNextSegment();
|
|
while (segment) {
|
|
const ob = this._bezierTo(segment.cp1[0], segment.cp1[1], segment.cp2[0], segment.cp2[1], segment.to[0], segment.to[1], path, o);
|
|
ops = ops.concat(ob);
|
|
segment = arcConverter.getNextSegment();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return ops;
|
|
}
|
|
}
|