Skip to content

Commit

Permalink
Correctly calculate incenter in Triangle::inscribed_circle (#387)
Browse files Browse the repository at this point in the history
The method erroneously calculated the circumcenter instead.

Tagging along on this PR are some changes to the triangle tests that I
used to track down some issues. If desired I can bump these to a
separate PR. The asserts testing approximate equality now take a
relative epsilon, making them a bit more useful for testing values
differing in orders of magnitude. I've also recalculated some of the
test values to specify them at 17 significant digits (absolute limit of
`f64`), and made the equality bounds tighter.
  • Loading branch information
tomcur authored Oct 31, 2024
1 parent 3fbc3cf commit da9bc17
Showing 1 changed file with 57 additions and 18 deletions.
75 changes: 57 additions & 18 deletions src/triangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ impl Triangle {
let bc = self.b.distance(self.c);
let ac = self.a.distance(self.c);

Circle::new(self.circumcenter(), 2.0 * self.area() / (ab + bc + ac))
// [`Vec2::div`] also multiplies by the reciprocal of the argument, but as this reciprocal
// is reused below to calculate the radius, there's explicitly only one division needed.
let perimeter_recip = 1. / (ab + bc + ac);
let incenter = (self.a.to_vec2() * bc + self.b.to_vec2() * ac + self.c.to_vec2() * ab)
* perimeter_recip;

Circle::new(incenter.to_point(), 2.0 * self.area() * perimeter_recip)
}

/// The circumscribed circle of [`Triangle`].
Expand Down Expand Up @@ -269,21 +275,24 @@ impl Iterator for TrianglePathIter {
mod tests {
use crate::{Point, Triangle, Vec2};

fn assert_approx_eq(x: f64, y: f64) {
assert!((x - y).abs() < 1e-7);
fn assert_approx_eq(x: f64, y: f64, max_relative_error: f64) {
assert!(
(x - y).abs() <= f64::max(x.abs(), y.abs()) * max_relative_error,
"{x} != {y}"
);
}

fn assert_approx_eq_point(x: Point, y: Point) {
assert_approx_eq(x.x, y.x);
assert_approx_eq(x.y, x.y);
fn assert_approx_eq_point(x: Point, y: Point, max_relative_error: f64) {
assert_approx_eq(x.x, y.x, max_relative_error);
assert_approx_eq(x.y, y.y, max_relative_error);
}

#[test]
fn centroid() {
let test = Triangle::from_coords((-90.02, 3.5), (7.2, -9.3), (8.0, 9.1)).centroid();
let expected = Point::new(-24.94, 1.1);

assert_approx_eq_point(test, expected);
assert_approx_eq_point(test, expected, f64::EPSILON * 100.);
}

#[test]
Expand All @@ -295,9 +304,9 @@ mod tests {
Vec2::new(199.6, 6.6),
];

test.iter()
.zip(expected.iter())
.for_each(|(t, e)| assert_approx_eq_point(t.to_point(), e.to_point()));
test.iter().zip(expected.iter()).for_each(|(t, e)| {
assert_approx_eq_point(t.to_point(), e.to_point(), f64::EPSILON * 100.);
});
}

#[test]
Expand All @@ -307,36 +316,66 @@ mod tests {
(7892.729, 238.459),
(7820.2, 712.23),
);
let expected = 1079952.91574081;
let expected = 1079952.9157407999;

// initial
assert_approx_eq(test.area(), -expected);
assert_approx_eq(test.area(), -expected, f64::EPSILON * 100.);
// permutate vertex
let test = Triangle::new(test.b, test.a, test.c);
assert_approx_eq(test.area(), expected);
assert_approx_eq(test.area(), expected, f64::EPSILON * 100.);
}

#[test]
fn circumcenter() {
let test = Triangle::EQUILATERAL.circumcenter();
let expected = Point::new(0.5, 0.2886751345948128);
let expected = Point::new(0.5, 0.28867513459481288);

assert_approx_eq_point(test, expected);
assert_approx_eq_point(test, expected, f64::EPSILON * 100.);
}

#[test]
fn inradius() {
let test = Triangle::EQUILATERAL.inscribed_circle().radius;
let expected = 0.28867513459481287;

assert_approx_eq(test, expected);
assert_approx_eq(test, expected, f64::EPSILON * 100.);
}

#[test]
fn circumradius() {
let test = Triangle::EQUILATERAL.circumscribed_circle().radius;
let expected = 0.5773502691896258;
let expected = 0.57735026918962576;

assert_approx_eq(test, expected, f64::EPSILON * 100.);
}

assert_approx_eq(test, expected);
#[test]
fn inscribed_circle() {
let test = Triangle::new((-4., 1.), (-4., -1.), (10., 3.));

let inscribed = test.inscribed_circle();
assert_approx_eq_point(
inscribed.center,
(-3.0880178529263671, 0.20904207741504303).into(),
f64::EPSILON * 100.,
);
assert_approx_eq(inscribed.radius, 0.91198214707363295, f64::EPSILON * 100.);
}

#[test]
fn circumscribed_circle() {
let test = Triangle::new((-4., 1.), (-4., -1.), (10., 3.));

let circumscribed = test.circumscribed_circle();
assert_approx_eq_point(
circumscribed.center,
(3.2857142857142857, 0.).into(),
f64::EPSILON * 100.,
);
assert_approx_eq(
circumscribed.radius,
7.3540215292764288,
f64::EPSILON * 100.,
);
}
}

0 comments on commit da9bc17

Please sign in to comment.