2011-04-11 13:27:11 -04:00
|
|
|
/*
|
2013-01-28 21:03:27 -05:00
|
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
2011-04-11 13:27:11 -04:00
|
|
|
* http://paperjs.org/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2014-01-03 19:47:16 -05:00
|
|
|
* Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://scratchdisk.com/ & http://jonathanpuckey.com/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 06:17:45 -04:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-04-11 13:27:11 -04:00
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2011-06-22 18:56:05 -04:00
|
|
|
/**
|
|
|
|
* @name CurveLocation
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2015-10-03 17:15:45 -04:00
|
|
|
* @class CurveLocation objects describe a location on {@link Curve} objects,
|
|
|
|
* as defined by the curve-time {@link #parameter}, a value between {@code 0}
|
|
|
|
* (beginning of the curve) and {@code 1} (end of the curve). If the curve is
|
|
|
|
* part of a {@link Path} item, its {@link #index} inside the
|
2011-06-22 18:56:05 -04:00
|
|
|
* {@link Path#curves} array is also provided.
|
2011-07-07 10:10:27 -04:00
|
|
|
*
|
|
|
|
* The class is in use in many places, such as
|
2015-10-03 17:15:45 -04:00
|
|
|
* {@link Path#getLocationAt(offset)},
|
2012-12-27 15:08:03 -05:00
|
|
|
* {@link Path#getLocationOf(point)},
|
2015-10-03 17:15:45 -04:00
|
|
|
* {@link Path#getNearestLocation(point)},
|
2012-12-27 15:08:03 -05:00
|
|
|
* {@link PathItem#getIntersections(path)},
|
|
|
|
* etc.
|
2011-06-22 18:56:05 -04:00
|
|
|
*/
|
2013-05-27 15:48:58 -04:00
|
|
|
var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
2014-08-16 13:24:54 -04:00
|
|
|
_class: 'CurveLocation',
|
|
|
|
// Enforce creation of beans, as bean getters have hidden parameters.
|
|
|
|
// See #getSegment() below.
|
|
|
|
beans: true,
|
2014-04-02 14:53:18 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
// DOCS: CurveLocation class description: add these back when the mentioned
|
|
|
|
// functioned have been added: {@link Path#split(location)}
|
|
|
|
/**
|
|
|
|
* Creates a new CurveLocation object.
|
|
|
|
*
|
|
|
|
* @param {Curve} curve
|
|
|
|
* @param {Number} parameter
|
2015-06-16 13:36:22 -04:00
|
|
|
* @param {Point} [point]
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
2015-09-09 11:17:49 -04:00
|
|
|
initialize: function CurveLocation(curve, parameter, point,
|
2015-09-24 12:49:39 -04:00
|
|
|
_overlap, _distance) {
|
2015-10-11 11:00:23 -04:00
|
|
|
// Merge intersections very close to the end of a curve with the
|
2015-09-09 11:17:49 -04:00
|
|
|
// beginning of the next curve.
|
2015-10-11 11:00:23 -04:00
|
|
|
if (parameter > /*#=*/(1 - Numerical.CURVETIME_EPSILON)) {
|
2015-09-09 11:17:49 -04:00
|
|
|
var next = curve.getNext();
|
|
|
|
if (next) {
|
|
|
|
parameter = 0;
|
|
|
|
curve = next;
|
|
|
|
}
|
|
|
|
}
|
2014-08-16 13:24:54 -04:00
|
|
|
// Define this CurveLocation's unique id.
|
2015-05-11 13:39:39 -04:00
|
|
|
// NOTE: We do not use the same pool as the rest of the library here,
|
|
|
|
// since this is only required to be unique at runtime among other
|
|
|
|
// CurveLocation objects.
|
2015-06-16 12:12:40 -04:00
|
|
|
this._id = UID.get(CurveLocation);
|
2015-09-15 13:38:28 -04:00
|
|
|
this._setCurve(curve);
|
2014-08-16 13:24:54 -04:00
|
|
|
this._parameter = parameter;
|
2015-06-16 13:36:22 -04:00
|
|
|
this._point = point || curve.getPointAt(parameter, true);
|
2015-10-11 11:05:23 -04:00
|
|
|
this._overlap = _overlap;
|
2015-09-24 12:49:39 -04:00
|
|
|
this._distance = _distance;
|
2015-10-08 17:54:00 -04:00
|
|
|
this._intersection = this._next = this._prev = null;
|
2015-09-15 13:38:28 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_setCurve: function(curve) {
|
|
|
|
var path = curve._path;
|
|
|
|
this._version = path ? path._version : 0;
|
|
|
|
this._curve = curve;
|
2015-09-13 07:06:01 -04:00
|
|
|
this._segment = null; // To be determined, see #getSegment()
|
2015-06-16 13:36:22 -04:00
|
|
|
// Also store references to segment1 and segment2, in case path
|
|
|
|
// splitting / dividing is going to happen, in which case the segments
|
|
|
|
// can be used to determine the new curves, see #getCurve(true)
|
|
|
|
this._segment1 = curve._segment1;
|
|
|
|
this._segment2 = curve._segment2;
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2015-10-03 20:25:33 -04:00
|
|
|
_setSegment: function(segment) {
|
|
|
|
this._setCurve(segment.getCurve());
|
|
|
|
this._segment = segment;
|
|
|
|
this._parameter = segment === this._segment1 ? 0 : 1;
|
|
|
|
},
|
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The segment of the curve which is closer to the described location.
|
|
|
|
*
|
|
|
|
* @type Segment
|
|
|
|
* @bean
|
|
|
|
*/
|
2015-09-15 13:38:28 -04:00
|
|
|
getSegment: function() {
|
|
|
|
// Request curve first, so _segment gets invalidated if it's out of sync
|
|
|
|
var curve = this.getCurve(),
|
|
|
|
segment = this._segment;
|
|
|
|
if (!segment) {
|
|
|
|
var parameter = this.getParameter();
|
|
|
|
if (parameter === 0) {
|
|
|
|
segment = curve._segment1;
|
|
|
|
} else if (parameter === 1) {
|
|
|
|
segment = curve._segment2;
|
|
|
|
} else if (parameter != null) {
|
2014-08-16 13:24:54 -04:00
|
|
|
// Determine the closest segment by comparing curve lengths
|
2015-09-15 13:38:28 -04:00
|
|
|
segment = curve.getPartLength(0, parameter)
|
2014-08-16 13:24:54 -04:00
|
|
|
< curve.getPartLength(parameter, 1)
|
|
|
|
? curve._segment1
|
|
|
|
: curve._segment2;
|
|
|
|
}
|
2015-09-15 13:38:28 -04:00
|
|
|
this._segment = segment;
|
2014-08-16 13:24:54 -04:00
|
|
|
}
|
2015-09-15 13:38:28 -04:00
|
|
|
return segment;
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The curve that this location belongs to.
|
|
|
|
*
|
|
|
|
* @type Curve
|
|
|
|
* @bean
|
|
|
|
*/
|
2015-06-16 13:36:22 -04:00
|
|
|
getCurve: function() {
|
|
|
|
var curve = this._curve,
|
2015-09-15 13:38:28 -04:00
|
|
|
path = curve && curve._path,
|
|
|
|
that = this;
|
2015-06-16 13:36:22 -04:00
|
|
|
if (path && path._version !== this._version) {
|
|
|
|
// If the path's segments have changed in the meantime, clear the
|
|
|
|
// internal _parameter value and force refetching of the correct
|
|
|
|
// curve again here.
|
2015-09-15 13:38:28 -04:00
|
|
|
curve = this._parameter = this._curve = null;
|
2015-06-16 13:36:22 -04:00
|
|
|
}
|
2015-09-15 13:38:28 -04:00
|
|
|
|
|
|
|
// If path is out of sync, access current curve objects through segment1
|
|
|
|
// / segment2. Since path splitting or dividing might have happened in
|
|
|
|
// the meantime, try segment1's curve, and see if _point lies on it
|
|
|
|
// still, otherwise assume it's the curve before segment2.
|
|
|
|
function trySegment(segment) {
|
|
|
|
var curve = segment && segment.getCurve();
|
|
|
|
if (curve && (that._parameter = curve.getParameterOf(that._point))
|
|
|
|
!= null) {
|
|
|
|
// Fetch path again as it could be on a new one through split()
|
|
|
|
that._setCurve(curve);
|
|
|
|
that._segment = segment;
|
|
|
|
return curve;
|
|
|
|
}
|
2014-08-16 13:24:54 -04:00
|
|
|
}
|
2015-09-15 13:38:28 -04:00
|
|
|
|
|
|
|
return curve
|
|
|
|
|| trySegment(this._segment)
|
|
|
|
|| trySegment(this._segment1)
|
|
|
|
|| trySegment(this._segment2.getPrevious());
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2013-04-30 21:41:26 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The path this curve belongs to, if any.
|
|
|
|
*
|
|
|
|
* @type Item
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getPath: function() {
|
|
|
|
var curve = this.getCurve();
|
|
|
|
return curve && curve._path;
|
|
|
|
},
|
2011-04-26 12:49:54 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
2015-10-03 11:40:13 -04:00
|
|
|
* The index of the {@link #curve} within the {@link Path#curves} list, if
|
|
|
|
* it is part of a {@link Path} item.
|
2014-08-16 13:24:54 -04:00
|
|
|
*
|
|
|
|
* @type Index
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getIndex: function() {
|
|
|
|
var curve = this.getCurve();
|
|
|
|
return curve && curve.getIndex();
|
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2015-06-16 13:36:22 -04:00
|
|
|
/**
|
2015-10-03 11:40:13 -04:00
|
|
|
* The curve-time parameter, as used by various bezier curve calculations.
|
|
|
|
* It is value between {@code 0} (beginning of the curve) and {@code 1}
|
|
|
|
* (end of the curve).
|
2015-06-16 13:36:22 -04:00
|
|
|
*
|
|
|
|
* @type Number
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getParameter: function() {
|
|
|
|
var curve = this.getCurve(),
|
|
|
|
parameter = this._parameter;
|
|
|
|
return curve && parameter == null
|
|
|
|
? this._parameter = curve.getParameterOf(this._point)
|
|
|
|
: parameter;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The point which is defined by the {@link #curve} and
|
|
|
|
* {@link #parameter}.
|
|
|
|
*
|
|
|
|
* @type Point
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getPoint: function() {
|
|
|
|
return this._point;
|
|
|
|
},
|
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The length of the path from its beginning up to the location described
|
|
|
|
* by this object. If the curve is not part of a path, then the length
|
|
|
|
* within the curve is returned instead.
|
|
|
|
*
|
|
|
|
* @type Number
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getOffset: function() {
|
|
|
|
var path = this.getPath();
|
|
|
|
return path ? path._getOffset(this) : this.getCurveOffset();
|
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The length of the curve from its beginning up to the location described
|
|
|
|
* by this object.
|
|
|
|
*
|
|
|
|
* @type Number
|
|
|
|
* @bean
|
|
|
|
*/
|
|
|
|
getCurveOffset: function() {
|
|
|
|
var curve = this.getCurve(),
|
|
|
|
parameter = this.getParameter();
|
|
|
|
return parameter != null && curve && curve.getPartLength(0, parameter);
|
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
2015-06-16 13:36:22 -04:00
|
|
|
* The curve location on the intersecting curve, if this location is the
|
|
|
|
* result of a call to {@link PathItem#getIntersections(path)} /
|
|
|
|
* {@link Curve#getIntersections(curve)}.
|
2014-08-16 13:24:54 -04:00
|
|
|
*
|
2015-06-16 13:36:22 -04:00
|
|
|
* @type CurveLocation
|
2014-08-16 13:24:54 -04:00
|
|
|
* @bean
|
|
|
|
*/
|
2015-06-16 13:36:22 -04:00
|
|
|
getIntersection: function() {
|
2015-09-09 11:17:49 -04:00
|
|
|
return this._intersection;
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2011-04-11 13:27:11 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The tangential vector to the {@link #curve} at the given location.
|
|
|
|
*
|
2015-10-03 17:15:45 -04:00
|
|
|
* @name CurveLocation#getTangent
|
2014-08-16 13:24:54 -04:00
|
|
|
* @type Point
|
2015-10-03 17:15:45 -04:00
|
|
|
* @bean
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
2011-06-30 06:01:51 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The normal vector to the {@link #curve} at the given location.
|
|
|
|
*
|
2015-10-03 17:15:45 -04:00
|
|
|
* @name CurveLocation#getNormal
|
2014-08-16 13:24:54 -04:00
|
|
|
* @type Point
|
2015-10-03 17:15:45 -04:00
|
|
|
* @bean
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
2014-07-25 14:05:44 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The curvature of the {@link #curve} at the given location.
|
|
|
|
*
|
2015-10-03 17:15:45 -04:00
|
|
|
* @name CurveLocation#getCurvature
|
2014-08-16 13:24:54 -04:00
|
|
|
* @type Number
|
2015-10-03 17:15:45 -04:00
|
|
|
* @bean
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
2011-04-27 14:26:03 -04:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
|
|
|
* The distance from the queried point to the returned location.
|
|
|
|
*
|
|
|
|
* @type Number
|
|
|
|
* @bean
|
2015-08-23 15:19:19 -04:00
|
|
|
* @see Curve#getNearestLocation(point)
|
|
|
|
* @see Path#getNearestLocation(point)
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
|
|
|
getDistance: function() {
|
|
|
|
return this._distance;
|
|
|
|
},
|
2011-07-06 16:19:01 -04:00
|
|
|
|
2015-06-16 13:36:22 -04:00
|
|
|
// DOCS: divide(), split()
|
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
divide: function() {
|
2015-06-16 13:36:22 -04:00
|
|
|
var curve = this.getCurve();
|
|
|
|
return curve && curve.divide(this.getParameter(), true);
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2012-12-31 15:42:55 -05:00
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
split: function() {
|
2015-06-16 13:36:22 -04:00
|
|
|
var curve = this.getCurve();
|
|
|
|
return curve && curve.split(this.getParameter(), true);
|
2014-08-16 13:24:54 -04:00
|
|
|
},
|
2012-12-30 13:53:09 -05:00
|
|
|
|
2015-10-03 17:15:45 -04:00
|
|
|
/**
|
|
|
|
* Checks whether tow CurveLocation objects are describing the same location
|
|
|
|
* on a path, by applying the same tolerances as elsewhere when dealing with
|
|
|
|
* curve time parameters.
|
|
|
|
*
|
|
|
|
* @param {CurveLocation} location
|
|
|
|
* @return {Boolean} {@true if the locations are equal}
|
|
|
|
*/
|
|
|
|
equals: function(loc, _ignoreOther) {
|
2015-10-12 18:10:21 -04:00
|
|
|
var res = this === loc;
|
|
|
|
if (!res && loc instanceof CurveLocation
|
|
|
|
&& this.getPath() === loc.getPath()) {
|
|
|
|
// NOTE: We need to compare both by (index + parameter) and by
|
|
|
|
// proximity of points, see:
|
|
|
|
// https://github.com/paperjs/paper.js/issues/784#issuecomment-143161586
|
2015-10-13 01:23:15 -04:00
|
|
|
// We need to wrap the diff value around the path's beginning / end.
|
2015-10-12 18:06:34 -04:00
|
|
|
var c1 = this.getCurve(),
|
|
|
|
c2 = loc.getCurve();
|
|
|
|
diff = ((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex())
|
|
|
|
+ this.getParameter())
|
|
|
|
- ((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex())
|
|
|
|
+ loc.getParameter());
|
|
|
|
// Use a relaxed threshold of < 1 for difference when deciding if
|
|
|
|
// two locations should be checked for point proximity. This is
|
|
|
|
// necessary to catch equal locations on very small curves.
|
2015-10-12 18:10:21 -04:00
|
|
|
res = (Math.abs(diff) < /*#=*/Numerical.CURVETIME_EPSILON
|
2015-10-03 17:15:45 -04:00
|
|
|
|| diff < 1 && this.getPoint().isClose(loc.getPoint(),
|
|
|
|
/*#=*/Numerical.GEOMETRIC_EPSILON))
|
2015-10-12 18:06:34 -04:00
|
|
|
&& (_ignoreOther
|
|
|
|
|| (!this._intersection && !loc._intersection
|
|
|
|
|| this._intersection && this._intersection.equals(
|
2015-10-12 18:11:24 -04:00
|
|
|
loc._intersection, true)));
|
2015-10-12 18:06:34 -04:00
|
|
|
}
|
2015-10-12 18:10:21 -04:00
|
|
|
return res;
|
2015-10-03 17:15:45 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return {String} a string representation of the curve location
|
|
|
|
*/
|
|
|
|
toString: function() {
|
|
|
|
var parts = [],
|
|
|
|
point = this.getPoint(),
|
|
|
|
f = Formatter.instance;
|
|
|
|
if (point)
|
|
|
|
parts.push('point: ' + point);
|
|
|
|
var index = this.getIndex();
|
|
|
|
if (index != null)
|
|
|
|
parts.push('index: ' + index);
|
|
|
|
var parameter = this.getParameter();
|
|
|
|
if (parameter != null)
|
|
|
|
parts.push('parameter: ' + f.number(parameter));
|
|
|
|
if (this._distance != null)
|
|
|
|
parts.push('distance: ' + f.number(this._distance));
|
|
|
|
return '{ ' + parts.join(', ') + ' }';
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@grouptitle Tests}
|
|
|
|
* Checks if the location is an intersection with another curve and is
|
|
|
|
* merely touching the other curve, as opposed to crossing it.
|
|
|
|
*
|
|
|
|
* @return {Boolean} {@true if the location is an intersection that is
|
|
|
|
* merely touching another curve}
|
|
|
|
* @see #isCrossing()
|
|
|
|
*/
|
|
|
|
isTouching: function() {
|
2015-09-18 11:32:19 -04:00
|
|
|
var t1 = this.getTangent(),
|
|
|
|
inter = this._intersection,
|
|
|
|
t2 = inter && inter.getTangent();
|
|
|
|
return t1 && t2 ? t1.isCollinear(t2) : false;
|
|
|
|
},
|
|
|
|
|
2015-10-03 17:15:45 -04:00
|
|
|
/**
|
|
|
|
* Checks if the location is an intersection with another curve and is
|
|
|
|
* crossing the other curve, as opposed to just touching it.
|
|
|
|
*
|
|
|
|
* @return {Boolean} {@true if the location is an intersection that is
|
|
|
|
* crossing another curve}
|
|
|
|
* @see #isTouching()
|
|
|
|
*/
|
2015-09-18 16:26:09 -04:00
|
|
|
isCrossing: function(_report) {
|
2015-09-15 13:38:28 -04:00
|
|
|
// Implementation based on work by Andy Finnell:
|
|
|
|
// http://losingfight.com/blog/2011/07/09/how-to-implement-boolean-operations-on-bezier-paths-part-3/
|
|
|
|
// https://bitbucket.org/andyfinnell/vectorboolean
|
2015-09-18 11:32:19 -04:00
|
|
|
var inter = this._intersection;
|
|
|
|
if (!inter)
|
|
|
|
return false;
|
2015-09-18 16:26:09 -04:00
|
|
|
// TODO: Make getCurve() and getParameter() sync work in boolean ops
|
|
|
|
// before and after splitting!!!
|
|
|
|
var t1 = this._parameter,
|
|
|
|
t2 = inter._parameter,
|
2015-09-18 11:32:19 -04:00
|
|
|
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
|
|
|
|
tMax = 1 - tMin;
|
|
|
|
// If the intersection is in the middle of the path, it is either a
|
|
|
|
// tangent or a crossing, no need for the detailed corner check below.
|
|
|
|
// But we do need a check for the edge case of tangents?
|
2015-09-18 16:26:09 -04:00
|
|
|
if (t1 >= tMin && t1 <= tMax || t2 >= tMin && t2 <= tMax)
|
2015-10-03 17:15:45 -04:00
|
|
|
return !this.isTouching();
|
2015-09-15 20:54:25 -04:00
|
|
|
// Values for getTangentAt() that are almost 0 and 1.
|
|
|
|
// NOTE: Even though getTangentAt() has code to support 0 and 1 instead
|
|
|
|
// of tMin and tMax, we still need to use this instead, as other issues
|
|
|
|
// emerge from switching to 0 and 1 in edge cases.
|
|
|
|
// NOTE: VectorBoolean has code that slowly shifts these points inwards
|
|
|
|
// until the resulting tangents are not ambiguous. Do we need this too?
|
2015-09-18 11:32:19 -04:00
|
|
|
var c2 = this._curve,
|
2015-09-15 13:38:28 -04:00
|
|
|
c1 = c2.getPrevious(),
|
2015-09-18 11:32:19 -04:00
|
|
|
c4 = inter._curve,
|
|
|
|
c3 = c4.getPrevious(),
|
|
|
|
PI = Math.PI;
|
2015-09-15 13:38:28 -04:00
|
|
|
if (!c1 || !c3)
|
2015-09-18 11:32:19 -04:00
|
|
|
return false;
|
2015-09-15 13:38:28 -04:00
|
|
|
|
2015-09-18 16:26:09 -04:00
|
|
|
if (_report) {
|
|
|
|
new Path.Circle({
|
|
|
|
center: this.getPoint(),
|
|
|
|
radius: 10,
|
|
|
|
strokeColor: 'red'
|
|
|
|
});
|
|
|
|
new Path({
|
|
|
|
segments: [c1.getSegment1(), c1.getSegment2(), c2.getSegment2()],
|
|
|
|
strokeColor: 'red',
|
|
|
|
strokeWidth: 4
|
|
|
|
});
|
|
|
|
new Path({
|
|
|
|
segments: [c3.getSegment1(), c3.getSegment2(), c4.getSegment2()],
|
|
|
|
strokeColor: 'orange',
|
|
|
|
strokeWidth: 4
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-09-15 13:38:28 -04:00
|
|
|
function isInRange(angle, min, max) {
|
|
|
|
return min < max
|
|
|
|
? angle > min && angle < max
|
2015-09-16 03:52:41 -04:00
|
|
|
// The range wraps around -PI / PI:
|
2015-09-15 13:38:28 -04:00
|
|
|
: angle > min && angle <= PI || angle >= -PI && angle < max;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate angles for all four tangents at the intersection point
|
2015-09-15 20:56:24 -04:00
|
|
|
var a1 = c1.getTangentAt(tMax, true).negate().getAngleInRadians(),
|
|
|
|
a2 = c2.getTangentAt(tMin, true).getAngleInRadians(),
|
|
|
|
a3 = c3.getTangentAt(tMax, true).negate().getAngleInRadians(),
|
|
|
|
a4 = c4.getTangentAt(tMin, true).getAngleInRadians();
|
2015-09-15 13:38:28 -04:00
|
|
|
|
|
|
|
// Count how many times curve2 angles appear between the curve1 angles
|
|
|
|
// If each pair of angles split the other two, then the edges cross.
|
|
|
|
return (isInRange(a3, a1, a2) ^ isInRange(a4, a1, a2))
|
|
|
|
&& (isInRange(a3, a2, a1) ^ isInRange(a4, a2, a1));
|
|
|
|
},
|
|
|
|
|
2014-08-16 13:24:54 -04:00
|
|
|
/**
|
2015-10-03 17:15:45 -04:00
|
|
|
* Checks if the location is an intersection with another curve and is
|
|
|
|
* part of an overlap between the two involved paths.
|
2014-08-16 13:24:54 -04:00
|
|
|
*
|
2015-10-03 17:15:45 -04:00
|
|
|
* @return {Boolean} {@true if the location is an intersection that is
|
|
|
|
* part of an overlap between the two involved paths}
|
|
|
|
* @see #isCrossing()
|
|
|
|
* @see #isTouching()
|
2014-08-16 13:24:54 -04:00
|
|
|
*/
|
2015-10-03 17:15:45 -04:00
|
|
|
isOverlap: function() {
|
2015-10-11 11:05:23 -04:00
|
|
|
return !!this._overlap;
|
2015-10-11 03:18:50 -04:00
|
|
|
}
|
|
|
|
}, Base.each(Curve.evaluateMethods, function(name) {
|
|
|
|
// Produce getters for #getTangent() / #getNormal() / #getCurvature()
|
|
|
|
if (name !== 'getPoint') {
|
|
|
|
var get = name + 'At';
|
|
|
|
this[name] = function() {
|
|
|
|
var parameter = this.getParameter(),
|
|
|
|
curve = this.getCurve();
|
|
|
|
return parameter != null && curve && curve[get](parameter, true);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}, {}),
|
|
|
|
new function() { // Scope for statics
|
|
|
|
|
|
|
|
function compare(loc1, loc2) {
|
|
|
|
var path1 = loc1.getPath(),
|
|
|
|
path2 = loc2.getPath();
|
|
|
|
return path1 === path2
|
2015-10-12 18:06:34 -04:00
|
|
|
//Sort by both index and parameter. The two values added
|
|
|
|
// together provides a convenient sorting index.
|
|
|
|
? (loc1.getIndex() + loc1.getParameter())
|
|
|
|
- (loc2.getIndex() + loc2.getParameter())
|
2015-10-11 03:18:50 -04:00
|
|
|
// Sort by path id to group all locs on same path.
|
|
|
|
: path1._id - path2._id;
|
|
|
|
}
|
2015-08-26 10:56:28 -04:00
|
|
|
|
2015-10-11 03:18:50 -04:00
|
|
|
function insert(locations, loc, merge) {
|
|
|
|
// Insert-sort by path-id, curve, parameter so we can easily merge
|
|
|
|
// duplicates with calls to equals() after.
|
|
|
|
var length = locations.length,
|
|
|
|
l = 0,
|
|
|
|
r = length - 1,
|
|
|
|
abs = Math.abs;
|
|
|
|
|
|
|
|
function search(index, dir) {
|
|
|
|
for (var i = index + dir; i >= 0 && i < length; i += dir) {
|
2015-10-13 01:27:25 -04:00
|
|
|
var loc2 = locations[i],
|
|
|
|
diff = abs(compare(loc, loc2));
|
|
|
|
// See #equals() for details of why `diff < 1` is used here.
|
|
|
|
if (diff < 1 && loc.equals(loc2))
|
2015-10-11 03:18:50 -04:00
|
|
|
return loc2;
|
2015-10-13 01:23:15 -04:00
|
|
|
// If we reach the beginning/end of the list, also compare with
|
|
|
|
// the location at the other end, as paths are circular lists.
|
|
|
|
if (i === 0 || i === length - 1) {
|
|
|
|
loc2 = locations[i === 0 ? length - 1 : 0];
|
|
|
|
if (loc.equals(loc2))
|
|
|
|
return loc2;
|
|
|
|
}
|
2015-10-13 01:27:25 -04:00
|
|
|
// Once we're outside of the range, we can stop searching.
|
|
|
|
if (diff >= 1)
|
|
|
|
break;
|
2015-10-08 17:54:00 -04:00
|
|
|
}
|
2015-10-11 03:18:50 -04:00
|
|
|
return null;
|
|
|
|
}
|
2015-10-08 17:54:00 -04:00
|
|
|
|
2015-10-11 03:18:50 -04:00
|
|
|
while (l <= r) {
|
|
|
|
var m = (l + r) >>> 1,
|
|
|
|
loc2 = locations[m],
|
|
|
|
diff = compare(loc, loc2);
|
|
|
|
// Only compare location with equals() if diff is < 1.
|
|
|
|
// See #equals() for details of why `< 1` is used here.
|
|
|
|
// NOTE: equals() takes the intersection location into account,
|
|
|
|
// while the above calculation of diff doesn't!
|
|
|
|
if (merge && abs(diff) < 1) {
|
|
|
|
// See if the two locations are actually the same, and merge if
|
|
|
|
// they are. If they aren't, we're not done yet since all
|
|
|
|
// neighbors with a diff < 1 are potential merge candidates, so
|
|
|
|
// check them too (see #search() for details)
|
|
|
|
if (loc2 = loc.equals(loc2) ? loc2
|
|
|
|
: search(m, -1) || search(m, 1)) {
|
|
|
|
// We're done, don't insert, merge with the found location
|
2015-10-11 11:05:23 -04:00
|
|
|
// instead, and carry over overlap:
|
|
|
|
if (loc._overlap) {
|
|
|
|
loc2._overlap = loc2._intersection._overlap = true;
|
2015-09-16 19:03:13 -04:00
|
|
|
}
|
2015-10-11 03:18:50 -04:00
|
|
|
return loc2;
|
2015-09-16 19:03:13 -04:00
|
|
|
}
|
2015-09-16 04:52:51 -04:00
|
|
|
}
|
2015-10-11 03:18:50 -04:00
|
|
|
if (diff < 0) {
|
|
|
|
r = m - 1;
|
|
|
|
} else {
|
|
|
|
l = m + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We didn't merge with a preexisting location, insert it now.
|
|
|
|
locations.splice(l, 0, loc);
|
|
|
|
return loc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return { statics: {
|
|
|
|
insert: insert,
|
2015-09-16 19:03:13 -04:00
|
|
|
|
|
|
|
expand: function(locations) {
|
2015-10-11 03:18:50 -04:00
|
|
|
// Create a copy since insert() keeps modifying the array and
|
|
|
|
// inserting at sorted indices.
|
2015-09-17 04:18:45 -04:00
|
|
|
var expanded = locations.slice();
|
2015-09-16 19:03:13 -04:00
|
|
|
for (var i = 0, l = locations.length; i < l; i++) {
|
2015-10-11 03:18:50 -04:00
|
|
|
insert(expanded, locations[i]._intersection, false);
|
2015-09-16 19:03:13 -04:00
|
|
|
}
|
2015-09-17 04:18:45 -04:00
|
|
|
return expanded;
|
2015-08-26 10:56:28 -04:00
|
|
|
}
|
2015-10-11 03:18:50 -04:00
|
|
|
}};
|
|
|
|
});
|