Simplify handling of strokeJoin / strokeCap geometries.

This commit is contained in:
Jürg Lehni 2017-02-22 22:22:23 +01:00
parent b40efbf6db
commit f19a50093b

View file

@ -1673,7 +1673,7 @@ var Path = PathItem.extend(/** @lends Path# */{
if (strokeRadius > 0) { if (strokeRadius > 0) {
join = style.getStrokeJoin(); join = style.getStrokeJoin();
cap = style.getStrokeCap(); cap = style.getStrokeCap();
miterLimit = strokeRadius * style.getMiterLimit(); miterLimit = style.getMiterLimit();
// Add the stroke radius to tolerance padding, taking // Add the stroke radius to tolerance padding, taking
// #strokeScaling into account through _getStrokeMatrix(). // #strokeScaling into account through _getStrokeMatrix().
strokePadding = strokePadding.add( strokePadding = strokePadding.add(
@ -1723,21 +1723,22 @@ var Path = PathItem.extend(/** @lends Path# */{
function checkSegmentStroke(segment) { function checkSegmentStroke(segment) {
// Handle joins / caps that are not round specificelly, by // Handle joins / caps that are not round specificelly, by
// hit-testing their polygon areas. // hit-testing their polygon areas.
if (join !== 'round' || cap !== 'round') { var isJoin = closed || segment._index > 0
&& segment._index < numSegments - 1;
if ((isJoin ? join : cap) === 'round') {
// Round join / cap is easy to handle.
return isCloseEnough(segment._point, strokePadding);
} else {
// Create an 'internal' path without id and outside the scene // Create an 'internal' path without id and outside the scene
// graph to run the hit-test on it. // graph to run the hit-test on it.
area = new Path({ internal: true, closed: true }); area = new Path({ internal: true, closed: true });
if (closed || segment._index > 0 if (isJoin) {
&& segment._index < numSegments - 1) { // It's a join. See that it's not a round one (collinear
// It's a join. See that it's not a round one (one of // handles).
// the handles has to be zero too for this!) // _addBevelJoin() handles both 'bevel' and 'miter' joins.
if (join !== 'round' && (segment._handleIn.isZero() Path._addBevelJoin(segment, join, strokeRadius,
|| segment._handleOut.isZero())) miterLimit, null, strokeMatrix, addToArea, true);
// _addBevelJoin() handles both 'bevel' and 'miter'! } else if (cap === 'square') {
Path._addBevelJoin(segment, join, strokeRadius,
miterLimit, null, strokeMatrix, addToArea, true);
} else if (cap !== 'round') {
// It's a cap
Path._addSquareCap(segment, cap, strokeRadius, null, Path._addSquareCap(segment, cap, strokeRadius, null,
strokeMatrix, addToArea, true); strokeMatrix, addToArea, true);
} }
@ -1752,8 +1753,6 @@ var Path = PathItem.extend(/** @lends Path# */{
&& isCloseEnough(loc.getPoint(), tolerancePadding); && isCloseEnough(loc.getPoint(), tolerancePadding);
} }
} }
// Fallback scenario is a round join / cap.
return isCloseEnough(segment._point, strokePadding);
} }
// If we're asked to query for segments, ends or handles, do all that // If we're asked to query for segments, ends or handles, do all that
@ -1790,7 +1789,8 @@ var Path = PathItem.extend(/** @lends Path# */{
if (!loc && join === 'miter' && numSegments > 1) { if (!loc && join === 'miter' && numSegments > 1) {
for (var i = 0; i < numSegments; i++) { for (var i = 0; i < numSegments; i++) {
var segment = segments[i]; var segment = segments[i];
if (point.getDistance(segment._point) <= miterLimit if (point.getDistance(segment._point)
<= miterLimit * strokeRadius
&& checkSegmentStroke(segment)) { && checkSegmentStroke(segment)) {
loc = segment.getLocation(); loc = segment.getLocation();
break; break;
@ -2695,7 +2695,7 @@ statics: {
var strokeRadius = strokeWidth / 2, var strokeRadius = strokeWidth / 2,
join = style.getStrokeJoin(), join = style.getStrokeJoin(),
cap = style.getStrokeCap(), cap = style.getStrokeCap(),
miterLimit = strokeRadius * style.getMiterLimit(), miterLimit = style.getMiterLimit(),
// Create a rectangle of padding size, used for union with bounds // Create a rectangle of padding size, used for union with bounds
// further down // further down
joinBounds = new Rectangle(new Size(strokePadding)); joinBounds = new Rectangle(new Size(strokePadding));
@ -2720,6 +2720,7 @@ statics: {
&& handleIn.isCollinear(handleOut)) { && handleIn.isCollinear(handleOut)) {
addRound(segment); addRound(segment);
} else { } else {
// _addBevelJoin() handles both 'bevel' and 'miter' joins.
Path._addBevelJoin(segment, join, strokeRadius, miterLimit, Path._addBevelJoin(segment, join, strokeRadius, miterLimit,
matrix, strokeMatrix, addPoint); matrix, strokeMatrix, addPoint);
} }
@ -2729,6 +2730,7 @@ statics: {
if (cap === 'round') { if (cap === 'round') {
addRound(segment); addRound(segment);
} else { } else {
// _addSquareCap() handles both 'square' and 'butt' caps.
Path._addSquareCap(segment, cap, strokeRadius, matrix, Path._addSquareCap(segment, cap, strokeRadius, matrix,
strokeMatrix, addPoint); strokeMatrix, addPoint);
} }
@ -2806,10 +2808,9 @@ statics: {
normal1 = normal1.negate(); normal1 = normal1.negate();
normal2 = normal2.negate(); normal2 = normal2.negate();
} }
if (isArea) { if (isArea)
addPoint(point); addPoint(point);
addPoint(point.add(normal1)); addPoint(point.add(normal1));
}
if (join === 'miter') { if (join === 'miter') {
// Intersect the two lines // Intersect the two lines
var corner = new Line(point.add(normal1), var corner = new Line(point.add(normal1),
@ -2819,15 +2820,11 @@ statics: {
), true); ), true);
// See if we actually get a bevel point and if its distance is below // See if we actually get a bevel point and if its distance is below
// the miterLimit. If not, make a normal bevel. // the miterLimit. If not, make a normal bevel.
if (corner && point.getDistance(corner) <= miterLimit) { if (corner && point.getDistance(corner) <= miterLimit * radius) {
addPoint(corner); addPoint(corner);
if (!isArea)
return;
} }
} }
// Produce a normal bevel // Produce a normal bevel
if (!isArea)
addPoint(point.add(normal1));
addPoint(point.add(normal2)); addPoint(point.add(normal2));
}, },
@ -2839,18 +2836,17 @@ statics: {
// Style#strokeScaling. // Style#strokeScaling.
var point = segment._point.transform(matrix), var point = segment._point.transform(matrix),
loc = segment.getLocation(), loc = segment.getLocation(),
// NOTE: normal is normalized, so multiply instead of normalize.
normal = loc.getNormal().multiply(radius).transform(strokeMatrix); normal = loc.getNormal().multiply(radius).transform(strokeMatrix);
if (isArea) {
addPoint(point.subtract(normal));
addPoint(point.add(normal));
}
// For square caps, we need to step away from point in the direction of // For square caps, we need to step away from point in the direction of
// the tangent, which is the rotated normal. // the tangent, which is the rotated normal.
// Checking loc.getTime() for 0 is to see whether this is the first // Checking loc.getTime() for 0 is to see whether this is the first
// or the last segment of the open path, in order to determine in which // or the last segment of the open path, in order to determine in which
// direction to move the point. // direction to move the point.
if (cap === 'square') { if (cap === 'square') {
if (isArea) {
addPoint(point.subtract(normal));
addPoint(point.add(normal));
}
point = point.add(normal.rotate( point = point.add(normal.rotate(
loc.getTime() === 0 ? -90 : 90)); loc.getTime() === 0 ? -90 : 90));
} }