mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-08 05:42:07 -05:00
Change the way the Line class handles direction vectors and infinite lines.
The beginning of performance improvements in the Line class.
This commit is contained in:
parent
1fb0a3a13c
commit
80f9f6061c
5 changed files with 66 additions and 80 deletions
|
@ -22,26 +22,13 @@ var Line = this.Line = Base.extend(/** @lends Line# */{
|
||||||
*
|
*
|
||||||
* @param {Point} point1
|
* @param {Point} point1
|
||||||
* @param {Point} point2
|
* @param {Point} point2
|
||||||
* @param {Boolean} [infinite=true]
|
* @param {Boolean} [asVector=false]
|
||||||
*/
|
*/
|
||||||
initialize: function(point1, point2, infinite) {
|
initialize: function(point1, point2, asVector) {
|
||||||
// Convention: With 3 parameters, both points are absolute, and infinite
|
this.point = Point.read(arguments);
|
||||||
// controls wether the line extends beyond the defining points, meaning
|
this.vector = Point.read(arguments);
|
||||||
// intersection outside the line segment are allowed.
|
if (!Base.read(arguments))
|
||||||
// With two parameters, the 2nd parameter is a direction, and infinite
|
this.vector = this.vector.subtract(this.point);
|
||||||
// is automatially true, since we're describing an infinite line.
|
|
||||||
var _point1 = Point.read(arguments),
|
|
||||||
_point2 = Point.read(arguments),
|
|
||||||
_infinite = Base.read(arguments);
|
|
||||||
if (_infinite !== undefined) {
|
|
||||||
this.point = _point1;
|
|
||||||
this.vector = _point2.subtract(_point1);
|
|
||||||
this.infinite = _infinite;
|
|
||||||
} else {
|
|
||||||
this.point = _point1;
|
|
||||||
this.vector = _point2;
|
|
||||||
this.infinite = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,32 +45,19 @@ var Line = this.Line = Base.extend(/** @lends Line# */{
|
||||||
* @type Point
|
* @type Point
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the line extends infinitely
|
|
||||||
*
|
|
||||||
* @name Line#infinite
|
|
||||||
* @type Boolean
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Line} line
|
* @param {Line} line
|
||||||
|
* @param {Boolean} [isInfinite=false]
|
||||||
* @return {Point} the intersection point of the lines, {@code undefined}
|
* @return {Point} the intersection point of the lines, {@code undefined}
|
||||||
* if the two lines are colinear, or {@code null} if they don't intersect.
|
* if the two lines are colinear, or {@code null} if they don't intersect.
|
||||||
*/
|
*/
|
||||||
intersect: function(line) {
|
intersect: function(line, isInfinite) {
|
||||||
var cross = this.vector.cross(line.vector);
|
var p1 = this.point,
|
||||||
// Avoid divisions by 0, and errors when getting too close to 0
|
v1 = this.vector,
|
||||||
if (Numerical.isZero(cross))
|
p2 = line.point,
|
||||||
return undefined;
|
v2 = line.vector;
|
||||||
var v = line.point.subtract(this.point),
|
return Line.intersect(p1.x, p1.y, v1.x, v1.y, p2.x, p2.y, v2.x, v2.y,
|
||||||
t1 = v.cross(line.vector) / cross,
|
true, isInfinite);
|
||||||
t2 = v.cross(this.vector) / cross;
|
|
||||||
// Check the ranges of t parameters if the line is not allowed to
|
|
||||||
// extend beyond the definition points.
|
|
||||||
return (this.infinite || 0 <= t1 && t1 <= 1)
|
|
||||||
&& (line.infinite || 0 <= t2 && t2 <= 1)
|
|
||||||
? this.point.add(this.vector.multiply(t1))
|
|
||||||
: null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// DOCS: document Line#getSide(point)
|
// DOCS: document Line#getSide(point)
|
||||||
|
@ -92,6 +66,7 @@ var Line = this.Line = Base.extend(/** @lends Line# */{
|
||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
getSide: function(point) {
|
getSide: function(point) {
|
||||||
|
point = Point.read(arguments);
|
||||||
var v1 = this.vector,
|
var v1 = this.vector,
|
||||||
v2 = point.subtract(this.point),
|
v2 = point.subtract(this.point),
|
||||||
ccw = v2.cross(v1);
|
ccw = v2.cross(v1);
|
||||||
|
@ -100,44 +75,57 @@ var Line = this.Line = Base.extend(/** @lends Line# */{
|
||||||
if (ccw > 0) {
|
if (ccw > 0) {
|
||||||
ccw = v2.subtract(v1).dot(v1);
|
ccw = v2.subtract(v1).dot(v1);
|
||||||
if (ccw < 0)
|
if (ccw < 0)
|
||||||
ccw = 0;
|
ccw = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
|
return ccw < 0 ? -1 : ccw > 0 ? 1 : 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// DOCS: document Line#getSignedDistance(point)
|
||||||
|
/**
|
||||||
|
* @param {Point} point
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
getSignedDistance: function(point) {
|
||||||
|
var m = this.vector.y / this.vector.x, // slope
|
||||||
|
b = this.point.y - (m * this.point.x); // y offset
|
||||||
|
// Distance to the linear equation
|
||||||
|
return (point.y - (m * point.x) - b) / Math.sqrt(m * m + 1);
|
||||||
|
},
|
||||||
|
|
||||||
// DOCS: document Line#getDistance(point)
|
// DOCS: document Line#getDistance(point)
|
||||||
/**
|
/**
|
||||||
* @param {Point} point
|
* @param {Point} point
|
||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
getDistance: function(point) {
|
getDistance: function(point) {
|
||||||
var m = this.vector.y / this.vector.x, // slope
|
return Math.abs(this.getSignedDistance(point));
|
||||||
b = this.point.y - (m * this.point.x); // y offset
|
|
||||||
// Distance to the linear equation
|
|
||||||
var dist = Math.abs(point.y - (m * point.x) - b) / Math.sqrt(m * m + 1);
|
|
||||||
return this.infinite ? dist : Math.min(dist,
|
|
||||||
point.getDistance(this.point),
|
|
||||||
point.getDistance(this.point.add(this.vector)));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
statics: {
|
statics: /** @lends Line */{
|
||||||
intersect: function(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, infinite) {
|
intersect: function(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, asVectors,
|
||||||
var adx = ax2 - ax1,
|
isInfinite) {
|
||||||
ady = ay2 - ay1,
|
// Convert 2nd points to vectors if they are not specified as such.
|
||||||
bdx = bx2 - bx1,
|
if (!asVectors) {
|
||||||
bdy = by2 - by1,
|
ax2 -= ax1;
|
||||||
dx = ax1 - bx1,
|
ay2 -= ay1;
|
||||||
dy = ay1 - by1,
|
bx2 -= bx1;
|
||||||
cross = bdy * adx - bdx * ady;
|
by2 -= by1;
|
||||||
|
}
|
||||||
|
var cross = by2 * ax2 - bx2 * ay2;
|
||||||
|
// Avoid divisions by 0, and errors when getting too close to 0
|
||||||
if (!Numerical.isZero(cross)) {
|
if (!Numerical.isZero(cross)) {
|
||||||
var ta = (bdx * dy - bdy * dx) / cross,
|
var dx = ax1 - bx1,
|
||||||
tb = (adx * dy - ady * dx) / cross;
|
dy = ay1 - by1,
|
||||||
if ((infinite || 0 <= ta && ta <= 1)
|
ta = (bx2 * dy - by2 * dx) / cross,
|
||||||
&& (infinite || 0 <= tb && tb <= 1))
|
tb = (ax2 * dy - ay2 * dx) / cross;
|
||||||
return Point.create(
|
// Check the ranges of t parameters if the line is not allowed
|
||||||
ax1 + ta * adx,
|
// to extend beyond the definition points.
|
||||||
ay1 + ta * ady);
|
if ((isInfinite || 0 <= ta && ta <= 1)
|
||||||
|
&& (isInfinite || 0 <= tb && tb <= 1))
|
||||||
|
return Point.create(
|
||||||
|
ax1 + ta * ax2,
|
||||||
|
ay1 + ta * ay2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1162,13 +1162,9 @@ new function() { // Scope for methods that require numerical integration
|
||||||
&& (Curve.isLinear(v2)
|
&& (Curve.isLinear(v2)
|
||||||
|| Curve.isFlatEnough(v2, /*#=*/ Numerical.TOLERANCE))) {
|
|| Curve.isFlatEnough(v2, /*#=*/ Numerical.TOLERANCE))) {
|
||||||
// See if the parametric equations of the lines interesct.
|
// See if the parametric equations of the lines interesct.
|
||||||
// var point = new Line(v1[0], v1[1], v1[6], v1[7], false)
|
|
||||||
// .intersect(new Line(v2[0], v2[1], v2[6], v2[7], false));
|
|
||||||
// Use static version without creation of Line objects, but it
|
|
||||||
// doesn't seem to yield measurable speed improvements!
|
|
||||||
var point = Line.intersect(
|
var point = Line.intersect(
|
||||||
v1[0], v1[1], v1[6], v1[7],
|
v1[0], v1[1], v1[6], v1[7],
|
||||||
v2[0], v2[1], v2[6], v2[7], false);
|
v2[0], v2[1], v2[6], v2[7]);
|
||||||
if (point)
|
if (point)
|
||||||
addLocation(locations, curve1, null, point, curve2);
|
addLocation(locations, curve1, null, point, curve2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1439,7 +1435,7 @@ new function() { // Scope for methods that require numerical integration
|
||||||
function getLineLineIntersection(v1, v2, curve1, curve2, locations) {
|
function getLineLineIntersection(v1, v2, curve1, curve2, locations) {
|
||||||
var point = Line.intersect(
|
var point = Line.intersect(
|
||||||
v1[0], v1[1], v1[6], v1[7],
|
v1[0], v1[1], v1[6], v1[7],
|
||||||
v2[0], v2[1], v2[6], v2[7], false);
|
v2[0], v2[1], v2[6], v2[7]);
|
||||||
// Passing null for parameter leads to lazy determination of parameter
|
// Passing null for parameter leads to lazy determination of parameter
|
||||||
// values in CurveLocation#getParameter() only once they are requested.
|
// values in CurveLocation#getParameter() only once they are requested.
|
||||||
if (point)
|
if (point)
|
||||||
|
|
|
@ -2104,11 +2104,11 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
|
||||||
// Construct the two perpendicular middle lines to (from, through)
|
// Construct the two perpendicular middle lines to (from, through)
|
||||||
// and (through, to), and intersect them to get the center
|
// and (through, to), and intersect them to get the center
|
||||||
var l1 = new Line(from.add(through).divide(2),
|
var l1 = new Line(from.add(through).divide(2),
|
||||||
through.subtract(from).rotate(90)),
|
through.subtract(from).rotate(90), true),
|
||||||
l2 = new Line(through.add(to).divide(2),
|
l2 = new Line(through.add(to).divide(2),
|
||||||
to.subtract(through).rotate(90)),
|
to.subtract(through).rotate(90), true),
|
||||||
center = l1.intersect(l2),
|
center = l1.intersect(l2, true),
|
||||||
line = new Line(from, to, true),
|
line = new Line(from, to),
|
||||||
throughSide = line.getSide(through);
|
throughSide = line.getSide(through);
|
||||||
if (!center) {
|
if (!center) {
|
||||||
// If the two lines are colinear, there cannot be an arc as the
|
// If the two lines are colinear, there cannot be an arc as the
|
||||||
|
@ -2389,10 +2389,10 @@ statics: {
|
||||||
normal2 = curve2.getNormalAt(0, true).normalize(miterRadius),
|
normal2 = curve2.getNormalAt(0, true).normalize(miterRadius),
|
||||||
// Intersect the two lines
|
// Intersect the two lines
|
||||||
line1 = new Line(point.add(normal1),
|
line1 = new Line(point.add(normal1),
|
||||||
Point.create(-normal1.y, normal1.x)),
|
Point.create(-normal1.y, normal1.x), true),
|
||||||
line2 = new Line(point.add(normal2),
|
line2 = new Line(point.add(normal2),
|
||||||
Point.create(-normal2.y, normal2.x)),
|
Point.create(-normal2.y, normal2.x), true),
|
||||||
corner = line1.intersect(line2);
|
corner = line1.intersect(line2, true);
|
||||||
// Now measure the distance from the segment to the
|
// Now measure the distance from the segment to the
|
||||||
// intersection, which his half of the miter distance
|
// intersection, which his half of the miter distance
|
||||||
if (!corner || point.getDistance(corner) > miterLimit) {
|
if (!corner || point.getDistance(corner) > miterLimit) {
|
||||||
|
|
|
@ -122,10 +122,10 @@ new function() {
|
||||||
if (handle1.isOrthogonal(handle2)) {
|
if (handle1.isOrthogonal(handle2)) {
|
||||||
var from = segment._point,
|
var from = segment._point,
|
||||||
to = next._point,
|
to = next._point,
|
||||||
// Find hte corner point by intersecting the lines described
|
// Find the corner point by intersecting the lines described
|
||||||
// by both handles:
|
// by both handles:
|
||||||
corner = new Line(from, handle1).intersect(
|
corner = new Line(from, handle1, true).intersect(
|
||||||
new Line(to, handle2));
|
new Line(to, handle2, true), true);
|
||||||
return corner && Numerical.isZero(handle1.getLength() /
|
return corner && Numerical.isZero(handle1.getLength() /
|
||||||
corner.subtract(from).getLength() - kappa)
|
corner.subtract(from).getLength() - kappa)
|
||||||
&& Numerical.isZero(handle2.getLength() /
|
&& Numerical.isZero(handle2.getLength() /
|
||||||
|
|
|
@ -30,6 +30,8 @@ var Formatter = Base.extend({
|
||||||
* @param {Number} num the number to be converted to a string
|
* @param {Number} num the number to be converted to a string
|
||||||
*/
|
*/
|
||||||
number: function(val) {
|
number: function(val) {
|
||||||
|
// It would be nice to use Number#toFixed() instead, but it pads with 0,
|
||||||
|
// unecessarily consuming space.
|
||||||
return Math.round(val * this.multiplier) / this.multiplier;
|
return Math.round(val * this.multiplier) / this.multiplier;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue