diff --git a/crates/fj-core/src/operations/build/cycle.rs b/crates/fj-core/src/operations/build/cycle.rs index ca5b47f61..b36a3eee6 100644 --- a/crates/fj-core/src/operations/build/cycle.rs +++ b/crates/fj-core/src/operations/build/cycle.rs @@ -1,8 +1,8 @@ -use fj_math::{Point, Scalar}; +use fj_math::{Point, Scalar, Vector}; use itertools::Itertools; use crate::{ - operations::{build::BuildHalfEdge, update::UpdateCycle}, + operations::{build::BuildHalfEdge, insert::Insert, update::UpdateCycle}, storage::Handle, topology::{Cycle, HalfEdge, Surface}, Core, @@ -19,15 +19,47 @@ pub trait BuildCycle { Cycle::new([]) } - /// Build a circle + /// # Build a circle + /// + /// This circle is built out of 4 distinct arcs. + /// + /// ## Implementation Note + /// + /// The cycle can't be built out of a single half-edge. That would be + /// invalid although there's not validation check to document and enforce + /// that yet. Please refer to the following issue for more information: + /// + /// + /// The cycle is built out of 4 arcs specifically, because that's easier to + /// implement than three, and building it out of two creates geometry that + /// the cycle winding code can't handle right now. The following issue has + /// more information on the cycle winding problems: + /// fn circle( center: impl Into>, radius: impl Into, surface: Handle, core: &mut Core, ) -> Cycle { - let circle = HalfEdge::circle(center, radius, surface, core); - Cycle::empty().add_half_edges([circle], core) + let center = center.into(); + let radius = radius.into(); + + let radius_right = Vector::from([radius, Scalar::ZERO]); + let radius_up = Vector::from([Scalar::ZERO, radius]); + + let a = center + radius_right; + let b = center + radius_up; + let c = center - radius_right; + let d = center - radius_up; + + let angle = Scalar::TAU / 4.; + + let ab = HalfEdge::arc(a, b, angle, surface.clone(), core).insert(core); + let bc = HalfEdge::arc(b, c, angle, surface.clone(), core).insert(core); + let cd = HalfEdge::arc(c, d, angle, surface.clone(), core).insert(core); + let da = HalfEdge::arc(d, a, angle, surface.clone(), core).insert(core); + + Cycle::empty().add_half_edges([ab, bc, cd, da], core) } /// Build a polygon diff --git a/crates/fj-core/src/operations/build/half_edge.rs b/crates/fj-core/src/operations/build/half_edge.rs index 6a976d9d4..1eb04bdfb 100644 --- a/crates/fj-core/src/operations/build/half_edge.rs +++ b/crates/fj-core/src/operations/build/half_edge.rs @@ -81,35 +81,6 @@ pub trait BuildHalfEdge { half_edge } - /// Create a circle - fn circle( - center: impl Into>, - radius: impl Into, - surface: Handle, - core: &mut Core, - ) -> Handle { - let path = SurfacePath::circle_from_center_and_radius(center, radius); - let boundary = - [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); - - let half_edge = HalfEdge::unjoined(core).insert(core); - - core.layers.geometry.define_curve( - half_edge.curve().clone(), - surface, - LocalCurveGeom { path }, - ); - core.layers.geometry.define_half_edge( - half_edge.clone(), - HalfEdgeGeom { - path, - boundary: boundary.into(), - }, - ); - - half_edge - } - /// Create a line segment fn line_segment( points_surface: [impl Into>; 2],