✨ Fixed offset issue when Cp lies on start/end
See https://github.com/Pomax/bezierjs/issues/37#issuecomment-410810976
This commit is contained in:
parent
4c81d41ab8
commit
8051a48880
2 changed files with 74 additions and 9 deletions
77
src/path.js
77
src/path.js
|
@ -190,19 +190,29 @@ function pathOffset(path, distance) {
|
|||
if (op.type === "line") {
|
||||
offset.push(offsetLine(current, op.to, distance));
|
||||
} else if (op.type === "curve") {
|
||||
// We need to avoid a control point sitting on top of start or end
|
||||
// because that will break the offset in bezier-js
|
||||
let cp1, cp2;
|
||||
if (current.sitsOn(op.cp1)) {
|
||||
cp1 = new Path()
|
||||
.move(current)
|
||||
.curve(op.cp1, op.cp2, op.to)
|
||||
.shiftAlong(1);
|
||||
} else cp1 = op.cp1;
|
||||
if (op.cp2.sitsOn(op.to)) {
|
||||
cp2 = new Path()
|
||||
.move(op.to)
|
||||
.curve(op.cp2, op.cp1, current)
|
||||
.shiftAlong(1);
|
||||
} else cp2 = op.cp2;
|
||||
let b = new Bezier(
|
||||
{ x: current.x, y: current.y },
|
||||
{ x: op.cp1.x, y: op.cp1.y },
|
||||
{ x: op.cp2.x, y: op.cp2.y },
|
||||
{ x: cp1.x, y: cp1.y },
|
||||
{ x: cp2.x, y: cp2.y },
|
||||
{ x: op.to.x, y: op.to.y }
|
||||
);
|
||||
for (let bezier of b.offset(distance)) {
|
||||
offset.push(asPath(bezier));
|
||||
}
|
||||
} else if (op.type === "close") {
|
||||
// offset.push(offsetLine(current, start, distance));
|
||||
closed = true;
|
||||
}
|
||||
for (let bezier of b.offset(distance)) offset.push(asPath(bezier));
|
||||
} else if (op.type === "close") closed = true;
|
||||
if (op.to) current = op.to;
|
||||
if (!start) start = current;
|
||||
}
|
||||
|
@ -252,4 +262,53 @@ function joinPaths(paths, closed = false) {
|
|||
return joint;
|
||||
}
|
||||
|
||||
/** Returns a point that lies at distance along this */
|
||||
Path.prototype.shiftAlong = function(distance) {
|
||||
let len = 0;
|
||||
let current;
|
||||
for (let i in this.ops) {
|
||||
let op = this.ops[i];
|
||||
if (op.type === "line") {
|
||||
let thisLen = op.to.dist(current);
|
||||
if (len + thisLen > distance)
|
||||
return current.shiftTowards(op.to, distance - len);
|
||||
else len += thisLen;
|
||||
} else if (op.type === "curve") {
|
||||
let bezier = new Bezier(
|
||||
{ x: current.x, y: current.y },
|
||||
{ x: op.cp1.x, y: op.cp1.y },
|
||||
{ x: op.cp2.x, y: op.cp2.y },
|
||||
{ x: op.to.x, y: op.to.y }
|
||||
);
|
||||
let thisLen = bezier.length();
|
||||
if (len + thisLen > distance)
|
||||
return shiftAlongBezier(distance - len, bezier);
|
||||
else len += thisLen;
|
||||
}
|
||||
current = op.to;
|
||||
}
|
||||
console.log("distance is", distance, "len is", len);
|
||||
throw "Ran out of curve to shift along";
|
||||
};
|
||||
|
||||
/** Returns a point that lies at distance along bezier */
|
||||
function shiftAlongBezier(distance, bezier) {
|
||||
let steps = 100;
|
||||
let maxLength = bezier.length();
|
||||
if (distance > maxLength) throw "Cannot shift further than the bezier length";
|
||||
let previous, next, t, thisLen;
|
||||
let len = 0;
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
t = i / steps;
|
||||
next = bezier.get(t);
|
||||
next = new Point(next.x, next.y);
|
||||
if (i > 0) {
|
||||
thisLen = next.dist(previous);
|
||||
if (len + thisLen > distance) return next;
|
||||
else len += thisLen;
|
||||
}
|
||||
previous = next;
|
||||
}
|
||||
}
|
||||
|
||||
export default Path;
|
||||
|
|
|
@ -99,6 +99,12 @@ Point.prototype.shiftTowards = function(that, distance) {
|
|||
return this.shift(this.angle(that), distance);
|
||||
};
|
||||
|
||||
/** Checks whether this has the same coordinates as that */
|
||||
Point.prototype.sitsOn = function(that) {
|
||||
if (this.x === that.x && this.y === that.y) return true;
|
||||
else return false;
|
||||
};
|
||||
|
||||
/** Shifts this point fraction of the distance towards that point */
|
||||
Point.prototype.shiftFractionTowards = function(that, fraction) {
|
||||
return this.shiftTowards(that, this.dist(that) * fraction);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue