From d194f1474fcf0d8bdd4ce7b5c1a23d5bdf16b34a Mon Sep 17 00:00:00 2001 From: Freek Dijkstra Date: Thu, 15 Nov 2012 22:40:20 +0100 Subject: [PATCH 1/4] Preliminary support for Lines with rounded corners So far only rects could have rounded corners. Warning 1: this code uses arcTo() which is tagged as "problematic in Opera" elsewhere in this library. Warning 2: this code does not yet correct for very acute angles, where the radius must be decreased for the rounding to fit. Manually making the calculation is possible, but computationally intensive. Limitation: Combine dashed lines with rounded corners will not be supported (it is possible, but that requires a lot of code) --- src/shapes/Line.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shapes/Line.js b/src/shapes/Line.js index a423f7dd..5b6373a8 100644 --- a/src/shapes/Line.js +++ b/src/shapes/Line.js @@ -40,6 +40,10 @@ Kinetic.Line.prototype = { var lastY = this.attrs.points[n - 1].y; this._dashedLine(context, lastX, lastY, x, y, this.attrs.dashArray); } + else if(this.attrs.cornerRadius && n+1 < this.attrs.points.length) { + // draw line with rounded corner + context.arcTo(x, y, this.attrs.points[n + 1].x, this.attrs.points[n + 1].y, this.attrs.cornerRadius); + } else { // draw normal line context.lineTo(x, y); From 8c48884cc0b411c27ae693d2d7273bb0c3c09872 Mon Sep 17 00:00:00 2001 From: Freek Dijkstra Date: Fri, 16 Nov 2012 17:49:57 +0100 Subject: [PATCH 2/4] Check if the rounded corner fits If it doesn't fit, reduce the radius till it fits. Note: this code is not optimized yet. --- src/shapes/Line.js | 47 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/shapes/Line.js b/src/shapes/Line.js index 5b6373a8..8a54e9a6 100644 --- a/src/shapes/Line.js +++ b/src/shapes/Line.js @@ -42,7 +42,52 @@ Kinetic.Line.prototype = { } else if(this.attrs.cornerRadius && n+1 < this.attrs.points.length) { // draw line with rounded corner - context.arcTo(x, y, this.attrs.points[n + 1].x, this.attrs.points[n + 1].y, this.attrs.cornerRadius); + var radius = this.attrs.cornerRadius + var lastX = this.attrs.points[n - 1].x; + var lastY = this.attrs.points[n - 1].y; + var nextX = this.attrs.points[n + 1].x; + var nextY = this.attrs.points[n + 1].y; + var lastXD = x - lastX + var lastYD = y - lastY + var nextXD = nextX - x + var nextYD = nextY - y + + var lastangle = Math.atan2(lastXD, lastYD); + var nextangle = Math.atan2(nextXD, nextYD); + if (lastangle > nextangle) { + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle - Math.PI)/2 // Any value between -1.5 π and 0.5 π // Angle + // Relative angle between normal line and either line segment + var halfangle = Math.abs((lastangle - nextangle - Math.PI)/2) // Value is between 0 and π/2 (0 and 90°). + } else { + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle + Math.PI)/2 // Any value between -0.5 π and 1.5 π + // Relative angle between normal line and either line segment + var halfangle = Math.abs((nextangle - lastangle - Math.PI)/2) // Value is between 0 and π/2 (0 and 90°). + } + if (radius * Math.cos(halfangle) < 0.5) { + // less than half a pixel of a corner to draw. Don't bother. + // This also prevents tan(halfang) to reach infinity. + context.lineTo(x, y); + continue; + } + + if (n == 1) { + lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD); + } else { + lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD)/2; + } + if (n+1 == this.attrs.points.length) { + nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD); + } else { + nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD)/2; + } + + // no worries that tan(halfang) is infinite; that is caught above + // Reduce the radius if there is not enough space + radius = Math.min(radius, Math.min(lastlen, nextlen) * Math.tan(halfangle)) + // Draw the line + context.arcTo(x, y, nextX, nextY, radius); } else { // draw normal line From 12019cc37cb91a29b51a296d678f6fdefec4d108 Mon Sep 17 00:00:00 2001 From: Freek Dijkstra Date: Thu, 22 Nov 2012 01:21:30 +0100 Subject: [PATCH 3/4] Replace arcTo() with arc() --- src/shapes/Line.js | 87 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/src/shapes/Line.js b/src/shapes/Line.js index 8a54e9a6..8ed8602a 100644 --- a/src/shapes/Line.js +++ b/src/shapes/Line.js @@ -51,43 +51,76 @@ Kinetic.Line.prototype = { var lastYD = y - lastY var nextXD = nextX - x var nextYD = nextY - y - + + // Warning: the angle system for atan2() and arc() in the + // HTML5 canvas is different. + // For atan2(), south is 0°, counter clockwise, range -π to π + // For arc(), east is 0°, clockwise, range 0 to 2π var lastangle = Math.atan2(lastXD, lastYD); var nextangle = Math.atan2(nextXD, nextYD); - if (lastangle > nextangle) { - // Direction of the normal line bisecting the two line segments - var normangle = (lastangle + nextangle - Math.PI)/2 // Any value between -1.5 π and 0.5 π // Angle - // Relative angle between normal line and either line segment - var halfangle = Math.abs((lastangle - nextangle - Math.PI)/2) // Value is between 0 and π/2 (0 and 90°). + var anglediff = nextangle - lastangle + if (anglediff < -Math.PI) { + anglediff += 2*Math.PI + var clockwise = false; + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle - Math.PI)/2 // Value is between -1.5 π and 0.5 π + // Relative angle between normal line and either line segment + var halfangle = (Math.PI - anglediff)/2 // Value is between 0 and π/2 (0 and 90°). + // Direction in arc() coordinates, not in atan2() coordinates. + var startangle = Math.PI - lastangle // lastangle + 90 degrees clockwise, other angle system. + var endangle = Math.PI - nextangle // nextangle + 90 degrees clockwise, other angle system. + } else if (anglediff < 0) { + var clockwise = true; + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle - Math.PI)/2 // Value is between -1.5 π and 0.5 π + // Relative angle between normal line and either line segment + var halfangle = (anglediff + Math.PI)/2 // Value is between 0 and π/2 (0 and 90°). + var startangle = -lastangle // lastangle + 90 degrees counter clockwise, other angle system. + var endangle = -nextangle // nextangle + 90 degrees counter clockwise, other angle system. + } else if (anglediff < Math.PI) { + var clockwise = false; + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle + Math.PI)/2 // Value is between -0.5 π and 1.5 π + // Relative angle between normal line and either line segment + var halfangle = (Math.PI - anglediff)/2 // Value is between 0 and π/2 (0 and 90°). + var startangle = Math.PI - lastangle // lastangle + 90 degrees clockwise, other angle system. + var endangle = Math.PI - nextangle // nextangle + 90 degrees clockwise, other angle system. } else { - // Direction of the normal line bisecting the two line segments - var normangle = (lastangle + nextangle + Math.PI)/2 // Any value between -0.5 π and 1.5 π - // Relative angle between normal line and either line segment - var halfangle = Math.abs((nextangle - lastangle - Math.PI)/2) // Value is between 0 and π/2 (0 and 90°). + anglediff -= 2*Math.PI + var clockwise = true; + // Direction of the normal line bisecting the two line segments + var normangle = (lastangle + nextangle + Math.PI)/2 // Value is between -0.5 π and 1.5 π + // Relative angle between normal line and either line segment + var halfangle = (anglediff + Math.PI)/2 // Value is between 0 and π/2 (0 and 90°). + var startangle = -lastangle // lastangle + 90 degrees counter clockwise, other angle system. + var endangle = -nextangle // nextangle + 90 degrees counter clockwise, other angle system. } if (radius * Math.cos(halfangle) < 0.5) { // less than half a pixel of a corner to draw. Don't bother. // This also prevents tan(halfang) to reach infinity. context.lineTo(x, y); - continue; - } - - if (n == 1) { - lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD); - } else { - lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD)/2; - } - if (n+1 == this.attrs.points.length) { - nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD); + radius = 0 } else { - nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD)/2; + if (n == 1) { + lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD); + } else { + lastlen = Math.sqrt(lastXD*lastXD + lastYD*lastYD)/2; + } + if (n+1 == this.attrs.points.length) { + nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD); + } else { + nextlen = Math.sqrt(nextXD*nextXD + nextYD*nextYD)/2; + } + // no worries that tan(halfang) is infinite; that is caught above + // Reduce the radius if there is not enough space + radius = Math.min(radius, Math.min(lastlen, nextlen) * Math.tan(halfangle)) + + // distance from x,y to center of the arc + var arcdist = radius/Math.sin(halfangle) + var arcX = x + arcdist*Math.sin(normangle) + var arcY = y + arcdist*Math.cos(normangle) + context.arc(arcX, arcY, radius, startangle, endangle, !clockwise); } - - // no worries that tan(halfang) is infinite; that is caught above - // Reduce the radius if there is not enough space - radius = Math.min(radius, Math.min(lastlen, nextlen) * Math.tan(halfangle)) - // Draw the line - context.arcTo(x, y, nextX, nextY, radius); } else { // draw normal line From 56ba4e88db43631e83a174a7031a3516579ef6b3 Mon Sep 17 00:00:00 2001 From: Freek Dijkstra Date: Sat, 24 Nov 2012 21:29:41 +0100 Subject: [PATCH 4/4] =?UTF-8?q?Special=20case=20for=20rounded=20corners:?= =?UTF-8?q?=20180=C2=B0=20turn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shapes/Line.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/shapes/Line.js b/src/shapes/Line.js index 8ed8602a..5d4d8495 100644 --- a/src/shapes/Line.js +++ b/src/shapes/Line.js @@ -115,11 +115,19 @@ Kinetic.Line.prototype = { // Reduce the radius if there is not enough space radius = Math.min(radius, Math.min(lastlen, nextlen) * Math.tan(halfangle)) - // distance from x,y to center of the arc - var arcdist = radius/Math.sin(halfangle) - var arcX = x + arcdist*Math.sin(normangle) - var arcY = y + arcdist*Math.cos(normangle) - context.arc(arcX, arcY, radius, startangle, endangle, !clockwise); + if (radius <= 0) { + // special case: the line segments make a 180° turn + var arcdist = Math.min(lastlen, nextlen) + var arcX = x + arcdist*Math.sin(normangle) + var arcY = y + arcdist*Math.cos(normangle) + context.lineTo(arcX, arcY); + } else { + // distance from x,y to center of the arc + var arcdist = radius/Math.sin(halfangle) + var arcX = x + arcdist*Math.sin(normangle) + var arcY = y + arcdist*Math.cos(normangle) + context.arc(arcX, arcY, radius, startangle, endangle, !clockwise); + } } } else {