use cadmpeg_ir::geometry::{CurveGeometry, SurfaceGeometry};
use cadmpeg_ir::math::{Point3, Vector3};
const SHIFTS: [usize; 4] = [0, 2, 4, 6];
#[derive(Debug, Clone)]
pub struct DecodedSurface {
pub pos: usize,
pub len: usize,
pub geometry: SurfaceGeometry,
}
#[derive(Debug, Clone)]
pub struct DecodedCurve {
pub pos: usize,
pub len: usize,
pub geometry: CurveGeometry,
}
#[derive(Debug, Clone)]
pub struct DecodedPoint {
pub pos: usize,
pub position: Point3,
}
const SURFACE_TAGS: [(u8, usize); 5] = [
(0x32, 91), (0x33, 99), (0x34, 115), (0x35, 99), (0x36, 107), ];
const CURVE_TAGS: [(u8, usize); 3] = [
(0x1e, 67), (0x1f, 99), (0x20, 107), ];
pub fn points(stream: &[u8]) -> Vec<DecodedPoint> {
let mut out = Vec::new();
let mut occupied_end = 0usize;
let mut p = 0usize;
while p + 2 <= stream.len() {
if stream[p] == 0x00 && stream[p + 1] == 0x1d && p >= occupied_end {
if let Some((xyz, shift)) = SHIFTS.into_iter().find_map(|shift| {
read_vec3(stream, p + 16 + shift).and_then(|xyz| {
(xyz.iter().all(|v| v.abs() < 1.0e6) && xyz.iter().any(|v| *v != 0.0))
.then_some((xyz, shift))
})
}) {
out.push(DecodedPoint {
pos: p,
position: mm_point(xyz),
});
occupied_end = p + 40 + shift; p += 2;
continue;
}
}
p += 1;
}
out
}
pub fn surfaces(stream: &[u8]) -> Vec<DecodedSurface> {
let mut out = Vec::new();
let mut occupied_end = 0usize;
let mut p = 0usize;
while p + 2 <= stream.len() {
if stream[p] == 0x00 && p >= occupied_end {
if let Some((_, len)) = SURFACE_TAGS.iter().find(|(t, _)| *t == stream[p + 1]) {
if let Some(geom) = decode_surface(stream, p, stream[p + 1]) {
out.push(DecodedSurface {
pos: p,
len: *len,
geometry: geom,
});
occupied_end = p + *len;
p += 2;
continue;
}
}
}
p += 1;
}
out
}
pub fn curves(stream: &[u8]) -> Vec<DecodedCurve> {
let mut out = Vec::new();
let mut occupied_end = 0usize;
let mut p = 0usize;
while p + 2 <= stream.len() {
if stream[p] == 0x00 && p >= occupied_end {
if let Some((_, len)) = CURVE_TAGS.iter().find(|(t, _)| *t == stream[p + 1]) {
if let Some(geom) = decode_curve(stream, p, stream[p + 1]) {
out.push(DecodedCurve {
pos: p,
len: *len,
geometry: geom,
});
occupied_end = p + *len;
p += 2;
continue;
}
}
}
p += 1;
}
out
}
fn decode_surface(stream: &[u8], p: usize, kind: u8) -> Option<SurfaceGeometry> {
for sh in SHIFTS {
let b = p + sh;
let geom = match kind {
0x32 => plane(stream, b),
0x33 => cylinder(stream, b),
0x34 => cone(stream, b),
0x35 => sphere(stream, b),
0x36 => torus(stream, b),
_ => None,
};
if geom.is_some() {
return geom;
}
}
None
}
fn decode_curve(stream: &[u8], p: usize, kind: u8) -> Option<CurveGeometry> {
for sh in SHIFTS {
let b = p + sh;
let geom = match kind {
0x1e => line(stream, b),
0x1f => circle(stream, b),
0x20 => ellipse(stream, b),
_ => None,
};
if geom.is_some() {
return geom;
}
}
None
}
fn plane(s: &[u8], b: usize) -> Option<SurfaceGeometry> {
let origin = read_vec3(s, b + 19)?;
let normal = read_vec3(s, b + 43)?;
let x_axis = read_vec3(s, b + 67)?;
if !is_unit(normal) || !is_unit(x_axis) || !model_scale(origin) {
return None;
}
Some(SurfaceGeometry::Plane {
origin: mm_point(origin),
normal: vec3(normal),
u_axis: vec3(x_axis),
})
}
fn cylinder(s: &[u8], b: usize) -> Option<SurfaceGeometry> {
let origin = read_vec3(s, b + 19)?;
let axis = read_vec3(s, b + 43)?;
let radius = read_f64(s, b + 67)?;
let x_axis = read_vec3(s, b + 75)?;
if !is_unit(axis) || !is_unit(x_axis) || !model_scale(origin) || !in_radius(radius) {
return None;
}
Some(SurfaceGeometry::Cylinder {
origin: mm_point(origin),
axis: vec3(axis),
ref_direction: vec3(x_axis),
radius: radius * 1000.0,
})
}
fn cone(s: &[u8], b: usize) -> Option<SurfaceGeometry> {
let origin = read_vec3(s, b + 19)?;
let axis = read_vec3(s, b + 43)?;
let radius = read_f64(s, b + 67)?;
let sin_half = read_f64(s, b + 75)?;
let cos_half = read_f64(s, b + 83)?;
let x_axis = read_vec3(s, b + 91)?;
if !is_unit(axis)
|| !is_unit(x_axis)
|| !model_scale(origin)
|| !(0.0..=1.0e3).contains(&radius)
{
return None;
}
if (sin_half * sin_half + cos_half * cos_half - 1.0).abs() > 1.0e-6 {
return None;
}
Some(SurfaceGeometry::Cone {
origin: mm_point(origin),
axis: vec3(axis),
ref_direction: vec3(x_axis),
radius: radius * 1000.0,
half_angle: sin_half.abs().atan2(cos_half.abs()),
})
}
fn sphere(s: &[u8], b: usize) -> Option<SurfaceGeometry> {
let center = read_vec3(s, b + 19)?;
let radius = read_f64(s, b + 43)?;
let axis = read_vec3(s, b + 51)?;
let x_axis = read_vec3(s, b + 75)?;
if !is_unit(axis) || !is_unit(x_axis) || !model_scale(center) || !in_radius(radius) {
return None;
}
Some(SurfaceGeometry::Sphere {
center: mm_point(center),
axis: vec3(axis),
ref_direction: vec3(x_axis),
radius: radius * 1000.0,
})
}
fn torus(s: &[u8], b: usize) -> Option<SurfaceGeometry> {
let center = read_vec3(s, b + 19)?;
let axis = read_vec3(s, b + 43)?;
let major = read_f64(s, b + 67)?;
let minor = read_f64(s, b + 75)?;
let x_axis = read_vec3(s, b + 83)?;
if !is_unit(axis)
|| !is_unit(x_axis)
|| !model_scale(center)
|| !in_radius(major)
|| !in_radius(minor)
{
return None;
}
Some(SurfaceGeometry::Torus {
center: mm_point(center),
axis: vec3(axis),
ref_direction: vec3(x_axis),
major_radius: major * 1000.0,
minor_radius: minor * 1000.0,
})
}
fn line(s: &[u8], b: usize) -> Option<CurveGeometry> {
let origin = read_vec3(s, b + 19)?;
let direction = read_vec3(s, b + 43)?;
if !is_unit(direction) || !model_scale(origin) {
return None;
}
Some(CurveGeometry::Line {
origin: mm_point(origin),
direction: vec3(direction),
})
}
fn circle(s: &[u8], b: usize) -> Option<CurveGeometry> {
let center = read_vec3(s, b + 19)?;
let normal = read_vec3(s, b + 43)?;
let x_axis = read_vec3(s, b + 67)?;
let radius = read_f64(s, b + 91)?;
if !is_unit(normal) || !is_unit(x_axis) || !model_scale(center) || !in_radius(radius) {
return None;
}
Some(CurveGeometry::Circle {
center: mm_point(center),
axis: vec3(normal),
ref_direction: vec3(x_axis),
radius: radius * 1000.0,
})
}
fn ellipse(s: &[u8], b: usize) -> Option<CurveGeometry> {
let center = read_vec3(s, b + 19)?;
let normal = read_vec3(s, b + 43)?;
let x_axis = read_vec3(s, b + 67)?;
let major = read_f64(s, b + 91)?;
let minor = read_f64(s, b + 99)?;
if !is_unit(normal) || !is_unit(x_axis) || !model_scale(center) {
return None;
}
if !in_radius(major) || !in_radius(minor) || minor > major + 1.0e-9 {
return None;
}
Some(CurveGeometry::Ellipse {
center: mm_point(center),
axis: vec3(normal),
major_direction: vec3(x_axis),
major_radius: major * 1000.0,
minor_radius: minor * 1000.0,
})
}
fn read_f64(s: &[u8], at: usize) -> Option<f64> {
s.get(at..at + 8)
.map(|w| f64::from_be_bytes([w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]]))
}
fn read_vec3(s: &[u8], at: usize) -> Option<[f64; 3]> {
Some([
read_f64(s, at)?,
read_f64(s, at + 8)?,
read_f64(s, at + 16)?,
])
}
fn is_unit(v: [f64; 3]) -> bool {
if !v.iter().all(|c| c.is_finite()) {
return false;
}
let n2 = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
(n2 - 1.0).abs() < 1.0e-6
}
fn model_scale(v: [f64; 3]) -> bool {
v.iter().all(|c| c.is_finite() && c.abs() < 1.0e3)
}
fn in_radius(r: f64) -> bool {
r.is_finite() && r > 1.0e-9 && r < 1.0e3
}
fn mm_point(v: [f64; 3]) -> Point3 {
Point3::new(v[0] * 1000.0, v[1] * 1000.0, v[2] * 1000.0)
}
fn vec3(v: [f64; 3]) -> Vector3 {
Vector3::new(v[0], v[1], v[2])
}