use super::{decode_pcurve_cache, final_pcurve_patch_layout, pcurve_fit_tolerance};
use crate::nurbs::blend::{
decode_rolling_ball_curve, decode_rolling_ball_side, decode_rolling_ball_surface,
DecodedRollingBallCurve,
};
use crate::nurbs::core::{
curve_cache, decode_curve_cache, decode_surface_cache, final_curve_patch_layout,
final_surface_patch_layout, first_curve_patch_layout, surface_cache, surface_patch_layout_at,
};
use crate::nurbs::proc_curve::{
compound_patch_layout, extrusion_patch_layout, helix_patch_layout, intersection_patch_layout,
procedural_curve_resolving_refs, projection_patch_layout, rolling_ball_patch_layout,
silhouette_patch_layout, spring_patch_layout, subset_patch_layout, surface_curve_patch_layout,
surface_offset_patch_layout, three_surface_patch_layout, vector_offset_patch_layout,
ProjectionTailPatchLayout,
};
use crate::nurbs::proc_surface::{DecodedProceduralSurfaceDefinition, EmbeddedLawExpression};
use crate::nurbs::reader::NUBS_MARKER;
use crate::nurbs::subtypes::SubtypeTables;
use crate::nurbs::toks::{lex_test_span, test_table};
use cadmpeg_ir::geometry::{CurveGeometry, SurfaceGeometry};
use cadmpeg_ir::math::{Point3, Vector3};
fn push_int(out: &mut Vec<u8>, tag: u8, value: i64, int_width: usize) {
out.push(tag);
if int_width == 4 {
out.extend_from_slice(
&i32::try_from(value)
.expect("test value fits i32")
.to_le_bytes(),
);
} else {
out.extend_from_slice(&value.to_le_bytes());
}
}
fn push_f64(out: &mut Vec<u8>, value: f64) {
out.push(0x06);
out.extend_from_slice(&value.to_le_bytes());
}
fn push_ident(out: &mut Vec<u8>, value: &str) {
out.extend_from_slice(&[0x0d, value.len() as u8]);
out.extend_from_slice(value.as_bytes());
}
fn push_string(out: &mut Vec<u8>, value: &str) {
out.extend_from_slice(&[0x07, value.len() as u8]);
out.extend_from_slice(value.as_bytes());
}
fn push_position(out: &mut Vec<u8>, values: [f64; 3]) {
out.push(0x13);
for value in values {
out.extend_from_slice(&value.to_le_bytes());
}
}
fn push_vector(out: &mut Vec<u8>, values: [f64; 3]) {
out.push(0x14);
for value in values {
out.extend_from_slice(&value.to_le_bytes());
}
}
fn curve_block_with_endpoint(int_width: usize, endpoint: [f64; 3]) -> Vec<u8> {
let mut b = NUBS_MARKER.to_vec();
push_int(&mut b, 0x04, 1, int_width); push_int(&mut b, 0x15, 0, int_width); push_int(&mut b, 0x04, 2, int_width); push_f64(&mut b, 0.0);
push_int(&mut b, 0x04, 1, int_width);
push_f64(&mut b, 1.0);
push_int(&mut b, 0x04, 1, int_width);
for component in [0.0, 0.0, 0.0, endpoint[0], endpoint[1], endpoint[2]] {
push_f64(&mut b, component);
}
b
}
fn curve_block(int_width: usize) -> Vec<u8> {
curve_block_with_endpoint(int_width, [1.0, 2.0, 3.0])
}
fn surface_block_with_x_offset(int_width: usize, x_offset: f64) -> Vec<u8> {
let mut b = NUBS_MARKER.to_vec();
push_int(&mut b, 0x04, 1, int_width); push_int(&mut b, 0x04, 1, int_width); for _ in 0..4 {
push_int(&mut b, 0x15, 0, int_width); }
push_int(&mut b, 0x04, 2, int_width); push_int(&mut b, 0x04, 2, int_width); for _ in 0..2 {
push_f64(&mut b, 0.0);
push_int(&mut b, 0x04, 1, int_width);
push_f64(&mut b, 1.0);
push_int(&mut b, 0x04, 1, int_width);
}
for pole in 0..4 {
push_f64(&mut b, x_offset + f64::from(pole));
push_f64(&mut b, 0.0);
push_f64(&mut b, 0.0);
}
b
}
fn surface_block(int_width: usize) -> Vec<u8> {
surface_block_with_x_offset(int_width, 0.0)
}
fn pcurve_block(int_width: usize) -> Vec<u8> {
let mut b = NUBS_MARKER.to_vec();
push_int(&mut b, 0x04, 1, int_width);
push_int(&mut b, 0x15, 0, int_width);
push_int(&mut b, 0x04, 2, int_width);
for knot in [0.0, 1.0] {
push_f64(&mut b, knot);
push_int(&mut b, 0x04, 1, int_width);
}
for component in [0.0, 0.0, 1.0, 1.0] {
push_f64(&mut b, component);
}
b
}
fn exact_cache_first_curve(int_width: usize) -> Vec<u8> {
let mut bytes = vec![0x0f];
push_ident(&mut bytes, "exact_int_cur");
push_int(&mut bytes, 0x04, 23_100, int_width);
push_int(&mut bytes, 0x15, 0, int_width);
bytes.extend_from_slice(&curve_block(int_width));
push_f64(&mut bytes, 0.0);
push_ident(&mut bytes, "plane");
push_position(&mut bytes, [0.0, 0.0, 0.0]);
push_vector(&mut bytes, [0.0, 0.0, 1.0]);
push_vector(&mut bytes, [1.0, 0.0, 0.0]);
bytes.extend_from_slice(&[0x0b; 5]);
push_ident(&mut bytes, "null_surface");
bytes.extend_from_slice(&pcurve_block(int_width));
push_ident(&mut bytes, "nullbs");
bytes.extend_from_slice(&[0x0b; 2]);
for _ in 0..3 {
push_int(&mut bytes, 0x04, 0, int_width);
}
push_int(&mut bytes, 0x04, 0, int_width);
bytes.push(0x0a);
push_f64(&mut bytes, 1.0);
bytes.push(0x0a);
push_f64(&mut bytes, 0.0);
push_int(&mut bytes, 0x15, 0, int_width);
push_int(&mut bytes, 0x15, 0, int_width);
bytes.push(0x10);
bytes
}
#[test]
fn intcurve_selector_uses_the_serialized_direct_slot() {
for int_width in [4usize, 8] {
let mut bytes = curve_block(int_width);
bytes.extend_from_slice(&pcurve_block(int_width));
let toks = crate::nurbs::toks::lex_test_span(&bytes, int_width);
let table = crate::nurbs::toks::test_table(&bytes, int_width);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, 2, &table)
.is_some()
);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, -2, &table)
.is_some()
);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, 1, &table)
.is_none()
);
}
}
#[test]
fn exact_curve_selector_uses_its_cache_first_support_slot() {
for int_width in [4usize, 8] {
let bytes = exact_cache_first_curve(int_width);
let toks = crate::nurbs::toks::lex_test_span(&bytes, int_width);
let table = crate::nurbs::toks::test_table(&bytes, int_width);
let decoded = crate::nurbs::proc_curve::procedural_curve_resolving_refs(&toks, &table)
.unwrap_or_else(|| panic!("exact curve at width {int_width}"));
assert_eq!(decoded.native_kind, "exact_int_cur");
let pcurve = crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, 1, &table)
.unwrap_or_else(|| panic!("exact curve pcurve at width {int_width}"));
assert_eq!(
pcurve.control_points[1],
cadmpeg_ir::math::Point2::new(10.0, -10.0)
);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, -1, &table)
.is_some()
);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, 2, &table)
.is_none()
);
}
}
#[test]
fn exact_curve_selector_follows_subtype_reference() {
for int_width in [4usize, 8] {
let active = exact_cache_first_curve(int_width);
for named in [false, true] {
let mut wrapper = vec![0x0f];
if named {
push_ident(&mut wrapper, "ref");
}
push_int(&mut wrapper, 0x04, 0, int_width);
wrapper.push(0x10);
let toks = crate::nurbs::toks::lex_test_span(&wrapper, int_width);
let table = crate::nurbs::toks::test_table(&active, int_width);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, -1, &table)
.is_some()
);
assert!(
crate::nurbs::proc_curve::pcurve_for_selector_resolving_refs(&toks, 2, &table)
.is_none()
);
}
}
}
fn rolling_ball_side(int_width: usize, label: &str) -> Vec<u8> {
let mut bytes = Vec::new();
push_string(
&mut bytes,
if label == "left" {
"blend_support_surface"
} else {
"blend_support_curve"
},
);
push_ident(&mut bytes, "null_surface");
bytes.extend_from_slice(&curve_block(int_width));
bytes.extend_from_slice(&[0x0b, 0x0b]);
bytes.extend_from_slice(&pcurve_block(int_width));
push_position(&mut bytes, [7.0, 8.0, 9.0]);
push_ident(&mut bytes, "nullbs");
push_int(&mut bytes, 0x04, 0, int_width);
push_ident(&mut bytes, "nullbs");
bytes
}
fn variable_blend_side(int_width: usize, name: &str, extension: Option<i64>) -> Vec<u8> {
let mut bytes = Vec::new();
push_string(&mut bytes, name);
push_ident(&mut bytes, "null_surface");
push_ident(&mut bytes, "null_curve");
bytes.extend_from_slice(&pcurve_block(int_width));
push_position(&mut bytes, [1.0, 2.0, 3.0]);
push_ident(&mut bytes, "nullbs");
if let Some(extension) = extension {
push_int(&mut bytes, 0x04, extension, int_width);
push_ident(&mut bytes, "nullbs");
}
bytes.push(0x10);
bytes
}
mod blend_laws;
mod cache_selection;
mod procedural_curves;
mod procedural_surfaces;