Skip to content

Commit

Permalink
Merge pull request #1592 from hannobraun/arc
Browse files Browse the repository at this point in the history
Simplify `Arc`
  • Loading branch information
hannobraun authored Feb 16, 2023
2 parents dc43d54 + 3ca98bb commit 6fc0e31
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 67 deletions.
8 changes: 2 additions & 6 deletions crates/fj-kernel/src/builder/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,8 @@ impl HalfEdgeBuilder for PartialHalfEdge {
.write()
.update_as_circle_from_center_and_radius(arc.center, arc.radius);

let [a_curve, b_curve] = if arc.flipped_construction {
[arc.end_angle, arc.start_angle]
} else {
[arc.start_angle, arc.end_angle]
}
.map(|coord| Point::from([coord]));
let [a_curve, b_curve] =
[arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));

for (vertex, point_curve) in
self.vertices.each_mut_ext().zip_ext([a_curve, b_curve])
Expand Down
102 changes: 41 additions & 61 deletions crates/fj-math/src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,10 @@ pub struct Arc {
pub radius: Scalar,

/// Angle of `start` relative to `center`, in radians
///
/// Guaranteed to be less than `end_angle`.
pub start_angle: Scalar,

/// Angle of `end` relative to `center`, in radians
///
/// Guaranteed to be greater than `end_angle`.
pub end_angle: Scalar,

/// True if `start` and `end` were switched to ensure `end_angle` > `start_angle`
pub flipped_construction: bool,
}

impl Arc {
Expand All @@ -34,51 +27,58 @@ impl Arc {
let p0 = p0.into();
let p1 = p1.into();

// This is an implementation of this solution:
// This is an adaptation of this:
// https://math.stackexchange.com/a/87374

let distance_between_endpoints = (p1 - p0).magnitude();
let more_than_half_turn = angle_rad.abs() > Scalar::PI;

let radius = distance_between_endpoints
/ (2. * (angle_rad.abs().into_f64() / 2.).sin());
let distance_center_to_midpoint =
(radius.powi(2) - (distance_between_endpoints.powi(2) / 4.)).sqrt();

let flipped_construction = angle_rad <= Scalar::ZERO;
let angle_rad = angle_rad.abs();

let [p0, p1] = if flipped_construction {
[p1, p0]
} else {
[p0, p1]
let center = {
let midpoint = Point {
coords: (p0.coords + p1.coords) / 2.,
};
let unit_vector_midpoint_to_center = {
let clockwise_turn = angle_rad <= Scalar::ZERO;
let f = match (clockwise_turn, more_than_half_turn) {
(false, false) | (true, true) => Scalar::ONE,
(false, true) | (true, false) => -Scalar::ONE,
};

let unit_vector_p0_to_p1 =
(p1 - p0) / distance_between_endpoints * f;

Vector::from([-unit_vector_p0_to_p1.v, unit_vector_p0_to_p1.u])
};
let distance_center_to_midpoint = (radius.powi(2)
- (distance_between_endpoints.powi(2) / 4.))
.sqrt();

midpoint
+ unit_vector_midpoint_to_center * distance_center_to_midpoint
};

let (uv_factor, end_angle_offset) = if angle_rad > Scalar::PI {
(Scalar::from_f64(-1.), Scalar::TAU)
} else {
(Scalar::ONE, Scalar::ZERO)
};
let unit_vector_p0_to_p1 =
(p1 - p0) / distance_between_endpoints * uv_factor;
let unit_vector_midpoint_to_center =
Vector::from([-unit_vector_p0_to_p1.v, unit_vector_p0_to_p1.u]);
let center = Point {
coords: (p0.coords + p1.coords) / 2.
+ unit_vector_midpoint_to_center * distance_center_to_midpoint,
};
let start_angle = {
let center_to_start = p0 - center;
center_to_start.v.atan2(center_to_start.u)
let from_center = p0 - center;
from_center.v.atan2(from_center.u)
};
let end_angle = {
let center_to_end = p1 - center;
center_to_end.v.atan2(center_to_end.u) + end_angle_offset
let from_center = p1 - center;
let offset = if more_than_half_turn {
Scalar::TAU
} else {
Scalar::ZERO
};

from_center.v.atan2(from_center.u) + offset
};
Self {
center,
radius,
start_angle,
end_angle,
flipped_construction,
}
}
}
Expand Down Expand Up @@ -143,38 +143,18 @@ mod tests {

dbg!(arc.start_angle);
dbg!(arc.end_angle);
dbg!(arc.flipped_construction);
assert_abs_diff_eq!(arc.center, center, epsilon = epsilon);
assert_abs_diff_eq!(
arc.radius,
Scalar::from(radius),
epsilon = epsilon
);

if a0 < a1 {
assert!(!arc.flipped_construction);
assert_abs_diff_eq!(
arc.start_angle,
Scalar::from(a0),
epsilon = epsilon
);
assert_abs_diff_eq!(
arc.end_angle,
Scalar::from(a1),
epsilon = epsilon
);
} else {
assert!(arc.flipped_construction);
assert_abs_diff_eq!(
arc.end_angle,
Scalar::from(a0),
epsilon = epsilon
);
assert_abs_diff_eq!(
arc.start_angle,
Scalar::from(a1),
epsilon = epsilon
);
}
assert_abs_diff_eq!(
arc.start_angle,
Scalar::from(a0),
epsilon = epsilon
);
assert_abs_diff_eq!(arc.end_angle, Scalar::from(a1), epsilon = epsilon);
}
}

0 comments on commit 6fc0e31

Please sign in to comment.