1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Maps a rasterized `facet_id` (a plane index, see `raster::SolidRasterizer::pick_at`)
//! back to the tier/orbit-member it came from, for hover/click/selection and the
//! critical-angle overlay.
//!
//! # Mirroring `Design::planes_from_solved`'s plane order exactly
//!
//! [`FacetMap::from_design`] must assign facet ids that line up 1:1 with
//! `indicatrix_cut_core::Design::planes_from_solved`'s output (`preform.planes()` first,
//! then one plane per (tier, index-on-gear) pair), since that plane slice is exactly
//! what `build_solid_mesh` -- and therefore `pick_at`'s `facet_id` -- indexes into.
//!
//! `StandardGemCuts::from_asc_schedule` is not a simple flatten: its last step is
//! `dedup_planes`, a linear, first-occurrence-wins scan dropping any facet whose
//! normal/offset are within tolerance of one already kept, with no "give me the
//! provenance" entry point, so this module reimplements the same three steps against
//! `Design`'s own tier list: (1) the same crown/pavilion side inheritance for an
//! unsigned-zero `angle_deg` (a running `last_side_is_crown` flag); (2) the same
//! per-(tier, index) normal construction (`sin`/`cos` of `theta = angle_deg.abs()`,
//! azimuth `phi = 2*pi*index/gear_teeth`) at `f32` precision; (3) the same
//! first-occurrence-wins dedup, at the same `1/2048` quantum.
//!
//! A candidate carries its `(tier_index, index_on_gear)` provenance through all
//! three steps, so the facet id a surviving candidate ends up at is the exact index
//! [`Design::planes_from_solved`] gives the same plane, by construction rather than
//! by re-matching floats after the fact.
use Vec3;
use Block;
/// Everything this map remembers about one facet beyond its bare plane equation.
///
/// `tier_index` is `None` for a preform plane (the rough's own bounding planes,
/// preceding every schedule-derived facet); other fields are placeholders then.
/// The critical-angle-overlay and selection-tint flags [`FacetMap::overlay_flags`]
/// produces.
///
/// Sized to the full plane count and indexed by `facet_id` -- ready to
/// drop straight into `raster::SolidStyle::flagged`/`pending`/`selected`.
/// Maps every `facet_id` (plane index) in `Design::planes_from_solved`'s output back
/// to the tier/orbit-member it came from (see this module's doc comment).
/// One (tier, index-on-gear) candidate plane, carrying its provenance through the
/// dedup pass.
/// Same tolerance `indicatrix::geometry::cuts::dedup_planes` uses (module doc, step 3).
const DEDUP_QUANTUM: f32 = 1.0 / 2048.0;