use glam::DVec3;
use super::super::{
BLANK_DOMINATION_FACTOR, DEFAULT_PLAUSIBLE_SCALE, MAX_PLANES, MeetConstraint, MeetTierInput,
SolveStrategy, SolvedTier,
blocks::{Block, classify_blocks, tier_sides},
candidates::tier_normals,
names::MeetNameResolver,
};
pub(in crate::geometry::meet_solver) struct SolveContext<'a> {
pub(in crate::geometry::meet_solver) tiers: &'a [MeetTierInput],
pub(super) normals: Vec<Vec<DVec3>>,
pub(super) blocks: Vec<Block>,
pub(super) resolved_named: Vec<Vec<usize>>,
pub(in crate::geometry::meet_solver) is_anchor: Vec<bool>,
pub(super) scale_prior: f64,
pub(super) scale_norm: f64,
pub(super) domination_limit: f64,
pub(in crate::geometry::meet_solver) total_planes: usize,
}
fn pow2_scale_norm(scale: f64) -> f64 {
if scale.is_finite() && scale > 0.0 {
2f64.powi(scale.log2().round() as i32)
} else {
1.0
}
}
impl<'a> SolveContext<'a> {
pub(in crate::geometry::meet_solver) fn new(
gear_teeth_abs: u32,
tiers: &'a [MeetTierInput],
) -> Self {
let sides = tier_sides(tiers);
let normals: Vec<Vec<DVec3>> = tiers
.iter()
.zip(&sides)
.map(|(t, &crown)| tier_normals(gear_teeth_abs, t.angle_deg, &t.indices, crown))
.collect();
let blocks = classify_blocks(tiers);
let resolver = MeetNameResolver::new(tiers);
let resolved_named: Vec<Vec<usize>> = tiers
.iter()
.enumerate()
.map(|(i, t)| match &t.constraint {
MeetConstraint::MeetNamed(names) => {
let mut refs = resolver.resolve_names(names).refs;
refs.retain(|&r| r != i);
refs
}
_ => Vec::new(),
})
.collect();
let is_anchor: Vec<bool> = tiers
.iter()
.map(|t| matches!(t.constraint, MeetConstraint::ScaleReference(_)))
.collect();
let scale_prior = tiers
.iter()
.filter_map(|t| match &t.constraint {
MeetConstraint::ScaleReference(v) => Some(v.abs()),
_ => None,
})
.fold(0.0_f64, |acc, v| {
if acc.is_nan() || v.is_nan() {
f64::NAN
} else {
acc.max(v)
}
});
let scale_prior = if scale_prior > 1e-9 {
scale_prior
} else {
DEFAULT_PLAUSIBLE_SCALE
};
let scale_norm = pow2_scale_norm(scale_prior);
let domination_limit = BLANK_DOMINATION_FACTOR * (scale_prior / scale_norm);
let total_planes = 6 + normals.iter().map(Vec::len).sum::<usize>();
Self {
tiers,
normals,
blocks,
resolved_named,
is_anchor,
scale_prior,
scale_norm,
domination_limit,
total_planes,
}
}
pub(in crate::geometry::meet_solver) fn failed_solved(&self) -> Vec<SolvedTier> {
let total_planes = self.total_planes;
self.tiers
.iter()
.map(|t| match &t.constraint {
MeetConstraint::ScaleReference(v) => SolvedTier {
mast: v.abs(),
strategy: SolveStrategy::ScaleReference,
detail: "given (scale reference)".to_string(),
},
_ => SolvedTier {
mast: self.scale_prior,
strategy: SolveStrategy::Failed,
detail: format!(
"design has {total_planes} planes, above the {MAX_PLANES}-plane cap \
for candidate-vertex enumeration"
),
},
})
.collect()
}
}