use nalgebra::{Vector3, Vector6};
use rigidity_core::PointCloud;
use crate::rng::Rng;
use crate::{Scene, SceneParams};
fn basis(index: usize) -> Vector6<f64> {
let mut v = Vector6::zeros();
v[index] = 1.0;
v
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SceneKind {
Plane,
Cylinder,
Sphere,
TwoPlanes,
Corner,
TeeJoint,
Corridor,
}
impl SceneKind {
pub const ALL: [SceneKind; 7] = [
Self::Plane,
Self::Cylinder,
Self::Sphere,
Self::TwoPlanes,
Self::Corner,
Self::TeeJoint,
Self::Corridor,
];
pub fn name(self) -> &'static str {
match self {
Self::Plane => "plane",
Self::Cylinder => "cylinder",
Self::Sphere => "sphere",
Self::TwoPlanes => "two planes",
Self::Corner => "trihedral corner",
Self::TeeJoint => "tee joint",
Self::Corridor => "corridor",
}
}
pub fn nullspace(self) -> Vec<Vector6<f64>> {
match self {
Self::Plane => vec![basis(0), basis(1), basis(5)],
Self::Cylinder => vec![basis(2), basis(5)],
Self::Sphere => vec![basis(3), basis(4), basis(5)],
Self::TwoPlanes | Self::TeeJoint | Self::Corridor => vec![basis(1)],
Self::Corner => Vec::new(),
}
}
pub fn nullspace_dimension(self) -> usize {
self.nullspace().len()
}
}
struct Patch {
center: Vector3<f64>,
u: Vector3<f64>,
v: Vector3<f64>,
half_u: f64,
half_v: f64,
normal: Vector3<f64>,
}
fn axes() -> (Vector3<f64>, Vector3<f64>, Vector3<f64>) {
(Vector3::x(), Vector3::y(), Vector3::z())
}
fn patches(kind: SceneKind, s: f64) -> Vec<Patch> {
let (x, y, z) = axes();
let patch = |center: Vector3<f64>,
u: Vector3<f64>,
v: Vector3<f64>,
half_u: f64,
half_v: f64,
normal: Vector3<f64>| Patch {
center,
u,
v,
half_u,
half_v,
normal,
};
match kind {
SceneKind::Plane => vec![patch(Vector3::zeros(), x, y, s, s, z)],
SceneKind::TwoPlanes => vec![
patch(Vector3::zeros(), x, y, s, s, z),
patch(Vector3::new(0.0, 0.0, s), y, z, s, s, x),
],
SceneKind::Corner => vec![
patch(Vector3::new(s, s, 0.0), x, y, s, s, z),
patch(Vector3::new(0.0, s, s), y, z, s, s, x),
patch(Vector3::new(s, 0.0, s), x, z, s, s, y),
],
SceneKind::TeeJoint => vec![
patch(Vector3::zeros(), x, y, s, 4.0 * s, z),
patch(Vector3::new(0.0, 0.0, s), y, z, 4.0 * s, s, x),
],
SceneKind::Corridor => vec![
patch(Vector3::zeros(), x, y, s, 10.0 * s, z),
patch(Vector3::new(-s, 0.0, s), y, z, 10.0 * s, s, x),
patch(Vector3::new(s, 0.0, s), y, z, 10.0 * s, s, -x),
],
SceneKind::Cylinder | SceneKind::Sphere => Vec::new(),
}
}
fn sample_patch(
patch: &Patch,
count: usize,
rng: &mut Rng,
points: &mut Vec<Vector3<f64>>,
normals: &mut Vec<Vector3<f64>>,
) {
for _ in 0..count {
let position = patch.center
+ patch.u * (rng.symmetric() * patch.half_u)
+ patch.v * (rng.symmetric() * patch.half_v);
points.push(position);
normals.push(patch.normal);
}
}
fn sample_cylinder(
scale: f64,
count: usize,
rng: &mut Rng,
points: &mut Vec<Vector3<f64>>,
normals: &mut Vec<Vector3<f64>>,
) {
let radius = scale * 0.5;
for _ in 0..count {
let angle = std::f64::consts::TAU * rng.unit();
let height = rng.symmetric() * scale;
let normal = Vector3::new(angle.cos(), angle.sin(), 0.0);
points.push(Vector3::new(radius * normal.x, radius * normal.y, height));
normals.push(normal);
}
}
fn sample_sphere(
scale: f64,
count: usize,
rng: &mut Rng,
points: &mut Vec<Vector3<f64>>,
normals: &mut Vec<Vector3<f64>>,
) {
for _ in 0..count {
let mut direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
while direction.norm() < 1e-12 {
direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
}
let normal = direction.normalize();
points.push(normal * scale);
normals.push(normal);
}
}
pub(crate) fn generate(kind: SceneKind, params: SceneParams) -> Scene {
let mut rng = Rng::new(params.seed);
let mut points: Vec<Vector3<f64>> = Vec::new();
let mut normals: Vec<Vector3<f64>> = Vec::new();
match kind {
SceneKind::Cylinder => sample_cylinder(
params.scale,
params.points_per_face,
&mut rng,
&mut points,
&mut normals,
),
SceneKind::Sphere => sample_sphere(
params.scale,
params.points_per_face,
&mut rng,
&mut points,
&mut normals,
),
_ => {
for patch in patches(kind, params.scale) {
sample_patch(
&patch,
params.points_per_face,
&mut rng,
&mut points,
&mut normals,
);
}
}
}
if params.noise_sigma > 0.0 {
for point in &mut points {
*point += Vector3::new(
rng.normal(params.noise_sigma),
rng.normal(params.noise_sigma),
rng.normal(params.noise_sigma),
);
}
}
let inlier_count = points.len();
let outlier_count = (inlier_count as f64 * params.outlier_ratio).round() as usize;
if outlier_count > 0 {
let extent = params.outlier_extent * params.scale;
let (mut min, mut max) = (points[0], points[0]);
for point in &points {
min = min.inf(point);
max = max.sup(point);
}
for _ in 0..outlier_count {
let position = Vector3::new(
min.x - extent + rng.unit() * (max.x - min.x + 2.0 * extent),
min.y - extent + rng.unit() * (max.y - min.y + 2.0 * extent),
min.z - extent + rng.unit() * (max.z - min.z + 2.0 * extent),
);
let mut direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
while direction.norm() < 1e-12 {
direction = Vector3::new(rng.normal(1.0), rng.normal(1.0), rng.normal(1.0));
}
points.push(position);
normals.push(direction.normalize());
}
}
let mut cloud = PointCloud::with_capacity(points.len());
for point in &points {
cloud.push(*point);
}
Scene {
kind,
params,
points,
cloud,
normals,
inlier_count,
}
}