use super::{Candidate, DEDUP_QUANTUM, FacetInfo, FacetMap};
use glam::Vec3;
use indicatrix::geometry::meet_solver::{SolvedTier, classify_blocks};
use indicatrix_cut_core::Design;
impl FacetMap {
#[must_use]
pub fn from_design(design: &Design, solved: &[SolvedTier]) -> Self {
let preform_plane_count = design.preform.planes().len();
let inputs = design.meet_tier_inputs();
let blocks = classify_blocks(&inputs);
let gear_teeth = (design.meta.gear_teeth_abs().max(1)) as f32;
let mut candidates: Vec<Candidate> = Vec::new();
let mut last_side_is_crown = true;
for (tier_index, tier) in design.tiers.iter().enumerate() {
let is_crown = if tier.angle_deg == 0.0 {
if tier.angle_deg.is_sign_negative() {
false
} else {
last_side_is_crown
}
} else {
tier.angle_deg > 0.0
};
last_side_is_crown = is_crown;
let theta = (tier.angle_deg.abs() as f32).to_radians();
let (sin_theta, cos_theta) = (theta.sin(), theta.cos());
let mast = solved.get(tier_index).map_or(0.0, |s| s.mast);
let offset = -(mast.abs() as f32);
if tier.indices.is_empty() {
let normal = if is_crown {
Vec3::new(0.0, cos_theta, sin_theta)
} else {
Vec3::new(0.0, -cos_theta, sin_theta)
};
candidates.push(Candidate {
normal: normal.normalize(),
offset,
tier_index,
index_on_gear: 0,
});
continue;
}
for &idx in &tier.indices {
let phi = 2.0 * std::f32::consts::PI * (idx as f32) / gear_teeth;
let (sin_phi, cos_phi) = (phi.sin(), phi.cos());
let normal = if is_crown {
Vec3::new(sin_theta * cos_phi, cos_theta, sin_theta * sin_phi)
} else {
Vec3::new(sin_theta * cos_phi, -cos_theta, sin_theta * sin_phi)
};
candidates.push(Candidate {
normal: normal.normalize(),
offset,
tier_index,
index_on_gear: idx.round() as u32,
});
}
}
let mut kept: Vec<Candidate> = Vec::with_capacity(candidates.len());
for candidate in candidates {
let is_duplicate = kept.iter().any(|k| {
let dot = k.normal.dot(candidate.normal);
dot >= 1.0 - DEDUP_QUANTUM && (k.offset - candidate.offset).abs() <= DEDUP_QUANTUM
});
if !is_duplicate {
kept.push(candidate);
}
}
let mut facets = vec![FacetInfo::preform(); preform_plane_count];
let mut orbits: Vec<Vec<u32>> = vec![Vec::new(); design.tiers.len()];
for candidate in kept {
let facet_id = facets.len() as u32;
let block = blocks.get(candidate.tier_index).copied();
let (angle_deg, name) = design
.tiers
.get(candidate.tier_index)
.map_or((0.0, String::new()), |t| (t.angle_deg, t.name.clone()));
facets.push(FacetInfo {
tier_index: Some(candidate.tier_index),
index_on_gear: candidate.index_on_gear,
angle_deg,
block,
name,
});
if let Some(orbit) = orbits.get_mut(candidate.tier_index) {
orbit.push(facet_id);
}
}
Self {
preform_plane_count,
facets,
orbits,
}
}
}