roughjs-es5
Version:
Create graphics using HTML Canvas or SVG with a hand-drawn, sketchy, appearance.
989 lines (962 loc) • 27.9 kB
JavaScript
import { RoughHachureIterator } from "./hachure.js"
import { RoughSegmentRelation, RoughSegment } from "./segment.js"
import { RoughPath, RoughArcConverter, PathFitter } from "./path.js"
export class RoughRenderer {
line(x1, y1, x2, y2, o) {
let 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
)
}
}
polygon(points, o) {
return this.linearPath(points, true, o)
}
rectangle(x, y, width, height, o) {
let points = [
[x, y],
[x + width, y],
[x + width, y + height],
[x, y + height]
]
return this.polygon(points, o)
}
curve(points, o) {
let o1 = this._curveWithOffset(points, 1 * (1 + o.roughness * 0.2), o)
let 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)
let o1 = this._ellipse(
increment,
x,
y,
rx,
ry,
1,
increment * this._getOffset(0.1, this._getOffset(0.4, 1, o), o),
o
)
let 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) {
let cx = x
let 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
}
let ellipseInc = Math.PI * 2 / o.curveStepCount
let arcInc = Math.min(ellipseInc / 2, (stp - strt) / 2)
let o1 = this._arc(arcInc, cx, cy, rx, ry, strt, stp, 1, o)
let 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 }
}
hachureFillArc(x, y, width, height, start, stop, o) {
let cx = x
let 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
}
let increment = (stp - strt) / o.curveStepCount
let offset = 1
let xc = [],
yc = []
for (let angle = strt; angle <= stp; angle = angle + increment) {
xc.push(cx + rx * Math.cos(angle))
yc.push(cy + ry * Math.sin(angle))
}
xc.push(cx + rx * Math.cos(stp))
yc.push(cy + ry * Math.sin(stp))
xc.push(cx)
yc.push(cy)
return this.hachureFillShape(xc, yc, o)
}
solidFillShape(xCoords, yCoords, o) {
let ops = []
if (
xCoords &&
yCoords &&
xCoords.length &&
yCoords.length &&
xCoords.length === yCoords.length
) {
let offset = o.maxRandomnessOffset || 0
const len = xCoords.length
if (len > 2) {
ops.push({
op: "move",
data: [
xCoords[0] + this._getOffset(-offset, offset, o),
yCoords[0] + this._getOffset(-offset, offset, o)
]
})
for (var i = 1; i < len; i++) {
ops.push({
op: "lineTo",
data: [
xCoords[i] + this._getOffset(-offset, offset, o),
yCoords[i] + this._getOffset(-offset, offset, o)
]
})
}
}
}
return { type: "fillPath", ops }
}
hachureFillShape(xCoords, yCoords, o) {
let ops = []
if (xCoords && yCoords && xCoords.length && yCoords.length) {
let left = xCoords[0]
let right = xCoords[0]
let top = yCoords[0]
let bottom = yCoords[0]
for (let i = 1; i < xCoords.length; i++) {
left = Math.min(left, xCoords[i])
right = Math.max(right, xCoords[i])
top = Math.min(top, yCoords[i])
bottom = Math.max(bottom, yCoords[i])
}
const angle = o.hachureAngle
let gap = o.hachureGap
if (gap < 0) {
gap = o.strokeWidth * 4
}
gap = Math.max(gap, 0.1)
const radPerDeg = Math.PI / 180
const hachureAngle = (angle % 180) * radPerDeg
const cosAngle = Math.cos(hachureAngle)
const sinAngle = Math.sin(hachureAngle)
const tanAngle = Math.tan(hachureAngle)
const it = new RoughHachureIterator(
top - 1,
bottom + 1,
left - 1,
right + 1,
gap,
sinAngle,
cosAngle,
tanAngle
)
let rectCoords
while ((rectCoords = it.getNextLine()) != null) {
let lines = this._getIntersectingLines(rectCoords, xCoords, yCoords)
for (let i = 0; i < lines.length; i++) {
if (i < lines.length - 1) {
let p1 = lines[i]
let p2 = lines[i + 1]
ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o))
}
}
}
}
return { type: "fillSketch", ops }
}
hachureFillEllipse(cx, cy, width, height, o) {
let ops = []
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)
let angle = o.hachureAngle
let gap = o.hachureGap
if (gap <= 0) {
gap = o.strokeWidth * 4
}
let fweight = o.fillWeight
if (fweight < 0) {
fweight = o.strokeWidth / 2
}
const radPerDeg = Math.PI / 180
let hachureAngle = (angle % 180) * radPerDeg
let tanAngle = Math.tan(hachureAngle)
let aspectRatio = ry / rx
let hyp = Math.sqrt(aspectRatio * tanAngle * aspectRatio * tanAngle + 1)
let sinAnglePrime = aspectRatio * tanAngle / hyp
let cosAnglePrime = 1 / hyp
let gapPrime =
gap /
(rx *
ry /
Math.sqrt(
ry * cosAnglePrime * (ry * cosAnglePrime) +
rx * sinAnglePrime * (rx * sinAnglePrime)
) /
rx)
let halfLen = Math.sqrt(
rx * rx - (cx - rx + gapPrime) * (cx - rx + gapPrime)
)
for (var xPos = cx - rx + gapPrime; xPos < cx + rx; xPos += gapPrime) {
halfLen = Math.sqrt(rx * rx - (cx - xPos) * (cx - xPos))
let p1 = this._affine(
xPos,
cy - halfLen,
cx,
cy,
sinAnglePrime,
cosAnglePrime,
aspectRatio
)
let p2 = this._affine(
xPos,
cy + halfLen,
cx,
cy,
sinAnglePrime,
cosAnglePrime,
aspectRatio
)
ops = ops.concat(this._doubleLine(p1[0], p1[1], p2[0], p2[1], o))
}
return { type: "fillSketch", ops }
}
svgPath(path, o) {
path = (path || "")
.replace(/\n/g, " ")
.replace(/(-\s)/g, "-")
.replace("/(ss)/g", " ")
let p = new RoughPath(path)
if (o.simplification) {
let fitter = new PathFitter(p.linearPoints, p.closed)
let d = fitter.fit(o.simplification)
p = new RoughPath(d)
}
let ops = []
let segments = p.segments || []
for (let i = 0; i < segments.length; i++) {
let s = segments[i]
let prev = i > 0 ? segments[i - 1] : null
let opList = this._processSegment(p, s, prev, o)
if (opList && opList.length) {
ops = ops.concat(opList)
}
}
return { type: "path", ops }
}
// privates
_bezierTo(x1, y1, x2, y2, x, y, path, o) {
let ops = []
let ros = [o.maxRandomnessOffset || 1, (o.maxRandomnessOffset || 1) + 0.5]
let f = null
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": {
let 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
}
let 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": {
let 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
}
let 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
let prevKey = prevSeg ? prevSeg.key : ""
var ref = null
if (
prevKey == "c" ||
prevKey == "C" ||
prevKey == "s" ||
prevKey == "S"
) {
ref = path.bezierReflectionPoint
}
if (ref) {
x1 = ref[0]
y1 = ref[1]
}
let 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
}
let offset1 = 1 * (1 + o.roughness * 0.2)
let 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
let prevKey = prevSeg ? prevSeg.key : ""
var ref = null
if (
prevKey == "q" ||
prevKey == "Q" ||
prevKey == "t" ||
prevKey == "T"
) {
ref = path.quadReflectionPoint
}
if (ref) {
x1 = ref[0]
y1 = ref[1]
}
let offset1 = 1 * (1 + o.roughness * 0.2)
let 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) {
let rx = +seg.data[0]
let ry = +seg.data[1]
let angle = +seg.data[2]
let largeArcFlag = +seg.data[3]
let 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 {
let final = null
let ro = o.maxRandomnessOffset || 0
for (let i = 0; i < 1; i++) {
let arcConverter = new RoughArcConverter(
[path.x, path.y],
[x, y],
[rx, ry],
angle,
largeArcFlag ? true : false,
sweepFlag ? true : false
)
let segment = arcConverter.getNextSegment()
while (segment) {
let 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
}
_getOffset(min, max, ops) {
return ops.roughness * (Math.random() * (max - min) + min)
}
_affine(x, y, cx, cy, sinAnglePrime, cosAnglePrime, R) {
var A = -cx * cosAnglePrime - cy * sinAnglePrime + cx
var B = R * (cx * sinAnglePrime - cy * cosAnglePrime) + cy
var C = cosAnglePrime
var D = sinAnglePrime
var E = -R * sinAnglePrime
var F = R * cosAnglePrime
return [A + C * x + D * y, B + E * x + F * y]
}
_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)
let 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) {
let ro = o.maxRandomnessOffset
// TODO: more roughness here?
ops.push({
ops: "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)
}
_getIntersectingLines(lineCoords, xCoords, yCoords) {
let intersections = []
var s1 = new RoughSegment(
lineCoords[0],
lineCoords[1],
lineCoords[2],
lineCoords[3]
)
for (var i = 0; i < xCoords.length; i++) {
let s2 = new RoughSegment(
xCoords[i],
yCoords[i],
xCoords[(i + 1) % xCoords.length],
yCoords[(i + 1) % xCoords.length]
)
if (s1.compare(s2) == RoughSegmentRelation().INTERSECTS) {
intersections.push([s1.xi, s1.yi])
}
}
return intersections
}
}