Add tolerance argument to Path#join(path, tolerance)

This commit is contained in:
Jürg Lehni 2016-06-11 12:41:23 +02:00
parent 68be3f102e
commit de8b626033
4 changed files with 61 additions and 42 deletions

View file

@ -168,6 +168,7 @@ contribute to the code.
- Add new options to `#exportSVG()` to control output bounds and transformation
matrix (#972).
- Allow `Item#position` to be selected via `Item#position.selected` (#980).
- Add `tolerance` argument to `Path#join(path, tolerance)`.
### Fixed
- Fix calculations of `Item#strokeBounds` for all possible combinations of

View file

@ -694,7 +694,7 @@ var Point = Base.extend(/** @lends Point# */{
isClose: function(/* point, tolerance */) {
var point = Point.read(arguments),
tolerance = Base.read(arguments);
return this.getDistance(point) < tolerance;
return this.getDistance(point) <= tolerance;
},
/**

View file

@ -1090,15 +1090,19 @@ var Path = PathItem.extend(/** @lends Path# */{
return location != null ? this.splitAt(location) : null;
},
// DOCS: document Path#join(path) in more detail.
// DOCS: document Path#join() (joining with itself)
// TODO: Consider adding a distance / tolerance parameter for merging.
/**
* Joins the path with the specified path, which will be removed in the
* process.
* Joins the path with the other specified path, which will be removed in
* the process. They can be joined if the first or last segments of either
* path lie in the same location. Locations are optionally compare with a
* provide `tolerance` value.
*
* @param {Path} path the path to join this path with
* @return {Path} the joined path
* If `null` or `this` is passed as the other path, the path will be joined
* with itself if the first and last segment are in the same location.
*
* @param {Path} path the path to join this path with; `null` or `this` to
* join the path with itself
* @param {Number} [tolerance=0] the tolerance with which to decide if two
* segments are to be considered the same location when joining
*
* @example {@paperscript}
* // Joining two paths:
@ -1159,25 +1163,26 @@ var Path = PathItem.extend(/** @lends Path# */{
* // Select the path to show that they have joined:
* path.selected = true;
*/
join: function(path) {
if (path) {
join: function(path, tolerance) {
var epsilon = tolerance || 0;
if (path && path !== this) {
var segments = path._segments,
last1 = this.getLastSegment(),
last2 = path.getLastSegment();
if (!last2) // an empty path?
return this;
if (last1 && last1._point.equals(last2._point))
if (last1 && last1._point.isClose(last2._point, epsilon))
path.reverse();
var first2 = path.getFirstSegment();
if (last1 && last1._point.equals(first2._point)) {
if (last1 && last1._point.isClose(first2._point, epsilon)) {
last1.setHandleOut(first2._handleOut);
this._add(segments.slice(1));
} else {
var first1 = this.getFirstSegment();
if (first1 && first1._point.equals(first2._point))
if (first1 && first1._point.isClose(first2._point, epsilon))
path.reverse();
last2 = path.getLastSegment();
if (first1 && first1._point.equals(last2._point)) {
if (first1 && first1._point.isClose(last2._point, epsilon)) {
first1.setHandleIn(last2._handleIn);
// Prepend all segments from path except the last one.
this._add(segments.slice(0, segments.length - 1), 0);
@ -1195,7 +1200,7 @@ var Path = PathItem.extend(/** @lends Path# */{
// only if its ends touch.
var first = this.getFirstSegment(),
last = this.getLastSegment();
if (first !== last && first._point.equals(last._point)) {
if (first !== last && first._point.isClose(last._point, epsilon)) {
first.setHandleIn(last._handleIn);
last.remove();
this.setClosed(true);

View file

@ -57,57 +57,70 @@ test('curve.getTimeAt() with straight curve', function() {
equals(t, 0.3869631475722452);
});
test('path.join(path)', function() {
var path = new Path();
path.add(0, 0);
path.add(10, 0);
test('path1.join(path2)', function() {
var path1 = new Path();
path1.add(0, 0);
path1.add(10, 0);
var path2 = new Path();
path2.add(10, 0);
path2.add(20, 10);
path.join(path2);
equals(path.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } }');
equals(function() {
return paper.project.activeLayer.children.length;
}, 1);
path1.join(path2);
equals(path1.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } }');
equals(function() { return paper.project.activeLayer.children.length; }, 1);
var path = new Path();
path.add(0, 0);
path.add(10, 0);
var path1 = new Path();
path1.add(0, 0);
path1.add(10, 0);
var path2 = new Path();
path2.add(20, 10);
path2.add(10, 0);
path.join(path2);
equals(path.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } }');
path1.join(path2);
equals(path1.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } }');
var path = new Path();
path.add(0, 0);
path.add(10, 0);
var path1 = new Path();
path1.add(0, 0);
path1.add(10, 0);
var path2 = new Path();
path2.add(30, 10);
path2.add(40, 0);
path.join(path2);
equals(path.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 30, y: 10 } },{ point: { x: 40, y: 0 } }');
path1.join(path2);
equals(path1.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 30, y: 10 } },{ point: { x: 40, y: 0 } }');
var path = new Path();
path.add(0, 0);
path.add(10, 0);
path.add(20, 10);
var path1 = new Path();
path1.add(0, 0);
path1.add(10, 0);
path1.add(20, 10);
var path2 = new Path();
path2.add(0, 0);
path2.add(10, 5);
path2.add(20, 10);
path.join(path2);
path1.join(path2);
equals(path1.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } },{ point: { x: 10, y: 5 } }');
equals(function() { return path1.closed; }, true);
});
test('path1.join(path2, tolerance)', function() {
var path1 = new Path();
path1.add(0, 0);
path1.add(10, 0);
var path2 = new Path();
path2.add(path1.lastSegment.point.add(1e-14));
path2.add(20, 10);
equals(path.segments.toString(), '{ point: { x: 0, y: 0 } },{ point: { x: 10, y: 0 } },{ point: { x: 20, y: 10 } },{ point: { x: 10, y: 5 } }');
equals(function() {
return path.closed;
}, true);
return path1.clone().join(path2.clone(), 0).segments.length;
}, 4);
equals(function() {
return path1.clone().join(path2.clone(), 1e-12).segments.length;
}, 3);
});
test('path.remove()', function() {