1use std::collections::{BTreeMap, BTreeSet};
31
32use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector3};
33
34use crate::cage::{Mesh, Topology, next, prev, undirected};
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub enum VertexKind {
39 Smooth,
41 Crease(usize, usize),
43 Corner,
46}
47
48pub fn is_sharp(sharp: &BTreeSet<[usize; 2]>, topology: &Topology, a: usize, b: usize) -> bool {
51 sharp.contains(&undirected(a, b)) || topology.is_boundary(a, b)
52}
53
54pub fn vertex_kind(
56 faces: &[Vec<usize>],
57 sharp: &BTreeSet<[usize; 2]>,
58 topology: &Topology,
59 v: usize,
60) -> VertexKind {
61 let (fan, closed) = &topology.fans[v];
62 let mut neighbours: Vec<usize> = fan.iter().map(|&c| next(faces, c)).collect();
63 if !closed {
64 neighbours.push(prev(faces, fan[fan.len() - 1]));
65 }
66 let sharp_ends: Vec<usize> = neighbours
67 .into_iter()
68 .filter(|&w| is_sharp(sharp, topology, v, w))
69 .collect();
70 match sharp_ends[..] {
71 _ if !closed && fan.len() == 1 => VertexKind::Corner,
72 [a, b] => VertexKind::Crease(a, b),
73 [] | [_] => VertexKind::Smooth,
74 _ => VertexKind::Corner,
75 }
76}
77
78pub fn divide(sum: Vector3, count: i64) -> GeopResult<Vector3> {
80 let d = Scalar::from_i64(count);
81 Ok(Vector3::from_array([
82 sum[0].div(d)?,
83 sum[1].div(d)?,
84 sum[2].div(d)?,
85 ]))
86}
87
88fn blend(a: Vector3, b: Vector3, s: f64) -> Vector3 {
90 let s = Scalar::from_f64(s);
91 a.add(&b.sub(&a).prod_scalar(s))
92}
93
94pub fn weighted(terms: &[(i64, Vector3)]) -> Vector3 {
96 terms.iter().fold(Vector3::zero(), |sum, (w, p)| {
97 if *w == 1 {
98 sum.add(p)
99 } else {
100 sum.add(&p.prod_scalar(Scalar::from_i64(*w)))
101 }
102 })
103}
104
105#[derive(Clone, Debug)]
108pub struct Subdivided {
109 pub positions: Vec<Vector3>,
110 pub faces: Vec<Vec<usize>>,
114 pub face_start: Vec<usize>,
115 pub face_point: Vec<usize>,
119 pub edge_point: BTreeMap<[usize; 2], usize>,
120 pub sharp: BTreeSet<[usize; 2]>,
122 pub topology: Topology,
123}
124
125pub fn subdivide(mesh: &Mesh, topology: &Topology) -> GeopResult<Subdivided> {
127 let p: Vec<Vector3> = mesh
128 .positions
129 .iter()
130 .map(|a| Vector3::from_array(a.map(Scalar::from_f64)))
131 .collect();
132 let faces = &mesh.faces;
133 let face_points = faces
134 .iter()
135 .map(|face| {
136 divide(
137 weighted(&face.iter().map(|&w| (1, p[w])).collect::<Vec<_>>()),
138 face.len() as i64,
139 )
140 })
141 .collect::<GeopResult<Vec<_>>>()?;
142 let vertex_point = |v: usize, sharp: &BTreeSet<[usize; 2]>| -> GeopResult<Vector3> {
144 Ok(match vertex_kind(faces, sharp, topology, v) {
145 VertexKind::Corner => p[v],
146 VertexKind::Crease(a, b) => divide(weighted(&[(1, p[a]), (6, p[v]), (1, p[b])]), 8)?,
147 VertexKind::Smooth => {
148 let fan = &topology.fans[v].0;
149 let n = fan.len() as i64;
150 let mut terms = vec![(n * (n - 2), p[v])];
151 for &c in fan {
152 terms.push((1, face_points[c.0]));
153 terms.push((1, p[next(faces, c)]));
154 }
155 divide(weighted(&terms), n * n)?
156 }
157 })
158 };
159 let all_creased: BTreeSet<[usize; 2]> =
162 mesh.sharp.iter().chain(mesh.soft.keys()).copied().collect();
163 let mut positions = Vec::with_capacity(p.len() + 2 * faces.len());
164 for v in 0..p.len() {
165 let point = vertex_point(v, &mesh.sharp)?;
166 let creases: Vec<f64> = all_creased
169 .iter()
170 .filter(|&&[a, b]| a == v || b == v)
171 .map(|e| mesh.soft.get(e).copied().unwrap_or(1.0))
172 .collect();
173 let soft = creases.iter().any(|&s| s < 1.0);
174 let point = if soft {
175 let mean = creases.iter().sum::<f64>() / creases.len() as f64;
176 blend(point, vertex_point(v, &all_creased)?, mean)
177 } else {
178 point
179 };
180 positions.push(point);
181 }
182 let face_point: Vec<usize> = (0..faces.len()).map(|f| positions.len() + f).collect();
183 positions.extend(face_points.iter().copied());
184 let mut edge_point = BTreeMap::new();
185 for ([a, b], adjacent) in topology.edges() {
186 let middle = || divide(p[a].add(&p[b]), 2);
187 let point = if is_sharp(&mesh.sharp, topology, a, b) {
188 middle()?
189 } else {
190 let terms = [
191 (1, p[a]),
192 (1, p[b]),
193 (1, face_points[adjacent[0]]),
194 (1, face_points[adjacent[1]]),
195 ];
196 let smooth = divide(weighted(&terms), 4)?;
197 match mesh.soft.get(&[a, b]) {
198 Some(&s) => blend(smooth, middle()?, s),
199 None => smooth,
200 }
201 };
202 edge_point.insert([a, b], positions.len());
203 positions.push(point);
204 }
205 let edge = |a: usize, b: usize| edge_point[&undirected(a, b)];
206 let mut quads = Vec::new();
207 let mut face_start = Vec::with_capacity(faces.len());
208 for (f, face) in faces.iter().enumerate() {
209 face_start.push(quads.len());
210 let n = face.len();
211 for k in 0..n {
212 let (v, after, before) = (face[k], face[(k + 1) % n], face[(k + n - 1) % n]);
213 quads.push(vec![v, edge(v, after), face_point[f], edge(before, v)]);
214 }
215 }
216 let mut sharp = BTreeSet::new();
217 for &[a, b] in &mesh.sharp {
218 let m = edge(a, b);
219 sharp.insert(undirected(a, m));
220 sharp.insert(undirected(b, m));
221 }
222 let names: Vec<String> = (0..positions.len())
223 .map(|i| format!("subdivided vertex {i}"))
224 .collect();
225 let face_names: Vec<String> = (0..quads.len())
226 .map(|i| format!("subdivided face {i}"))
227 .collect();
228 let topology = Topology::new(positions.len(), &quads, &names, &face_names)?;
229 Ok(Subdivided {
230 positions,
231 faces: quads,
232 face_start,
233 face_point,
234 edge_point,
235 sharp,
236 topology,
237 })
238}