gemini_engine/primitives/
helpers.rs

1use crate::core::Vec2D;
2
3fn is_left_turn(p0: Vec2D, p1: Vec2D, p2: Vec2D) -> bool {
4    let v1 = p1 - p0;
5    let v2 = p2 - p0;
6    v1.perp_dot(v2) > 0
7}
8
9fn is_ear(vertex: Vec2D, prev_vertex: Vec2D, next_vertex: Vec2D, polygon: &[Vec2D]) -> bool {
10    for i in 0..polygon.len() {
11        let p1 = polygon[i];
12        let p2 = polygon[(i + 1) % polygon.len()];
13        let p3 = polygon[(i + 2) % polygon.len()];
14
15        if p1 != vertex
16            && p2 != vertex
17            && p3 != vertex
18            && is_left_turn(vertex, p1, p2)
19            && is_left_turn(vertex, p2, p3)
20            && is_left_turn(vertex, p3, p1)
21        {
22            let triangle_area = (p1 - p2).perp_dot(p3 - p2).abs();
23            if triangle_area > 0 {
24                let p = (vertex - prev_vertex).perp_dot(next_vertex - prev_vertex);
25                return (p1 - prev_vertex).perp_dot(p1 - next_vertex) > 0 && p > 0;
26            }
27        }
28    }
29    false
30}
31
32/// Split a polygon up into triangles using the ear cutting algorithm. Returns a vec of coordinate sets for each triangle
33#[must_use]
34pub fn triangulate(vertices: &[Vec2D]) -> Vec<[Vec2D; 3]> {
35    let mut triangles = Vec::new();
36    let n = vertices.len();
37
38    if n < 3 {
39        return triangles;
40    }
41
42    let mut remaining_vertices = vertices.to_vec();
43
44    while remaining_vertices.len() > 3 {
45        let mut ear_index = 0;
46        for i in 0..remaining_vertices.len() {
47            let prev_index = (i + remaining_vertices.len() - 1) % remaining_vertices.len();
48            let next_index = (i + 1) % remaining_vertices.len();
49
50            let vertex = remaining_vertices[i];
51            let prev_vertex = remaining_vertices[prev_index];
52            let next_vertex = remaining_vertices[next_index];
53
54            if is_ear(vertex, prev_vertex, next_vertex, &remaining_vertices) {
55                ear_index = i;
56                break;
57            }
58        }
59
60        let ear_vertex = remaining_vertices[ear_index];
61        let prev_index = (ear_index + remaining_vertices.len() - 1) % remaining_vertices.len();
62        let next_index = (ear_index + 1) % remaining_vertices.len();
63        let prev_vertex = remaining_vertices[prev_index];
64        let next_vertex = remaining_vertices[next_index];
65
66        triangles.push([prev_vertex, ear_vertex, next_vertex]);
67        remaining_vertices.remove(ear_index);
68    }
69
70    triangles.push([
71        remaining_vertices[0],
72        remaining_vertices[1],
73        remaining_vertices[2],
74    ]);
75
76    triangles
77}
78
79/// Draw a pseudo-line between the independent and dependent positions. Returns rounded values as `i64`s. If you don't want the values rounded, use [`interpolate_floating`]
80#[must_use]
81pub fn interpolate(i0: i64, d0: i64, i1: i64, d1: i64) -> Vec<i64> {
82    interpolate_floating(i0, d0 as f64, i1, d1 as f64)
83        .iter()
84        .map(|n| n.round() as i64)
85        .collect()
86}
87
88/// Draw a pseudo-line between the independent and dependent positions
89#[must_use]
90pub fn interpolate_floating(i0: i64, d0: f64, i1: i64, d1: f64) -> Vec<f64> {
91    if i0 == i1 {
92        return vec![d0];
93    }
94    let mut values = vec![];
95
96    let a = (d1 - d0) / (i1 - i0) as f64;
97    let mut d = d0;
98    for _i in i0..=i1 {
99        values.push(d);
100        d += a;
101    }
102    values
103}