I searched for it for a long time, and I have already seen many questions including the two:
How to draw Arc between two points on the Canvas?
How to draw a curved line between 2 points on canvas?
Although they seem like the same question, I'm very sure they are not the same. In the first question, the center of the circle is known, and in the second, it draws a Bezier curve not an arc.
Now we have two points A
and B
and the curve radius given, how to draw the arc as the image shows?
Since Path.arcTo
's required arguments are RectF
, startAngle
and sweepAngle
, there seems hardly a easy way.
I now have my solution, I'll show that in the answer below.
Since the solution is so complex, I wonder if there is a easier way to solve it?
Probably there is no easier way. All what can do would be to refine your solution by geometrical approach. Since the center of circle is always on the perpendicular bisector of the chord, it's not required to solve so generalized equations.
By the way, it's not clear how you defined Clockwise/Counter-clockwise. Arc's winding direction should be determined independently of node-placements (=A, B's coordinates).
As is shown in the figure below, on the straight path from A to B, the center O is to be placed righthandside(CW) or lefthandside(CCW). That's all.
And, some more aspects to be altered:
After all the code can be slightly simplified as follows.
@Throws(Exception::class)
private fun Path.arcFromTo2(
x1: Float, y1: Float, x2: Float, y2: Float, r: Float,
clockwise: Boolean = true
) {
val d = PointF((x2 - x1) * 0.5F, (y2 - y1) * 0.5F)
val a = d.length()
if (a > r) throw Exception()
val side = if (clockwise) 1 else -1
val oc = sqrt(r * r - a * a)
val ox = (x1 + x2) * 0.5F - side * oc * d.y / a
val oy = (y1 + y2) * 0.5F + side * oc * d.x / a
val startAngle = atan2(y1 - oy, x1 - ox) * 180F / Math.PI.toFloat()
val sweepAngle = side * 2.0F * asin(a / r) * 180F / Math.PI.toFloat()
arcTo(
ox - r, oy - r, ox + r, oy + r,
startAngle, sweepAngle,
false
)
}