mod utils;
#[cfg(feature = "monstertruck")]
mod tests {
use crate::utils::default_end_cap_type;
use crate::utils::{assert_file_matches, test_output_path};
use monstertruck::geometry::prelude::{BsplineCurve, KnotVector, ParametricSurface};
use monstertruck::modeling::{Curve, Edge, Face, Shell, Surface, Vertex, Wire};
use monstertruck::step::save::{CompleteStepDisplay, StepHeaderDescriptor, StepModel};
use opensubdiv_petite::far::{
AdaptiveRefinementOptions, PatchTable, PatchTableOptions, PrimvarRefiner,
TopologyDescriptor, TopologyRefiner, TopologyRefinerOptions,
};
use opensubdiv_petite::monstertruck::PatchTableExt;
#[test]
fn test_simple_cube_single_patch() -> anyhow::Result<()> {
let vertex_positions = vec![
[-0.5, -0.5, 0.5],
[0.5, -0.5, 0.5],
[0.5, -0.5, -0.5],
[-0.5, -0.5, -0.5],
[-0.5, 0.5, 0.5],
[0.5, 0.5, 0.5],
[0.5, 0.5, -0.5],
[-0.5, 0.5, -0.5],
];
let face_vertices = [
vec![0, 1, 5, 4], vec![2, 3, 7, 6], vec![0, 4, 7, 3], vec![1, 2, 6, 5], vec![0, 3, 2, 1], vec![4, 5, 6, 7], ];
let num_face_vertices = face_vertices
.iter()
.map(|f| f.len() as u32)
.collect::<Vec<_>>();
let face_indices_u32: Vec<u32> =
face_vertices.iter().flatten().map(|&i| i as u32).collect();
let descriptor = TopologyDescriptor::new(
vertex_positions.len(),
&num_face_vertices, &face_indices_u32, )?;
let refiner_options = TopologyRefinerOptions::default();
let mut refiner = TopologyRefiner::new(descriptor, refiner_options)?;
let adaptive_options = AdaptiveRefinementOptions {
isolation_level: 3,
..Default::default()
};
refiner.refine_adaptive(adaptive_options, &[]);
let primvar_refiner = PrimvarRefiner::new(&refiner)?;
let total_vertices = refiner.vertex_count_all_levels();
let mut all_vertices = Vec::with_capacity(total_vertices);
all_vertices.extend_from_slice(&vertex_positions);
let num_levels = refiner.refinement_levels();
let mut level_start = 0;
for level in 1..num_levels {
let prev_level_count = refiner
.level(level - 1)
.map(|l| l.vertex_count())
.unwrap_or(0);
let _level_verts = refiner.level(level).map(|l| l.vertex_count()).unwrap_or(0);
let src_data: Vec<f32> = all_vertices[level_start..level_start + prev_level_count]
.iter()
.flat_map(|v| v.iter().copied())
.collect();
if let Some(refined) = primvar_refiner.interpolate(level, 3, &src_data) {
let level_vertices: Vec<[f32; 3]> = refined
.chunks_exact(3)
.map(|chunk| [chunk[0], chunk[1], chunk[2]])
.collect();
all_vertices.extend_from_slice(&level_vertices);
}
level_start += prev_level_count;
}
let patch_options = PatchTableOptions::new().end_cap_type(default_end_cap_type());
let patch_table = PatchTable::new(&refiner, Some(patch_options))?;
let surfaces = patch_table.to_monstertruck_surfaces(&all_vertices)?;
if surfaces.is_empty() {
panic!("No surfaces created!");
}
let first_surface = surfaces.into_iter().next().unwrap();
let p00 = first_surface.evaluate(1.0 / 3.0, 1.0 / 3.0);
let p10 = first_surface.evaluate(2.0 / 3.0, 1.0 / 3.0);
let p11 = first_surface.evaluate(2.0 / 3.0, 2.0 / 3.0);
let p01 = first_surface.evaluate(1.0 / 3.0, 2.0 / 3.0);
let v00 = Vertex::new(p00);
let v10 = Vertex::new(p10);
let v11 = Vertex::new(p11);
let v01 = Vertex::new(p01);
let e0 = Edge::new(
&v00,
&v10,
Curve::BsplineCurve(BsplineCurve::new(
KnotVector::bezier_knot(1),
vec![p00, p10],
)),
);
let e1 = Edge::new(
&v10,
&v11,
Curve::BsplineCurve(BsplineCurve::new(
KnotVector::bezier_knot(1),
vec![p10, p11],
)),
);
let e2 = Edge::new(
&v11,
&v01,
Curve::BsplineCurve(BsplineCurve::new(
KnotVector::bezier_knot(1),
vec![p11, p01],
)),
);
let e3 = Edge::new(
&v01,
&v00,
Curve::BsplineCurve(BsplineCurve::new(
KnotVector::bezier_knot(1),
vec![p01, p00],
)),
);
let wire = Wire::from(vec![e0, e1, e2, e3]);
let face = Face::new(vec![wire], Surface::BsplineSurface(first_surface));
let shell = Shell::from(vec![face]);
let compressed = shell.compress();
let step_string = CompleteStepDisplay::new(
StepModel::from(&compressed),
StepHeaderDescriptor {
file_name: "simple_cube_single_patch.step".to_owned(),
..Default::default()
},
)
.to_string();
let step_path = test_output_path("simple_cube_single_patch.step");
std::fs::write(&step_path, &step_string)?;
assert_file_matches(&step_path, "simple_cube_single_patch.step");
Ok(())
}
}