1use std::collections::HashMap;
31
32use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector3};
33
34use crate::{
35 cage::{next, prev},
36 subdivide::{Subdivided, VertexKind, divide, is_sharp, vertex_kind, weighted},
37};
38
39pub type Patch = [[Vector3; 4]; 4];
41
42#[derive(Clone, Debug)]
44pub struct ControlNet {
45 pub corner: Vec<Vector3>,
47 pub edge: HashMap<(usize, usize), Vector3>,
49 pub interior: Vec<[Vector3; 4]>,
51}
52
53impl ControlNet {
54 pub fn patch(&self, mesh: &Subdivided, f: usize) -> Patch {
57 let q = |i: usize| mesh.faces[f][i];
58 let l = |i: usize| self.corner[q(i)];
59 let e = |a: usize, b: usize| self.edge[&(q(a), q(b))];
60 let n = |i: usize| self.interior[f][i];
61 [
62 [l(0), e(0, 3), e(3, 0), l(3)],
63 [e(0, 1), n(0), n(3), e(3, 2)],
64 [e(1, 0), n(1), n(2), e(2, 3)],
65 [l(1), e(1, 2), e(2, 1), l(2)],
66 ]
67 }
68}
69
70fn sector_sizes(mesh: &Subdivided, v: usize) -> Vec<usize> {
73 let (fan, closed) = &mesh.topology.fans[v];
74 let m = fan.len();
75 let cut = |i: usize| {
78 let w = prev(&mesh.faces, fan[i]);
79 is_sharp(&mesh.sharp, &mesh.topology, v, w)
80 };
81 let start = if *closed {
82 match (0..m).find(|&i| cut(i)) {
85 Some(i) => (i + 1) % m,
86 None => return vec![m; m],
87 }
88 } else {
89 0
90 };
91 let mut sizes = vec![0; m];
92 let mut run: Vec<usize> = Vec::new();
93 for step in 0..m {
94 let i = (start + step) % m;
95 run.push(i);
96 if step + 1 == m || cut(i) {
97 for &j in &run {
98 sizes[j] = run.len();
99 }
100 run.clear();
101 }
102 }
103 sizes
104}
105
106fn tangent_normal(mesh: &Subdivided, v: usize) -> Vector3 {
111 let fan = &mesh.topology.fans[v].0;
112 let n = fan.len();
113 let p = &mesh.positions;
114 let angle = |i: usize| 2.0 * std::f64::consts::PI * i as f64 / n as f64;
115 let c = (std::f64::consts::PI / n as f64).cos();
116 let a = 1.0 + angle(1).cos() + c * (2.0 * (9.0 + angle(1).cos())).sqrt();
117 let tangent = |trig: fn(f64) -> f64| {
118 fan.iter()
119 .enumerate()
120 .fold(Vector3::zero(), |sum, (i, &corner)| {
121 let e = p[next(&mesh.faces, corner)].sub(&p[v]);
122 let d = p[mesh.faces[corner.0][(corner.1 + 2) % 4]].sub(&p[v]);
123 let we = Scalar::from_f64(a * trig(angle(i)));
124 let wd = Scalar::from_f64(trig(angle(i)) + trig(angle(i + 1)));
125 sum.add(&e.prod_scalar(we)).add(&d.prod_scalar(wd))
126 })
127 };
128 tangent(f64::cos).prod_cross(&tangent(f64::sin)).sharpen()
129}
130
131pub fn control_net(mesh: &Subdivided) -> GeopResult<ControlNet> {
133 let p = &mesh.positions;
134 let faces = &mesh.faces;
135 let topology = &mesh.topology;
136 let kinds: Vec<VertexKind> = (0..p.len())
137 .map(|v| vertex_kind(faces, &mesh.sharp, topology, v))
138 .collect();
139
140 let mut weight = vec![[0usize; 4]; faces.len()];
141 for (v, kind) in kinds.iter().enumerate() {
142 let fan = &topology.fans[v].0;
143 let sizes = sector_sizes(mesh, v);
144 for (i, &(f, k)) in fan.iter().enumerate() {
145 weight[f][k] = match kind {
146 VertexKind::Smooth => fan.len(),
147 VertexKind::Crease(..) => 2 * sizes[i],
148 VertexKind::Corner => 4 * sizes[i],
149 };
150 }
151 }
152 let mut interior = Vec::with_capacity(faces.len());
153 for (f, face) in faces.iter().enumerate() {
154 let mut points = [Vector3::zero(); 4];
155 for k in 0..4 {
156 let n = weight[f][k] as i64;
157 let terms = [
158 (n, p[face[k]]),
159 (2, p[face[(k + 1) % 4]]),
160 (2, p[face[(k + 3) % 4]]),
161 (1, p[face[(k + 2) % 4]]),
162 ];
163 points[k] = divide(weighted(&terms), n + 5)?;
164 }
165 interior.push(points);
166 }
167
168 let mut corner = Vec::with_capacity(p.len());
169 for v in 0..p.len() {
170 corner.push(match kinds[v] {
171 VertexKind::Corner => p[v],
172 VertexKind::Crease(a, b) => divide(weighted(&[(1, p[a]), (4, p[v]), (1, p[b])]), 6)?,
173 VertexKind::Smooth => {
174 let fan = &topology.fans[v].0;
175 let n = fan.len() as i64;
176 let mut terms = vec![(n * n, p[v])];
177 for &(f, k) in fan {
178 terms.push((4, p[next(faces, (f, k))]));
179 terms.push((1, p[faces[f][(k + 2) % 4]]));
180 }
181 divide(weighted(&terms), n * (n + 5))?
182 }
183 });
184 }
185
186 let mut normals: HashMap<usize, Vector3> = HashMap::new();
187 let mut edge = HashMap::new();
188 for (&(v, w), &(f, k)) in &topology.directed {
189 for (from, to) in [(v, w), (w, v)] {
190 if edge.contains_key(&(from, to)) {
191 continue;
192 }
193 let sharp = is_sharp(&mesh.sharp, topology, from, to);
194 let point = if sharp && kinds[from] != VertexKind::Smooth {
195 divide(weighted(&[(2, p[from]), (1, p[to])]), 3)?
196 } else {
197 let &(g, j) = &topology.directed[&(w, v)];
200 let at = |face: usize, corner_of: usize, from: usize| {
201 if faces[face][corner_of] == from {
202 interior[face][corner_of]
203 } else {
204 interior[face][(corner_of + 1) % 4]
205 }
206 };
207 let sum = at(f, k, from).add(&at(g, j, from));
208 let average = divide(sum, 2)?;
209 let n = topology.fans[from].0.len();
210 if kinds[from] == VertexKind::Smooth && n != 4 {
211 let normal = *normals
212 .entry(from)
213 .or_insert_with(|| tangent_normal(mesh, from));
214 let off = average.sub(&corner[from]);
215 let along = off.prod_dot(&normal).div(normal.norm_sq())?;
216 average.sub(&normal.prod_scalar(along))
217 } else {
218 average
219 }
220 };
221 edge.insert((from, to), point);
222 }
223 }
224 Ok(ControlNet {
225 corner,
226 edge,
227 interior,
228 })
229}