An exact circular arc

Contents

An exact circular arc#

Polynomial Bézier curves cannot represent a nonconstant circular arc exactly. A rational quadratic can. For the unit quarter circle, choose

\[\mathbf P_0=(1,0),\quad \mathbf P_1=(1,1),\quad \mathbf P_2=(0,1), \qquad (w_0,w_1,w_2)=(1,1/\sqrt2,1).\]

The first and last points lie on the circle; the middle point is the intersection of their tangent lines. Providing P and W alone creates a rational Bézier curve of degree two.

Run python demos/documentation/circular_arc.py, or download the script.

"""Represent an exact quarter circle with a rational quadratic Bezier."""
import numpy as np
import matplotlib.pyplot as plt
import nurbspy as nrb

nrb.set_plot_options()

P = np.array([[1., 1., 0.],
              [0., 1., 1.]])
W = np.array([1., 1 / np.sqrt(2), 1.])
# Without a degree or knot vector, these inputs define rational Bezier.
arc = nrb.NurbsCurve(control_points=P, weights=W)
polynomial = nrb.NurbsCurve(control_points=P)
u = np.linspace(0., 1., 301)
C = arc.get_value(u)
radius_error = np.max(np.abs(np.linalg.norm(C, axis=0) - 1.))
curvature = arc.get_curvature(u)

print(f"Maximum radius error: {radius_error:.2e}")
print(f"Maximum curvature error: {np.max(np.abs(curvature - 1.)):.2e}")
print("Midpoint:", arc.get_value(0.5)[:, 0].round(8))

fig, ax = plt.subplots(figsize=(5.5, 5), layout="constrained")
arc.plot_control_points(fig, ax, color="0.6")
ax.lines[-1].set_label("Control polygon")
polynomial.plot_curve(fig, ax, linestyle="--", color=nrb.COLORS_MATLAB[1])
ax.lines[-1].set_label("Polynomial quadratic")
arc.plot_curve(fig, ax, linewidth=2, color=nrb.COLORS_MATLAB[0])
ax.lines[-1].set_label("Rational quadratic: exact circle")
ax.set(xlabel="x", ylabel="y", title="An exact unit quarter circle")
ax.set_aspect("equal", adjustable="box")
ax.legend(fontsize=12)
ax.grid(alpha=0.2)
plt.show()

Output#

The midpoint is [0.70710678 0.70710678]. The radius and curvature are both one at every sample, with numerical errors close to machine precision. Removing the weights produces a polynomial quadratic whose midpoint is \((0.75,0.75)\), outside the unit circle.

Exact rational quarter circle compared with a polynomial quadratic using the same three control points.

Weights make the rational quadratic follow the circle exactly.#

For other radii, planes, or angle spans, use nurbspy.nurbs_curve_circular_arc.CircularArc. The constructor returns a helper object; its .NurbsCurve attribute holds the curve. Angles are in radians, while the resulting curve is evaluated with a normalized parameter \(u\in[0,1]\).