Skip to main content

brep_kernel/meshing/
tessellation.rs

1use crate::curve::interior_knots;
2use crate::mass_properties::trim_polygons;
3use crate::topology::{BrepSolid, FaceRecord};
4use crate::{KnotVector, Mesh, Vec3};
5
6#[derive(Clone, Copy, Debug)]
7pub struct TessellationOptions {
8    pub slabs_per_span_u: usize,
9    pub steps_per_span_v: usize,
10}
11
12impl Default for TessellationOptions {
13    fn default() -> Self {
14        Self {
15            slabs_per_span_u: 8,
16            steps_per_span_v: 8,
17        }
18    }
19}
20
21fn crossings_at(polygons: &[Vec<[f64; 2]>], u: f64) -> Vec<f64> {
22    let mut crossings = Vec::new();
23    for polygon in polygons {
24        for index in 0..polygon.len() {
25            let a = polygon[index];
26            let b = polygon[(index + 1) % polygon.len()];
27            if (a[0] > u) != (b[0] > u) {
28                crossings.push(a[1] + (u - a[0]) / (b[0] - a[0]) * (b[1] - a[1]));
29            }
30        }
31    }
32    crossings.sort_by(f64::total_cmp);
33    crossings
34}
35
36fn emit_vertex(
37    mesh: &mut Mesh,
38    face: &FaceRecord,
39    u: f64,
40    v: f64,
41    domains: [f64; 4],
42) -> Result<u32, String> {
43    let (point, su, sv) = face.surface.deriv1(u, v)?;
44    let mut normal = su.cross(sv);
45    if normal.length() <= 1e-12 {
46        let [u0, u1, v0, v1] = domains;
47        let epsilon = 1e-4;
48        let nudged_u = u.clamp(u0 + epsilon * (u1 - u0), u1 - epsilon * (u1 - u0));
49        let nudged_v = v.clamp(v0 + epsilon * (v1 - v0), v1 - epsilon * (v1 - v0));
50        let (_, nsu, nsv) = face.surface.deriv1(nudged_u, nudged_v)?;
51        normal = nsu.cross(nsv);
52        if normal.length() <= 1e-12 {
53            normal = Vec3::new(0.0, 0.0, 1.0);
54        }
55    }
56    normal = normal.normalized()?;
57    if !face.same_sense {
58        normal = normal.scale(-1.0);
59    }
60    mesh.positions.extend([point.x, point.y, point.z]);
61    mesh.normals.extend([normal.x, normal.y, normal.z]);
62    Ok((mesh.positions.len() / 3 - 1) as u32)
63}
64
65fn push_triangle(mesh: &mut Mesh, a: u32, b: u32, c: u32, ccw: bool, face_id: u32) {
66    if ccw {
67        mesh.indices.extend([a, b, c]);
68    } else {
69        mesh.indices.extend([a, c, b]);
70    }
71    mesh.face_ids.push(face_id);
72}
73
74pub fn tessellate_face(
75    face: &FaceRecord,
76    options: TessellationOptions,
77    face_id: u32,
78) -> Result<Mesh, String> {
79    let ku = KnotVector::new(face.surface.knots_u.clone(), face.surface.degree_u)?;
80    let kv = KnotVector::new(face.surface.knots_v.clone(), face.surface.degree_v)?;
81    let [u0, u1] = ku.domain();
82    let [v0, v1] = kv.domain();
83    let polygons = trim_polygons(face)?;
84    let slabs = options.slabs_per_span_u.max(1);
85    let steps = options.steps_per_span_v.max(1);
86
87    let mut u_knots = vec![u0];
88    u_knots.extend(interior_knots(&face.surface.knots_u, face.surface.degree_u));
89    u_knots.push(u1);
90    let mut breaks = Vec::new();
91    for pair in u_knots.windows(2) {
92        for index in 0..=slabs {
93            breaks.push(pair[0] + (pair[1] - pair[0]) * index as f64 / slabs as f64);
94        }
95    }
96    for polygon in &polygons {
97        breaks.extend(polygon.iter().map(|point| point[0].clamp(u0, u1)));
98    }
99    breaks.sort_by(f64::total_cmp);
100    let minimum_gap = (u1 - u0) * 1e-9;
101    breaks.dedup_by(|a, b| (*a - *b).abs() <= minimum_gap);
102
103    let v_span_length =
104        (v1 - v0) / (interior_knots(&face.surface.knots_v, face.surface.degree_v).len() + 1) as f64;
105    let v_step = v_span_length / steps as f64;
106    let mut mesh = Mesh::default();
107    for pair in breaks.windows(2) {
108        let ua = pair[0];
109        let ub = pair[1];
110        if ub - ua <= minimum_gap {
111            continue;
112        }
113        let midpoint_crossings = crossings_at(&polygons, (ua + ub) * 0.5);
114        if midpoint_crossings.len() < 2 {
115            continue;
116        }
117        let mut left_crossings = crossings_at(&polygons, ua + (ub - ua) * 1e-7);
118        let mut right_crossings = crossings_at(&polygons, ub - (ub - ua) * 1e-7);
119        if left_crossings.len() != midpoint_crossings.len()
120            || right_crossings.len() != midpoint_crossings.len()
121        {
122            left_crossings.clone_from(&midpoint_crossings);
123            right_crossings.clone_from(&midpoint_crossings);
124        }
125        for interval in (0..midpoint_crossings.len() - 1).step_by(2) {
126            let lower_left = left_crossings[interval].clamp(v0, v1);
127            let upper_left = left_crossings[interval + 1].clamp(v0, v1);
128            let lower_right = right_crossings[interval].clamp(v0, v1);
129            let upper_right = right_crossings[interval + 1].clamp(v0, v1);
130            let span = (upper_left - lower_left).max(upper_right - lower_right);
131            if span <= 1e-12 {
132                continue;
133            }
134            let row_steps = ((span / v_step).ceil() as usize).clamp(1, 64);
135            let mut left = Vec::with_capacity(row_steps + 1);
136            let mut right = Vec::with_capacity(row_steps + 1);
137            for index in 0..=row_steps {
138                let fraction = index as f64 / row_steps as f64;
139                left.push(emit_vertex(
140                    &mut mesh,
141                    face,
142                    ua,
143                    lower_left + (upper_left - lower_left) * fraction,
144                    [u0, u1, v0, v1],
145                )?);
146                right.push(emit_vertex(
147                    &mut mesh,
148                    face,
149                    ub,
150                    lower_right + (upper_right - lower_right) * fraction,
151                    [u0, u1, v0, v1],
152                )?);
153            }
154            for index in 0..row_steps {
155                push_triangle(
156                    &mut mesh,
157                    left[index],
158                    right[index],
159                    right[index + 1],
160                    face.same_sense,
161                    face_id,
162                );
163                push_triangle(
164                    &mut mesh,
165                    left[index],
166                    right[index + 1],
167                    left[index + 1],
168                    face.same_sense,
169                    face_id,
170                );
171            }
172        }
173    }
174    mesh.validate()?;
175    Ok(mesh)
176}
177
178pub fn tessellate_brep(solid: &BrepSolid, options: TessellationOptions) -> Result<Mesh, String> {
179    let mut mesh = Mesh::default();
180    let mut sequential_face_id = 0u32;
181    for shell in &solid.shells {
182        for face in &shell.faces {
183            let face_mesh = tessellate_face(face, options, sequential_face_id)?;
184            let base = (mesh.positions.len() / 3) as u32;
185            mesh.positions.extend(face_mesh.positions);
186            mesh.normals.extend(face_mesh.normals);
187            mesh.indices
188                .extend(face_mesh.indices.into_iter().map(|index| index + base));
189            mesh.face_ids.extend(face_mesh.face_ids);
190            sequential_face_id += 1;
191        }
192    }
193    mesh.validate()?;
194    Ok(mesh)
195}
196
197// BREP private tests: 52762cc01b7c9570