use crate::prelude::*;
#[must_use]
pub fn pointline_area(points: &Pointline) -> f64 {
if points.len() < 3 {
return 0.0;
}
let mut area = 0.0;
let n = points.len();
for i in 0..n {
let j = (i + 1) % n;
area += points[i].x * points[j].y;
area -= points[j].x * points[i].y;
}
area / 2.0
}
#[must_use]
pub fn arcline_area(arcs: &Arcline) -> f64 {
if arcs.is_empty() {
return 0.0;
}
let mut total_area = 0.0;
for arc in arcs {
if arc.is_seg() {
total_area += arc.a.perp(arc.b) / 2.0;
} else {
total_area += arc_area_contribution(arc);
}
}
total_area
}
#[doc(hidden)]
fn arc_area_contribution(arc: &Arc) -> f64 {
if arc.is_seg() {
return arc.a.perp(arc.b) / 2.0;
}
let center = arc.c;
let start = arc.a;
let end = arc.b;
let start_vector = start - center;
let end_vector = end - center;
let start_angle = start_vector.y.atan2(start_vector.x);
let end_angle = end_vector.y.atan2(end_vector.x);
let mut arc_angle = end_angle - start_angle;
if arc_angle < 0.0 {
arc_angle += 2.0 * std::f64::consts::PI;
}
if start.close_enough(end, 1e-10) {
arc_angle = 2.0 * std::f64::consts::PI;
}
let line_contribution = (start.x * end.y - end.x * start.y) / 2.0;
let radius = arc.r;
let sector_area = 0.5 * radius * radius * arc_angle;
let triangle_area = 0.5
* (center.x * (start.y - end.y)
+ start.x * (end.y - center.y)
+ end.x * (center.y - start.y));
let arc_curvature_contribution = sector_area - triangle_area;
line_contribution + arc_curvature_contribution
}
#[cfg(test)]
mod test_pointline_area {
use super::*;
#[test]
fn test_pointline_area_square() {
let square = vec![
point(0.0, 0.0),
point(1.0, 0.0),
point(1.0, 1.0),
point(0.0, 1.0),
];
let area = pointline_area(&square);
assert_eq!(area, 1.0);
}
#[test]
fn test_pointline_area_triangle() {
let triangle = vec![point(0.0, 0.0), point(2.0, 0.0), point(1.0, 2.0)];
let area = pointline_area(&triangle);
assert_eq!(area, 2.0);
}
}
#[cfg(test)]
mod test_arcline_area {
use super::*;
#[test]
fn test_arcline_area_empty() {
let empty_arcline: Vec<Arc> = vec![];
let area = arcline_area(&empty_arcline);
assert_eq!(area, 0.0);
}
#[test]
fn test_arcline_area_square_line_segments() {
let square_arcs = vec![
arcseg(point(0.0, 0.0), point(1.0, 0.0)),
arcseg(point(1.0, 0.0), point(1.0, 1.0)),
arcseg(point(1.0, 1.0), point(0.0, 1.0)),
arcseg(point(0.0, 1.0), point(0.0, 0.0)),
];
let area = arcline_area(&square_arcs);
assert!((area - 1.0).abs() < 1e-10, "Expected 1.0, got {}", area);
}
#[test]
fn test_arcline_area_triangle_line_segments() {
let triangle_arcs = vec![
arcseg(point(0.0, 0.0), point(2.0, 0.0)),
arcseg(point(2.0, 0.0), point(1.0, 2.0)),
arcseg(point(1.0, 2.0), point(0.0, 0.0)),
];
let area = arcline_area(&triangle_arcs);
assert!((area - 2.0).abs() < 1e-10, "Expected 2.0, got {}", area);
}
#[test]
fn test_arcline_area_semicircle() {
let semicircle = vec![
arc(point(1.0, 0.0), point(-1.0, 0.0), point(0.0, 0.0), 1.0),
arcseg(point(-1.0, 0.0), point(1.0, 0.0)), ];
let area = arcline_area(&semicircle);
let expected_area = std::f64::consts::PI / 2.0; assert!(
(area - expected_area).abs() < 1e-10,
"Expected {}, got {}",
expected_area,
area
);
}
#[test]
fn test_arcline_area_quarter_circle() {
let quarter_circle = vec![
arc(point(1.0, 0.0), point(0.0, 1.0), point(0.0, 0.0), 1.0),
arcseg(point(0.0, 1.0), point(0.0, 0.0)), arcseg(point(0.0, 0.0), point(1.0, 0.0)), ];
let area = arcline_area(&quarter_circle);
let expected_area = std::f64::consts::PI / 4.0; assert!(
(area - expected_area).abs() < 1e-10,
"Expected {}, got {}",
expected_area,
area
);
}
#[test]
fn test_arcline_area_mixed_arcs_and_lines() {
let mixed_shape = vec![
arcseg(point(-1.0, 0.0), point(1.0, 0.0)), arc(point(1.0, 0.0), point(-1.0, 0.0), point(0.0, 0.0), 1.0), ];
let area = arcline_area(&mixed_shape);
let expected_area = std::f64::consts::PI / 2.0; assert!(
(area - expected_area).abs() < 1e-10,
"Expected {}, got {}",
expected_area,
area
);
}
#[test]
fn test_arcline_area_full_circle_single_arc() {
let center = point(0.0, 0.0);
let radius = 2.0;
let start_end = point(radius, 0.0);
let full_circle = vec![arc(start_end, start_end, center, radius)];
let area = arcline_area(&full_circle);
let expected_area = std::f64::consts::PI * radius * radius;
assert!(
(area - expected_area).abs() < 1e-9,
"Expected {}, got {}",
expected_area,
area
);
}
#[test]
fn test_arcline_area_single_arc_segment() {
let single_arc = vec![arc(point(1.0, 0.0), point(0.0, 1.0), point(0.0, 0.0), 1.0)];
let area = arcline_area(&single_arc);
let line_contribution = (1.0 * 1.0 - 0.0 * 0.0) / 2.0; let sector_area = 0.5 * 1.0 * 1.0 * (std::f64::consts::PI / 2.0); let triangle_area = 0.5 * (0.0 * (0.0 - 1.0) + 1.0 * (1.0 - 0.0) + 0.0 * (0.0 - 0.0)); let arc_curvature_contribution = sector_area - triangle_area;
let expected_area = line_contribution + arc_curvature_contribution;
assert!(
(area - expected_area).abs() < 1e-10,
"Expected {}, got {}",
expected_area,
area
);
let quarter_circle_area = std::f64::consts::PI / 4.0;
assert!(
(area - quarter_circle_area).abs() < 1e-10,
"Single arc area should be π/4, got {}",
area
);
}
#[test]
fn test_arcline_area_clockwise_vs_counterclockwise() {
let ccw_triangle = vec![
arcseg(point(0.0, 0.0), point(1.0, 0.0)),
arcseg(point(1.0, 0.0), point(0.0, 1.0)),
arcseg(point(0.0, 1.0), point(0.0, 0.0)),
];
let cw_triangle = vec![
arcseg(point(0.0, 0.0), point(0.0, 1.0)),
arcseg(point(0.0, 1.0), point(1.0, 0.0)),
arcseg(point(1.0, 0.0), point(0.0, 0.0)),
];
let ccw_area = arcline_area(&ccw_triangle);
let cw_area = arcline_area(&cw_triangle);
assert!(
(ccw_area + cw_area).abs() < 1e-10,
"CCW: {}, CW: {}",
ccw_area,
cw_area
);
assert!(ccw_area > 0.0, "CCW area should be positive");
assert!(cw_area < 0.0, "CW area should be negative");
}
#[test]
fn test_arcline_area_ccw_arc_orientation() {
let ccw_quarter_arc = arc(point(1.0, 0.0), point(0.0, 1.0), point(0.0, 0.0), 1.0);
let ccw_shape = vec![
ccw_quarter_arc,
arcseg(point(0.0, 1.0), point(0.0, 0.0)), arcseg(point(0.0, 0.0), point(1.0, 0.0)), ];
let area = arcline_area(&ccw_shape);
assert!(
area > 0.0,
"CCW oriented shape should have positive area, got {}",
area
);
let expected_area = std::f64::consts::PI / 4.0;
assert!(
(area - expected_area).abs() < 1e-10,
"Expected area {}, got {}",
expected_area,
area
);
}
#[test]
fn test_arcline_area_full_circle_ccw() {
let center = point(0.0, 0.0);
let radius = 2.0;
let full_circle_ccw = vec![arc(point(radius, 0.0), point(radius, 0.0), center, radius)];
let area = arcline_area(&full_circle_ccw);
let expected_area = std::f64::consts::PI * radius * radius;
assert!(area > 0.0, "CCW full circle should have positive area");
assert!(
(area - expected_area).abs() < 1e-9,
"Expected area {}, got {}",
expected_area,
area
);
}
}