1use crate::mesh::{Builder, Mesh};
2use std::f32::consts::{PI, TAU};
3
4pub const ROLL_WGSL: &str = r#"
5struct Rolled {
6 position: vec3<f32>,
7 tangent: vec3<f32>,
8 normal: vec3<f32>,
9}
10
11fn roll_outer(core: f32, thickness: f32, wound: f32) -> f32 {
12 return sqrt(core * core + max(wound, 0.0) * thickness / 3.14159265);
13}
14
15fn roll_place(rest: vec3<f32>, at: f32, outer: f32, thickness: f32) -> Rolled {
16 var out: Rolled;
17 let s = rest.x - at;
18 if (s <= 0.0) {
19 out.position = vec3<f32>(rest.x, 0.0, rest.z);
20 out.tangent = vec3<f32>(1.0, 0.0, 0.0);
21 out.normal = vec3<f32>(0.0, 1.0, 0.0);
22 return out;
23 }
24 var theta = s / outer;
25 if (thickness > 1e-7) {
26 let inside = max(outer * outer - thickness * s / 3.14159265, 0.0);
27 theta = (outer - sqrt(inside)) * 6.28318531 / thickness;
28 }
29 let shrink = thickness / 6.28318531;
30 let r = outer - shrink * theta;
31 let around = vec2<f32>(sin(theta), -cos(theta));
32 let along = vec2<f32>(cos(theta), sin(theta));
33 let flat = -shrink * around + r * along;
34 let t = normalize(flat);
35 out.position = vec3<f32>(at + r * around.x, outer + r * around.y, rest.z);
36 out.tangent = vec3<f32>(t.x, t.y, 0.0);
37 out.normal = vec3<f32>(-t.y, t.x, 0.0);
38 return out;
39}
40"#;
41
42#[derive(Clone, Copy, Debug, PartialEq)]
43pub struct Strip {
44 pub length: f32,
45 pub width: f32,
46}
47
48#[derive(Clone, Copy, Debug, PartialEq)]
49pub struct Roller {
50 pub at: f32,
51 pub core: f32,
52 pub thickness: f32,
53}
54
55#[derive(Clone, Copy, Debug, PartialEq)]
56pub struct Rolled {
57 pub position: [f32; 3],
58 pub tangent: [f32; 3],
59 pub normal: [f32; 3],
60}
61
62impl Roller {
63 pub fn outer(&self, strip: &Strip) -> f32 {
64 let wound = (strip.length - self.at).max(0.0);
65 (self.core * self.core + wound * self.thickness / PI).sqrt()
66 }
67
68 pub fn place(&self, rest: [f32; 3], outer: f32) -> Rolled {
69 let s = rest[0] - self.at;
70 if s <= 0.0 {
71 return Rolled {
72 position: [rest[0], 0.0, rest[2]],
73 tangent: [1.0, 0.0, 0.0],
74 normal: [0.0, 1.0, 0.0],
75 };
76 }
77 let theta = if self.thickness > 1e-7 {
78 let inside = (outer * outer - self.thickness * s / PI).max(0.0);
79 (outer - inside.sqrt()) * TAU / self.thickness
80 } else {
81 s / outer
82 };
83 let shrink = self.thickness / TAU;
84 let r = outer - shrink * theta;
85 let around = [theta.sin(), -theta.cos()];
86 let along = [theta.cos(), theta.sin()];
87 let flat = [
88 -shrink * around[0] + r * along[0],
89 -shrink * around[1] + r * along[1],
90 ];
91 let len = (flat[0] * flat[0] + flat[1] * flat[1]).sqrt().max(1e-12);
92 let t = [flat[0] / len, flat[1] / len];
93 Rolled {
94 position: [self.at + r * around[0], outer + r * around[1], rest[2]],
95 tangent: [t[0], t[1], 0.0],
96 normal: [-t[1], t[0], 0.0],
97 }
98 }
99}
100
101impl Strip {
102 pub fn columns(&self, core: f32, error: f32, band: Option<(f32, f32)>) -> Vec<f32> {
103 let step = column_step(core, error);
104 let (from, to) = band
105 .map(|(a, b)| (a.clamp(0.0, self.length), b.clamp(0.0, self.length)))
106 .unwrap_or((0.0, self.length));
107 let (from, to) = (from.min(to), from.max(to));
108 let mut out = vec![0.0];
109 if from > 0.0 {
110 out.push(from);
111 }
112 let count = ((to - from) / step).ceil().max(1.0) as usize;
113 for i in 1..=count {
114 out.push(from + (to - from) * i as f32 / count as f32);
115 }
116 if to < self.length {
117 out.push(self.length);
118 }
119 out.dedup_by(|a, b| (*a - *b).abs() <= 1e-7);
120 out
121 }
122
123 pub fn mesh(&self, core: f32, error: f32, band: Option<(f32, f32)>, across: usize) -> Mesh {
124 let xs = self.columns(core, error, band);
125 let across = across.max(1);
126 let mut builder = Builder::new();
127 let mut grid = Vec::with_capacity(xs.len());
128 for x in &xs {
129 let mut column = Vec::with_capacity(across + 1);
130 for j in 0..=across {
131 let z = self.width * j as f32 / across as f32;
132 let uv = [x / self.length.max(1e-6), z / self.width.max(1e-6)];
133 column.push(builder.vertex([*x, 0.0, z], [0.0, 1.0, 0.0], uv));
134 }
135 grid.push(column);
136 }
137 for i in 0..xs.len() - 1 {
138 for j in 0..across {
139 let (a, b, c, d) = (
140 grid[i][j],
141 grid[i + 1][j],
142 grid[i + 1][j + 1],
143 grid[i][j + 1],
144 );
145 builder.tri(a, b, c);
146 builder.tri(a, c, d);
147 }
148 }
149 builder.build()
150 }
151}
152
153pub fn column_step(core: f32, error: f32) -> f32 {
154 let core = core.max(1e-6);
155 let error = error.clamp(core * 1e-5, core);
156 let alpha = 2.0 * (1.0 - error / core).acos();
157 (core * alpha).max(1e-6)
158}