1. find the center of the circle
This can be solved by a binary quadratic equation, as the image shows:

Though there are other solutions, anyway, now the position of circle center is known.
2. calculate start angle and sweep angle
According to the circle center, RectF
is easy to know.
Now calculate startAngle
and sweepAngle
.

Via geometric methods, we can calculate the startAngle
and sweepAngle
:
val startAngle = acos((x1 - x0) / r) / Math.PI.toFloat() * 180
val endAngle = acos((x2 - x0) / r) / Math.PI.toFloat() * 180
val sweepAngle = endAngle - startAngle
In this case, x1
is the x-coordinary of point A, x2
if the x-coordinary of point B, and r
is the curve radius of the arc. (There are possible results, the other is [-startAngle, startAngle - endAngle]
. Choose one according to actual situation.)
Thus, we get all the required arguments for Path.arcTo
method and we can draw the arc now.
3. kotlin code
the entire code of the help function:
/**
* Append the arc which is starting at ([x1], [y1]), ending at ([x2], [y2])
* and with the curve radius [r] to the path.
* The Boolean value [clockwise] shows whether the process drawing the arc
* is clockwise.
*/
@Throws(Exception::class)
private fun Path.arcFromTo(
x1: Float, y1: Float, x2: Float, y2: Float, r: Float,
clockwise: Boolean = true
) {
val c = centerPos(x1, y1, x2, y2, r, clockwise) // circle centers
// RectF borders
val left = c.x - r
val top = c.y - r
val right = c.x + r
val bottom = c.y + r
val startAngle = acos((x1 - c.x) / r) / Math.PI.toFloat() * 180
val endAngle = acos((x2 - c.x) / r) / Math.PI.toFloat() * 180
arcTo(
left, top, right, bottom,
if (clockwise) startAngle else -startAngle,
if (clockwise) endAngle - startAngle else startAngle - endAngle,
false
)
}
// use similar triangles to calculate circle center
@Throws(Exception::class)
private fun centerPos(
x1: Float, y1: Float, x2: Float, y2: Float, r: Float,
clockwise: Boolean
): Point {
val ab = ((x1 - x2).p2 + (y1 - y2).p2).sqrt
if (ab > r * 2) throw Exception("No circle fits the condition.")
val a = ab / 2
val oc = (r.p2 - a.p2).sqrt
val dx = (oc * (y2 - y1) / ab).absoluteValue.toInt()
val dy = (oc * (x2 - x1) / ab).absoluteValue.toInt()
val cx = ((x1 + x2) / 2).toInt()
val cy = ((y1 + y2) / 2).toInt()
return if (x1 >= x2 && y1 >= y2 || x1 <= x2 && y1 <= y2)
if (clockwise) Point(cx + dx, cy - dy) else Point(cx - dx, cy + dy)
else
if (clockwise) Point(cx - dx, cy - dy) else Point(cx + dx, cy + dy)
}