@@ -65,6 +65,7 @@ import IntervalSets.endpoints
6565
6666export Polygon, ClippedPolygon, Ellipse, Circle
6767export circle,
68+ circle_polygon,
6869 clip,
6970 cliptree,
7071 circularapprox,
@@ -169,14 +170,52 @@ function convert(::Type{Ellipse{T}}, e::Ellipse{S}) where {T, S}
169170 return Ellipse {T} (convert (Point{T}, e. center), convert (NTuple{2 , T}, e. radii), e. angle)
170171end
171172
172- function to_polygons (e:: Ellipse ; Δθ= 5 °, kwargs... )
173- r =
174- θ ->
175- (e. radii[1 ] * e. radii[2 ]) /
176- sqrt ((e. radii[2 ] * cos (θ))^ 2 + (e. radii[1 ] * sin (θ))^ 2 )
177- # θ₀ = atand(e.axis[2], e.axis[1])°
178- p = θ -> e. center + r (θ - e. angle) .* Point (cos (θ), sin (θ))
179- return Polygon (p .(0 : Δθ:(360 ° - Δθ)))
173+ """
174+ ellipse_curvature(e::Ellipse, θ)
175+
176+ Compute the curvature of an ellipse at parameter θ.
177+ For an ellipse with semi-major axis `a` and semi-minor axis `b`,
178+ the curvature is κ(φ) = ab / (a²sin²φ + b²cos²φ)^(3/2)
179+ where φ is the angle measured from the major axis of the ellipse.
180+
181+ Since θ in the ellipse parameterization is measured from the global x-axis,
182+ we need φ = θ - e.angle to get the angle relative to the ellipse's major axis.
183+ """
184+ function ellipse_curvature (e:: Ellipse , θ)
185+ a, b = e. radii[1 ], e. radii[2 ] # a is major axis, b is minor axis
186+ φ = θ - e. angle # Convert from global angle θ to ellipse-relative angle φ
187+ return (a * b) / (a^ 2 * sin (φ)^ 2 + b^ 2 * cos (φ)^ 2 )^ 1.5
188+ end
189+
190+ function to_polygons (
191+ e:: Ellipse ;
192+ atol= DeviceLayout. onenanometer (eltype (e. center)),
193+ Δθ= nothing ,
194+ kwargs...
195+ )
196+ if ! isnothing (Δθ) # Use Δθ-based discretization
197+ r =
198+ θ ->
199+ (e. radii[1 ] * e. radii[2 ]) /
200+ sqrt ((e. radii[2 ] * cos (θ))^ 2 + (e. radii[1 ] * sin (θ))^ 2 )
201+ p = θ -> e. center + r (θ - e. angle) .* Point (cos (θ), sin (θ))
202+ return Polygon (p .(0 : Δθ:(360 ° - Δθ)))
203+ else # Use tolerance-based discretization
204+ curvature_fn = θ -> ellipse_curvature (e, θ)
205+ # t_scale is used to approximately convert "t" (θ) to arclength, use the larger radius to be safe
206+ θs = DeviceLayout. discretization_grid (
207+ curvature_fn,
208+ atol,
209+ (0.0 , 2 π);
210+ t_scale= e. radii[1 ]
211+ )
212+ r =
213+ θ ->
214+ (e. radii[1 ] * e. radii[2 ]) /
215+ sqrt ((e. radii[2 ] * cos (θ))^ 2 + (e. radii[1 ] * sin (θ))^ 2 )
216+ p = θ -> e. center + r (θ - e. angle) .* Point (cos (θ), sin (θ))
217+ return Polygon (p .(θs[1 : (end - 1 )])) # Don't duplicate last point
218+ end
180219end
181220
182221DeviceLayout. magnify (e:: Ellipse , mag) = Ellipse (mag .* e. center, mag .* e. radii, e. angle)
@@ -481,11 +520,20 @@ function perimeter(e::Ellipse)
481520end
482521
483522"""
484- circle (r, α =10°)
523+ circle_polygon (r, Δθ =10°)
485524
486- Return a circular `Polygon` centered about the origin with radius `r` and angular step `α `.
525+ Return a circular `Polygon` centered about the origin with radius `r` and angular step `Δθ `.
487526"""
488- circle (r, α= 10 °) = Polygon (r .* (a -> Point (cos (a), sin (a))). (0 : α:(360 ° - α)))
527+ circle_polygon (r, Δθ= 10 °) = Polygon (r .* (a -> Point (cos (a), sin (a))). (0 : Δθ:(360 ° - Δθ)))
528+ function circle (r, α= 10 °)
529+ @warn """ "
530+ `circle(r, α)` is deprecated. Use `Circle(r)` or `Circle(center, r)` to create an \
531+ exact circle that will be discretized at render time according to rendering keyword \
532+ `atol` (default 1nm) or `Δθ` (if provided). To construct the polygon directly, use \
533+ `circle_polygon(r, α)`.
534+ """
535+ return circle_polygon (r, α)
536+ end
489537
490538"""
491539 struct Rounded{T <: Coordinate} <: GeometryEntityStyle
0 commit comments