Registry / RepositoryTypeScriptRustPython
Rollpie
ReadmeFiles
Versions
Info
Download
0.2.0528.5 KB2026-10-160.1.1542.8 KB2026-10-150.1.0541.1 KB2026-10-15
Version
0.2.0
Copyright
Rollpie, Irohabook
Publisher
math
Published
2026-10-16
Size
528.5 KB
Downloads
7
Checksum
f8871632f38084ae53dd9bf16e9ea90c80a209bf6cd234b6767630c5470dc305
Dependencies
None

ring.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::f64::consts::PI;


pub fn first_angle(size: usize, incoming: f64) -> f64 {
    incoming + 90.0 - 180.0 / size as f64
}


pub fn turn(size: usize) -> f64 {
    -360.0 / size as f64
}


pub fn radius(size: usize, span: f64) -> f64 {
    span / (2.0 * (PI / size as f64).sin())
}


pub fn inradius(size: usize, span: f64) -> f64 {
    span / (2.0 * (PI / size as f64).tan())
}


pub fn center(size: usize, span: f64, vertex: (f64, f64), incoming: f64) -> (f64, f64) {
    let length = radius(size, span);
    let radian = incoming.to_radians();

    (vertex.0 + radian.cos() * length, vertex.1 - radian.sin() * length)
}


pub fn inset(size: usize, gap: f64) -> f64 {
    let half = (180.0 - 360.0 / size as f64) / 2.0;

    gap / half.to_radians().tan()
}


#[cfg(test)]
mod tests {
    use super::*;

    fn walk(size: usize, span: f64, incoming: f64) -> Vec<(f64, f64)> {
        let mut found = vec![(0.0, 0.0)];
        let mut angle = first_angle(size, incoming);

        for _ in 0..size {
            let last = *found.last().unwrap();
            let radian = angle.to_radians();
            found.push((last.0 + radian.cos() * span, last.1 - radian.sin() * span));
            angle += turn(size);
        }

        found
    }

    #[test]
    fn a_ring_comes_back_to_where_it_started() {
        for size in 3..=12 {
            let found = walk(size, 3.0, 0.0);
            let last = found[size];
            assert!(last.0.abs() < 1e-9 && last.1.abs() < 1e-9, "the ring of size {size} is open");
        }
    }

    #[test]
    fn every_side_has_the_same_length() {
        let found = walk(6, 3.0, 0.0);

        for pair in found.windows(2) {
            let span = ((pair[1].0 - pair[0].0).powi(2) + (pair[1].1 - pair[0].1).powi(2)).sqrt();
            assert!((span - 3.0).abs() < 1e-9);
        }
    }

    #[test]
    fn a_plain_hexagon_lies_flat() {
        let found = walk(6, 3.0, 0.0);
        assert!((found[1].0 - 1.5).abs() < 1e-9);
        assert!((found[1].1 + 2.598).abs() < 1e-3);
        assert!((found[3].0 - 6.0).abs() < 1e-9);
        assert!(found[3].1.abs() < 1e-9, "opposite vertices are not level");
    }

    #[test]
    fn the_ring_grows_along_the_bond_that_came_in() {
        let middle = center(6, 3.0, (0.0, 0.0), 0.0);
        assert!((middle.0 - 3.0).abs() < 1e-9);
        assert!(middle.1.abs() < 1e-9);

        let upward = center(6, 3.0, (0.0, 0.0), 90.0);
        assert!(upward.0.abs() < 1e-9);
        assert!((upward.1 + 3.0).abs() < 1e-9);
    }

    #[test]
    fn the_center_is_the_same_distance_from_every_vertex() {
        let found = walk(5, 3.0, 30.0);
        let middle = center(5, 3.0, (0.0, 0.0), 30.0);
        let length = radius(5, 3.0);

        for vertex in &found[0..5] {
            let span = ((vertex.0 - middle.0).powi(2) + (vertex.1 - middle.1).powi(2)).sqrt();
            assert!((span - length).abs() < 1e-9);
        }
    }

    #[test]
    fn the_inner_radius_is_shorter_than_the_outer() {
        for size in 3..=12 {
            assert!(inradius(size, 3.0) < radius(size, 3.0));
        }
    }

    #[test]
    fn a_hexagon_trims_its_inner_line_by_the_gap_over_root_three() {
        assert!((inset(6, 0.2) - 0.2 / 3f64.sqrt()).abs() < 1e-9);
    }
}