1use nalgebra::{Vector3, Vector6};
4use rigidity_core::PointCloud;
5
6use crate::rng::Rng;
7use crate::{Scene, SceneParams};
8
9fn basis(index: usize) -> Vector6<f64> {
11 let mut v = Vector6::zeros();
12 v[index] = 1.0;
13 v
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SceneKind {
19 Plane,
21 Cylinder,
23 Sphere,
25 TwoPlanes,
27 Corner,
29 TeeJoint,
31 Corridor,
33}
34
35impl SceneKind {
36 pub const ALL: [SceneKind; 7] = [
38 Self::Plane,
39 Self::Cylinder,
40 Self::Sphere,
41 Self::TwoPlanes,
42 Self::Corner,
43 Self::TeeJoint,
44 Self::Corridor,
45 ];
46
47 pub fn name(self) -> &'static str {
49 match self {
50 Self::Plane => "plane",
51 Self::Cylinder => "cylinder",
52 Self::Sphere => "sphere",
53 Self::TwoPlanes => "two planes",
54 Self::Corner => "trihedral corner",
55 Self::TeeJoint => "tee joint",
56 Self::Corridor => "corridor",
57 }
58 }
59
60 pub fn nullspace(self) -> Vec<Vector6<f64>> {
98 match self {
99 Self::Plane => vec![basis(0), basis(1), basis(5)],
100 Self::Cylinder => vec![basis(2), basis(5)],
101 Self::Sphere => vec![basis(3), basis(4), basis(5)],
102 Self::TwoPlanes | Self::TeeJoint | Self::Corridor => vec![basis(1)],
103 Self::Corner => Vec::new(),
104 }
105 }
106
107 pub fn nullspace_dimension(self) -> usize {
109 self.nullspace().len()
110 }
111}
112
113struct Patch {
115 center: Vector3<f64>,
116 u: Vector3<f64>,
117 v: Vector3<f64>,
118 half_u: f64,
119 half_v: f64,
120 normal: Vector3<f64>,
121}
122
123fn axes() -> (Vector3<f64>, Vector3<f64>, Vector3<f64>) {
124 (Vector3::x(), Vector3::y(), Vector3::z())
125}
126
127fn patches(kind: SceneKind, s: f64) -> Vec<Patch> {
128 let (x, y, z) = axes();
129 let patch = |center: Vector3<f64>,
130 u: Vector3<f64>,
131 v: Vector3<f64>,
132 half_u: f64,
133 half_v: f64,
134 normal: Vector3<f64>| Patch {
135 center,
136 u,
137 v,
138 half_u,
139 half_v,
140 normal,
141 };
142
143 match kind {
144 SceneKind::Plane => vec![patch(Vector3::zeros(), x, y, s, s, z)],
145 SceneKind::TwoPlanes => vec![
146 patch(Vector3::zeros(), x, y, s, s, z),
147 patch(Vector3::new(0.0, 0.0, s), y, z, s, s, x),
148 ],
149 SceneKind::Corner => vec![
150 patch(Vector3::new(s, s, 0.0), x, y, s, s, z),
151 patch(Vector3::new(0.0, s, s), y, z, s, s, x),
152 patch(Vector3::new(s, 0.0, s), x, z, s, s, y),
153 ],
154 SceneKind::TeeJoint => vec![
155 patch(Vector3::zeros(), x, y, s, 4.0 * s, z),
157 patch(Vector3::new(0.0, 0.0, s), y, z, 4.0 * s, s, x),
158 ],
159 SceneKind::Corridor => vec![
160 patch(Vector3::zeros(), x, y, s, 10.0 * s, z),
161 patch(Vector3::new(-s, 0.0, s), y, z, 10.0 * s, s, x),
162 patch(Vector3::new(s, 0.0, s), y, z, 10.0 * s, s, -x),
163 ],
164 SceneKind::Cylinder | SceneKind::Sphere => Vec::new(),
165 }
166}
167
168fn sample_patch(
169 patch: &Patch,
170 count: usize,
171 rng: &mut Rng,
172 points: &mut Vec<Vector3<f64>>,
173 normals: &mut Vec<Vector3<f64>>,
174) {
175 for _ in 0..count {
176 let position = patch.center
177 + patch.u * (rng.symmetric() * patch.half_u)
178 + patch.v * (rng.symmetric() * patch.half_v);
179 points.push(position);
180 normals.push(patch.normal);
181 }
182}
183
184fn sample_cylinder(
185 scale: f64,
186 count: usize,
187 rng: &mut Rng,
188 points: &mut Vec<Vector3<f64>>,
189 normals: &mut Vec<Vector3<f64>>,
190) {
191 let radius = scale * 0.5;
192 for _ in 0..count {
193 let angle = std::f64::consts::TAU * rng.unit();
194 let height = rng.symmetric() * scale;
195 let normal = Vector3::new(angle.cos(), angle.sin(), 0.0);
196 points.push(Vector3::new(radius * normal.x, radius * normal.y, height));
197 normals.push(normal);
198 }
199}
200
201fn sample_sphere(
202 scale: f64,
203 count: usize,
204 rng: &mut Rng,
205 points: &mut Vec<Vector3<f64>>,
206 normals: &mut Vec<Vector3<f64>>,
207) {
208 for _ in 0..count {
209 let mut direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
212 while direction.norm() < 1e-12 {
213 direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
214 }
215 let normal = direction.normalize();
216 points.push(normal * scale);
217 normals.push(normal);
218 }
219}
220
221pub(crate) fn generate(kind: SceneKind, params: SceneParams) -> Scene {
222 let mut rng = Rng::new(params.seed);
223 let mut points: Vec<Vector3<f64>> = Vec::new();
224 let mut normals: Vec<Vector3<f64>> = Vec::new();
225
226 match kind {
227 SceneKind::Cylinder => sample_cylinder(
228 params.scale,
229 params.points_per_face,
230 &mut rng,
231 &mut points,
232 &mut normals,
233 ),
234 SceneKind::Sphere => sample_sphere(
235 params.scale,
236 params.points_per_face,
237 &mut rng,
238 &mut points,
239 &mut normals,
240 ),
241 _ => {
242 for patch in patches(kind, params.scale) {
243 sample_patch(
244 &patch,
245 params.points_per_face,
246 &mut rng,
247 &mut points,
248 &mut normals,
249 );
250 }
251 }
252 }
253
254 if params.noise_sigma > 0.0 {
255 for point in &mut points {
256 *point += Vector3::new(
257 rng.normal(params.noise_sigma),
258 rng.normal(params.noise_sigma),
259 rng.normal(params.noise_sigma),
260 );
261 }
262 }
263
264 let inlier_count = points.len();
265
266 let outlier_count = (inlier_count as f64 * params.outlier_ratio).round() as usize;
273 if outlier_count > 0 {
274 let extent = params.outlier_extent * params.scale;
275 let (mut min, mut max) = (points[0], points[0]);
276 for point in &points {
277 min = min.inf(point);
278 max = max.sup(point);
279 }
280 for _ in 0..outlier_count {
281 let position = Vector3::new(
282 min.x - extent + rng.unit() * (max.x - min.x + 2.0 * extent),
283 min.y - extent + rng.unit() * (max.y - min.y + 2.0 * extent),
284 min.z - extent + rng.unit() * (max.z - min.z + 2.0 * extent),
285 );
286 let mut direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
287 while direction.norm() < 1e-12 {
288 direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
289 }
290 points.push(position);
291 normals.push(direction.normalize());
292 }
293 }
294
295 let mut cloud = PointCloud::with_capacity(points.len());
296 for point in &points {
297 cloud.push(*point);
298 }
299
300 Scene {
301 kind,
302 params,
303 points,
304 cloud,
305 normals,
306 inlier_count,
307 }
308}