问题
I have three points that are not on the same line, originally I want to draw a circle arc through these three points, which I did. But chrome is actually not drawing real circle but using several bezier curves to pretend it is circle, because bezier curves are cheap. If chrome is doing that as a middle man, why ain't I drawing circle-like bezier myself (two bezier, from point 1 to middle point, middle point to point 3)? That would be much cleaner, and cheap ( 2 compared to unknown number of bezier curves browser decides). That is where I am stuck, how? where should the "controlling points" be?
Here is my old draw arc function in javascript
drawArc = function(startPoint, thirdPoint, endPoint){
var ctx = this.ctx;
ctx.lineWidth = this.strokeWidth;
ctx.strokeStyle = this.strokeColor;
var centerObject = circleCenter( new Point(startPoint.x, startPoint.y),
new Point(thirdPoint.x, thirdPoint.y),
new Point(endPoint.x, endPoint.y) );
var centerX = centerObject.x;
var centerY = centerObject.y;
var r = centerObject.r
var angle = Math.atan2(centerX-startPoint.x, centerY-startPoint.y);
// console.log(centerObject);
if (!angle){
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
ctx.lineTo(endPoint.x, endPoint.y);
} else {
if( angle > Math.PI/2) {
ctx.beginPath();
ctx.arc(centerX, centerY, r, Math.PI * 1.5-angle, Math.PI * 1.5 + angle, true);
} else {
ctx.beginPath();
ctx.arc(centerX, centerY, r, Math.PI * 1.5-angle, Math.PI * 1.5 + angle, false);
}
}
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
var circleCenter = function(startPoint, thirdPoint, endPoint){
var dy1 = thirdPoint.y - startPoint.y;
var dx1 = thirdPoint.x - startPoint.x;
var dy2 = endPoint.y - thirdPoint.y;
var dx2 = endPoint.x - thirdPoint.x;
var aSlope = dy1/dx1;
var bSlope = dy2/dx2;
var centerX = (aSlope*bSlope*(startPoint.y - endPoint.y) + bSlope*(startPoint.x + thirdPoint.x)
- aSlope*(thirdPoint.x+endPoint.x) )/( 2* (bSlope-aSlope) );
var centerY = -1*(centerX - (startPoint.x+thirdPoint.x)/2)/aSlope + (startPoint.y+thirdPoint.y)/2;
var r = dist(centerX, centerY, startPoint.x, startPoint.y)
return {
x: centerX,
y: centerY,
r: r
};
}
can anyone help me rewrite drawArc function to use canvas bezierCurveTo() Method instead arc()?
my code example is here: http://codepen.io/wentin/pen/VYegqq
回答1:
You can approximate a circle using 4 cubic Bezier curves...but it's not a perfect circle ;-)
Example code and a Demo:
You can use the relationships between the radius & constant c
to the start, end and control points to calculate your own desired control points. This Bezier approximated circle is drawn around the origin [0,0] so you will, of course, translate to the centerpoint of your specific circle.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
ctx.lineWidth=3;
// refined from 0.551915024494 thanks to Pomax
var c=0.5522847498307933984022516322796 ;
var cx=150; // center x
var cy=150; // center y
var r=100; // radius
drawBezierCircle(cx,cy,r);
function drawBezierCircle(cx,cy,r){
ctx.translate(cx,cy); // translate to centerpoint
ctx.beginPath();
ctx.moveTo(0,-r);
ctx.bezierCurveTo(
c*r,-r,
r,-c*r,
r,0
);
ctx.strokeStyle='red';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(r,0);
ctx.bezierCurveTo(
r,c*r,
c*r,r,
0,r
);
ctx.strokeStyle='green';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0,r);
ctx.bezierCurveTo(
-c*r,r,
-r,c*r,
-r,0
);
ctx.strokeStyle='blue';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-r,0);
ctx.bezierCurveTo(
-r,-c*r,
-c*r,-r,
0,-r
);
ctx.strokeStyle='gold';
ctx.stroke();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
回答2:
You can use the following ways to find the control points of a cubic Bezier curve for approximating a circular arc with end points P0, P1, radius R and angular span A:
Denoting the control points as Q0, Q1, Q2 and Q3, then
Q0=P0,
Q3=P1,
Q1=P0 + L * T0
Q2=P1 - L * T1
where T0 and T1 are the unit tangent vector of the circular arc at P0 and P1 and L = (4/3)*tan(A/4).
Please note that the approximation error will grow as the angular span A gets bigger. So, if your circular arc defined by the 3 points has a relatively small angular span, you can even use a single cubic Bezier curve to approximate it with good precision. Similarly, if you always use two Bezier curves (one curve between each two points) to approximate the circular arc, then you might end up with a not-so-good approximation. If Chrome is using several Bezier curves to draw a circular arc, precision might be the reason that the number of Bezier curves is not a fixed value.
来源:https://stackoverflow.com/questions/28229695/draw-2-bezier-curves-through-3-points-to-mimic-a-circle-arc