use super::*;
use indicatrix::geometry::meet_solver::{MeetConstraint, SolvedTier};
use indicatrix_cut_core::{ConstraintTier, Design, PreformSpec, ScheduleMeta};
use std::collections::BTreeSet;
fn standard_round_brilliant_design() -> Design {
const GIRDLE_INDICES: [f64; 16] = [
0.0, 6.0, 12.0, 18.0, 24.0, 30.0, 36.0, 42.0, 48.0, 54.0, 60.0, 66.0, 72.0, 78.0, 84.0,
90.0,
];
const BREAK_INDICES: [f64; 16] = [
95.0, 1.0, 11.0, 13.0, 23.0, 25.0, 35.0, 37.0, 47.0, 49.0, 59.0, 61.0, 71.0, 73.0, 83.0,
85.0,
];
const MAIN_INDICES: [f64; 8] = [0.0, 12.0, 24.0, 36.0, 48.0, 60.0, 72.0, 84.0];
const STAR_INDICES: [f64; 8] = [6.0, 18.0, 30.0, 42.0, 54.0, 66.0, 78.0, 90.0];
fn tier(name: &str, angle_deg: f64, indices: &[f64], mast: f64) -> ConstraintTier {
ConstraintTier {
angle_deg,
name: name.to_string(),
indices: indices.to_vec(),
constraint: MeetConstraint::ScaleReference(mast),
imported_meet: None,
original_notes: None,
detached: Vec::new(),
}
}
let tiers = vec![
tier("Table", 0.0, &[], 0.32),
tier("Star", 15.0, &STAR_INDICES, 0.45),
tier("Crown Main", 34.5, &MAIN_INDICES, 0.59),
tier("Upper Girdle", 41.0, &BREAK_INDICES, 0.67),
tier("Girdle", 90.0, &GIRDLE_INDICES, 1.0),
tier("Pavilion Main", -41.0, &MAIN_INDICES, 0.67),
tier("Lower Girdle", -42.5, &BREAK_INDICES, 0.68),
tier("Culet", -0.0, &[], 0.88),
];
Design::new(
PreformSpec::block(2.0, 1.0, 2.0),
ScheduleMeta {
gemcad_version: "GemCad 5.0".to_string(),
gear_teeth: 96,
gear_reference_angle: 0.0,
symmetry_order: 8,
mirror: true,
refractive_index: 1.54,
headers: Vec::new(),
footnotes: Vec::new(),
},
tiers,
)
}
#[test]
fn plane_count_matches_design_planes_on_the_standard_round_brilliant() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let planes = design.planes_from_solved(&solved);
let map = FacetMap::from_design(&design, &solved);
assert_eq!(map.facets.len(), planes.len());
assert_eq!(map.preform_plane_count(), design.preform.planes().len());
}
#[test]
fn preform_planes_map_to_no_tier() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
for facet_id in 0..map.preform_plane_count() {
assert_eq!(map.tier_of(facet_id), None);
}
}
#[test]
fn orbit_sizes_match_each_tiers_index_count() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
for (tier_index, tier) in design.tiers.iter().enumerate() {
let expected = tier.indices.len().max(1);
assert_eq!(
map.facets_of_tier(tier_index).len(),
expected,
"tier {tier_index} ({})",
tier.name
);
}
}
#[test]
fn every_mapped_facets_normal_carries_its_tiers_own_elevation_angle() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let planes = design.planes_from_solved(&solved);
let map = FacetMap::from_design(&design, &solved);
for (facet_id, &(normal, _offset)) in planes.iter().enumerate().skip(map.preform_plane_count())
{
let tier_index = map
.tier_of(facet_id)
.expect("every non-preform facet must map to a tier");
let tier = &design.tiers[tier_index];
let theta = tier.angle_deg.abs().to_radians();
assert!(
(normal.y.abs() - theta.cos()).abs() < 1e-5,
"facet {facet_id} (tier {tier_index} {}): normal.y={}, expected +-{}",
tier.name,
normal.y,
theta.cos()
);
let horizontal = normal.x.hypot(normal.z);
assert!(
(horizontal - theta.sin()).abs() < 1e-5,
"facet {facet_id} (tier {tier_index} {}): horizontal={horizontal}, expected {}",
tier.name,
theta.sin()
);
}
}
#[test]
fn overlay_flags_mark_the_pending_and_selected_tiers_and_nothing_else_by_default() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
let n_d = design.effective_refractive_index();
let flags = map.overlay_flags(&design, n_d, Some(2), &BTreeSet::from([5]));
for &facet_id in map.facets_of_tier(5) {
assert!(flags.pending[facet_id as usize]);
}
for &facet_id in map.facets_of_tier(2) {
assert!(flags.selected[facet_id as usize]);
}
for &facet_id in map.facets_of_tier(3) {
assert!(!flags.pending[facet_id as usize]);
assert!(!flags.selected[facet_id as usize]);
}
assert!(
flags.flagged.iter().all(|&f| !f),
"diamond RBC must not window"
);
}
#[test]
fn overlay_flags_marks_every_member_of_a_multi_tier_pending_set() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
let n_d = design.effective_refractive_index();
let flags = map.overlay_flags(&design, n_d, None, &BTreeSet::from([2, 5]));
for &facet_id in map.facets_of_tier(2) {
assert!(flags.pending[facet_id as usize], "tier 2 must be pending");
}
for &facet_id in map.facets_of_tier(5) {
assert!(flags.pending[facet_id as usize], "tier 5 must be pending");
}
for &facet_id in map.facets_of_tier(3) {
assert!(
!flags.pending[facet_id as usize],
"tier 3 must not be pending"
);
}
}
#[test]
fn hover_text_omits_the_margin_clause_for_crown_and_girdle_facets() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
let n_d = design.effective_refractive_index();
let crown_facet = map.facets_of_tier(1)[0] as usize;
let pavilion_facet = map.facets_of_tier(5)[0] as usize;
let crown_text = map.hover_text(crown_facet, n_d);
let pavilion_text = map.hover_text(pavilion_facet, n_d);
assert!(
!crown_text.contains("margin"),
"a crown facet must not claim a windowing margin: {crown_text}"
);
assert!(
pavilion_text.contains("margin"),
"a pavilion facet must still report its margin: {pavilion_text}"
);
}
#[test]
fn index_on_gear_matches_the_tiers_own_index_and_is_zero_for_preform() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
assert_eq!(map.index_on_gear(0), 0, "a preform plane carries no index");
let first_main_facet = map.facets_of_tier(2)[0] as usize;
assert_eq!(map.index_on_gear(first_main_facet), 0);
}
#[test]
fn meeting_facet_pairs_cross_products_a_meet_nameds_two_orbits() {
fn tier(
name: &str,
angle_deg: f64,
indices: &[f64],
constraint: MeetConstraint,
) -> ConstraintTier {
ConstraintTier {
angle_deg,
name: name.to_string(),
indices: indices.to_vec(),
constraint,
imported_meet: None,
original_notes: None,
detached: Vec::new(),
}
}
let tiers = vec![
tier("Table", 0.0, &[], MeetConstraint::ScaleReference(0.3)),
tier(
"Star",
15.0,
&[6.0, 18.0, 30.0, 42.0],
MeetConstraint::MeetNamed(vec!["Table".to_string()]),
),
];
let design = Design::new(
PreformSpec::block(2.0, 1.0, 2.0),
ScheduleMeta {
gemcad_version: "GemCad 5.0".to_string(),
gear_teeth: 96,
gear_reference_angle: 0.0,
symmetry_order: 8,
mirror: true,
refractive_index: 1.54,
headers: Vec::new(),
footnotes: Vec::new(),
},
tiers,
);
let fabricated_solved: Vec<SolvedTier> = design
.tiers
.iter()
.map(|_| SolvedTier {
mast: 1.0,
strategy: indicatrix::geometry::meet_solver::SolveStrategy::ScaleReference,
detail: String::new(),
})
.collect();
let map = FacetMap::from_design(&design, &fabricated_solved);
let pairs = map.meeting_facet_pairs(&design);
assert_eq!(pairs.len(), 4, "got: {pairs:?}");
let table_facet = map.facets_of_tier(0)[0];
for &(a, b) in &pairs {
assert!(
a == table_facet || b == table_facet,
"every pair must involve Table's facet: {a},{b}"
);
}
}
#[test]
fn hover_text_reports_a_preform_plane_distinctly_from_a_facet() {
let design = standard_round_brilliant_design();
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
let n_d = design.effective_refractive_index();
assert_eq!(map.hover_text(0, n_d), "Preform");
let facet_text = map.hover_text(map.preform_plane_count(), n_d);
assert!(facet_text.contains("Table"), "got: {facet_text}");
assert!(facet_text.contains("index 0"), "got: {facet_text}");
}
#[test]
fn cheater_offset_moves_only_its_own_tiers_facets_in_the_2d_diagram() {
use super::super::diagram2d::{DiagramConfig, DiagramStyle, render_diagram};
use indicatrix::geometry::stone_metrics::{SolidStatus, build_solid_mesh};
let mut design = standard_round_brilliant_design();
let cheatered_tier = design
.tiers
.iter()
.position(|t| t.name == "Crown Main")
.expect("fixture must have a Crown Main tier");
let unrelated_tier_names = ["Table", "Girdle", "Pavilion Main", "Lower Girdle", "Culet"];
let solved = design.solve().expect("every tier is pinned");
let map = FacetMap::from_design(&design, &solved);
let plain_planes = design.planes_from_solved(&solved);
design.cheater_offsets_deg.insert(cheatered_tier, 0.5);
let cheatered_planes = design.planes_from_solved(&solved);
assert_ne!(
plain_planes, cheatered_planes,
"the cheater offset must change at least one plane"
);
let plain_mesh = match build_solid_mesh(&plain_planes) {
SolidStatus::Closed(m) => m,
other => panic!("plain fixture must close: {other:?}"),
};
let cheatered_mesh = match build_solid_mesh(&cheatered_planes) {
SolidStatus::Closed(m) => m,
other => panic!("cheatered fixture must close: {other:?}"),
};
let config = DiagramConfig {
width: 300,
height: 300,
gear_teeth: design.meta.gear_teeth_abs(),
gear_reference_angle: design.meta.gear_reference_angle as f32,
symmetry_order: design.meta.symmetry_order,
mirror: design.meta.mirror,
};
let style = DiagramStyle::default();
let plain_frame = render_diagram(&plain_mesh, &config, &style);
let cheatered_frame = render_diagram(&cheatered_mesh, &config, &style);
let unrelated_facet_ids: std::collections::BTreeSet<usize> = (0..map.facets.len())
.filter(|&id| {
map.tier_of(id)
.is_some_and(|t| unrelated_tier_names.contains(&design.tiers[t].name.as_str()))
})
.collect();
let cheatered_facet_ids: std::collections::BTreeSet<usize> = (0..map.facets.len())
.filter(|&id| map.tier_of(id) == Some(cheatered_tier))
.collect();
assert!(!unrelated_facet_ids.is_empty());
assert!(!cheatered_facet_ids.is_empty());
let mut unrelated_tier_pixel_differs = false;
let mut cheatered_tier_pixel_differs = false;
for y in 0..config.height {
for x in 0..config.width {
let plain_pick = plain_frame.pick_at(x, y).map(|id| id as usize);
let cheatered_pick = cheatered_frame.pick_at(x, y).map(|id| id as usize);
if plain_pick == cheatered_pick {
continue;
}
for id in [plain_pick, cheatered_pick].into_iter().flatten() {
if unrelated_facet_ids.contains(&id) {
unrelated_tier_pixel_differs = true;
}
if cheatered_facet_ids.contains(&id) {
cheatered_tier_pixel_differs = true;
}
}
}
}
assert!(
cheatered_tier_pixel_differs,
"the cheatered tier's own facet(s) must visibly move in the diagram -- \
if this fails, diagram2d.rs is not reading the already-rotated planes \
`Design::planes_from_solved` produces"
);
assert!(
!unrelated_tier_pixel_differs,
"a cheater offset on Crown Main must not move any pixel belonging to a \
tier that shares no plane with it"
);
}