Various changes on #isLinear(), #hasHandles() & co

Relates to #652

- Implement #hasHandles() on Path, Segment and Curve
- Remove Path#isPolygon()
- Define #isLinear() consistently across Path, Segment and Curve.
- Introduce new Segment#isStraight()
This commit is contained in:
Jürg Lehni 2015-08-17 14:31:23 +02:00
parent 97ca1f6ff8
commit f8314f927e
6 changed files with 99 additions and 40 deletions

View file

@ -291,14 +291,27 @@ var Curve = Base.extend(/** @lends Curve# */{
},
/**
* Checks if this curve is linear, meaning it does not define any curve
* handle.
* Checks if this curve defines any curve handle.
*
* @return {Boolean} {@true if the curve has handles defined}
* @see Segment#hasHandles()
* @see Path#hasHandles()
*/
hasHandles: function() {
return !this._segment1._handleOut.isZero()
|| !this._segment2._handleIn.isZero();
},
/**
* Checks if this curve appears as a line. This can mean that it has no
* handles defined, or that the handles run collinear with the line.
*
* @return {Boolean} {@true if the curve is linear}
* @see Segment#isLinear()
* @see Path#isLinear()
*/
isLinear: function() {
return this._segment1._handleOut.isZero()
&& this._segment2._handleIn.isZero();
return this._segment1.isLinear();
},
// DOCS: Curve#getIntersections()
@ -643,10 +656,19 @@ statics: {
return v;
},
isLinear: function(v) {
hasHandles: function(v) {
var isZero = Numerical.isZero;
return isZero(v[0] - v[2]) && isZero(v[1] - v[3])
&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]);
return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3])
&& isZero(v[4] - v[6]) && isZero(v[5] - v[7]));
},
isLinear: function(v) {
// See Segment#isLinear():
var p1x = v[0], p1y = v[1],
p2x = v[6], p2y = v[7],
l = new Point(p2x - p1x, p2y - p1y);
return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y))
&& l.isCollinear(new Point(v[4] - p2x, v[5] - p2y));
},
isFlatEnough: function(v, tolerance) {

View file

@ -368,14 +368,40 @@ var Path = PathItem.extend(/** @lends Path# */{
return this._segments.length === 0;
},
isPolygon: function() {
for (var i = 0, l = this._segments.length; i < l; i++) {
if (!this._segments[i].isLinear())
/**
* Checks if this path consists of only linear curves. This can mean that
* the curves have no handles defined, or that the handles run collinear
* with the line.
*
* @return {Boolean} {@true if the path is entirely linear}
* @see Segment#isLinear()
* @see Curve#isLinear()
*/
isLinear: function() {
var segments = this._segments;
for (var i = 0, l = segments.length; i < l; i++) {
if (!segments[i].isLinear())
return false;
}
return true;
},
/**
* Checks if none of the curves in the path define any curve handles.
*
* @return {Boolean} {@true if the path contains no curve handles}
* @see Segment#hasHandles()
* @see Curve#hasHandles()
*/
hasHandles: function() {
var segments = this._segments;
for (var i = 0, l = segments.length; i < l; i++) {
if (segments[i].hasHandles())
return true;
}
return false;
},
_transformContent: function(matrix) {
var coords = new Array(6);
for (var i = 0, l = this._segments.length; i < l; i++)
@ -1402,7 +1428,7 @@ var Path = PathItem.extend(/** @lends Path# */{
// See if actually have any curves in the path. Differentiate
// between straight objects (line, polyline, rect, and polygon) and
// objects with curves(circle, ellipse, roundedRectangle).
if (this.isPolygon() && segments.length === 4
if (!this.hasHandles() && segments.length === 4
&& isCollinear(0, 2) && isCollinear(1, 3) && isOrthogonal(1)) {
type = Shape.Rectangle;
size = new Size(getDistance(0, 3), getDistance(0, 1));

View file

@ -465,12 +465,11 @@ PathItem.inject(new function() {
// Add the path to the result, while avoiding stray segments and
// incomplete paths. The amount of segments for valid paths depend
// on their geometry:
// - Closed paths with only straight lines (polygons) need more than
// two segments.
// - Closed paths with curves can consist of only one segment.
// - Open paths need at least two segments.
// - Closed paths with only straight lines need more than 2 segments
// - Closed paths with curves can consist of only one segment
// - Open paths need at least two segments
if (path._segments.length >
(path._closed ? path.isPolygon() ? 2 : 0 : 1))
(path._closed ? path.isLinear() ? 2 : 0 : 1))
paths.push(path);
}
return paths;

View file

@ -145,8 +145,8 @@ var Segment = Base.extend(/** @lends Segment# */{
},
_serialize: function(options) {
// If the Segment is linear, only serialize point, otherwise handles too
return Base.serialize(this.isLinear() ? this._point
// If it is straight, only serialize point, otherwise handles too.
return Base.serialize(this.isStraight() ? this._point
: [this._point, this._handleIn, this._handleOut],
options, true);
},
@ -209,8 +209,6 @@ var Segment = Base.extend(/** @lends Segment# */{
var point = Point.read(arguments);
// See #setPoint:
this._handleIn.set(point.x, point.y);
// Update corner accordingly
// this.corner = !this._handleIn.isCollinear(this._handleOut);
},
/**
@ -228,33 +226,47 @@ var Segment = Base.extend(/** @lends Segment# */{
var point = Point.read(arguments);
// See #setPoint:
this._handleOut.set(point.x, point.y);
// Update corner accordingly
// this.corner = !this._handleIn.isCollinear(this._handleOut);
},
// TODO: Rename this to #corner?
/**
* Specifies whether the segment has no handles defined, meaning it connects
* two straight lines.
* Checks whether the segment has curve handles defined, meaning it is not
* a straight segment.
*
* @type Boolean
* @bean
* @return {Boolean} {@true if the segment has handles defined}
* @see Curve#hasHandles()
* @see Path#hasHandles()
*/
isLinear: function() {
hasHandles: function() {
return !this.isStraight();
},
/**
* Checks whether the segment is straight, meaning it has no curve
* handles defined.
* If two straight segments are adjacent to each other, the curve between
* them will be a straight line.
*
* @return {Boolean} {@true if the segment is straight}
*/
isStraight: function() {
return this._handleIn.isZero() && this._handleOut.isZero();
},
setLinear: function(linear) {
if (linear) {
this._handleIn.set(0, 0);
this._handleOut.set(0, 0);
} else {
// TODO: smooth() ?
}
/**
* Checks if the curve that starts in this segment appears as a line. This
* can mean that it has no handles defined, or that the handles run
* collinear with the line.
*
* @return {Boolean} {@true if the curve is linear}
* @see Curve#isLinear()
* @see Path#isLinear()
*/
isLinear: function() {
var next = this.getNext(),
l = next._point.subtract(this._point);
return l.isCollinear(this._handleOut) && l.isCollinear(next._handleIn);
},
// DOCS: #isCollinear(segment), #isOrthogonal(), #isArc()
/**
* Returns true if the the two segments are the beginning of two lines and
* if these two lines are running parallel.

View file

@ -125,7 +125,7 @@ new function() {
attrs = getTransform(item._matrix);
if (segments.length === 0)
return null;
if (matchShapes && item.isPolygon()) {
if (matchShapes && !item.hasHandles()) {
if (segments.length >= 3) {
type = item._closed ? 'polygon' : 'polyline';
var parts = [];

View file

@ -107,10 +107,10 @@ test('Curve list after removing a segment - 2', function() {
}, 1, 'After removing the last segment, we should be left with one curve');
});
test('Splitting a straight path should produce linear segments', function() {
test('Splitting a straight path should produce straight segments', function() {
var path = new Path.Line([0, 0], [50, 50]);
var path2 = path.split(0, 0.5);
equals(function() {
return path2.firstSegment.linear;
return path2.firstSegment.isStraight();
}, true);
});