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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Making room for the transition cells, which is Lengyel's Equation 4.2.
//!
//! A transition cell occupies a slab of thickness `w` on the **low-resolution**
//! side of a block boundary. Something has to vacate that slab, and it is the
//! coarse block's own boundary cells — §4.4:
//!
//! > If adjacent blocks are rendered at different levels of detail … then regular
//! > cells in the block for the lower level of detail must be **scaled in one or
//! > more directions to make space for transition cells** … we store two positions
//! > for each vertex belonging to regular cells on the boundary of a
//! > low-resolution block, a **primary** position used when transition cells are
//! > not rendered, and a **secondary** position used when transition cells are
//! > rendered.
//!
//! # The formula, and why it is simpler than it looks
//!
//! Equation 4.2 is written in level-0 cell units, which is what makes the `2^−k`
//! appear:
//!
//! ```text
//! Δx = (1 − 2^−k·x)·w(k) if x < 2^k
//! 0 if 2^k ≤ x ≤ 2^k(s − 1)
//! (s − 1 − 2^−k·x)·w(k) if x > 2^k(s − 1)
//! ```
//!
//! Substituting `x = c·2^k`, where `c` is the coordinate in **this block's own
//! cells**, the level index cancels completely:
//!
//! ```text
//! Δ = (1 − c)·w if c < 1
//! 0 if 1 ≤ c ≤ s − 1
//! (s − 1 − c)·w if c > s − 1
//! ```
//!
//! So it is a linear taper across the first and last cell of the block, and
//! nothing in between moves. A vertex exactly on the boundary plane (`c = 0`)
//! moves by exactly `w`, which is precisely the displacement
//! [`TransitionCell`](super::cell::TransitionCell) gives its half-resolution
//! face — and that coincidence is what keeps the seam closed at a non-zero width.
//! `the_seam_stays_closed_at_a_real_width` is what checks it rather than trusting
//! the algebra. The high face gets the same exactness by being anchored on its
//! *own* plane — computed as `origin + h·s`, the extractors' corner expression —
//! rather than measured from the low one, whose recomputed `c` lands a last bit
//! off `s` at generic origins; `the_high_plane_vertex_moves_by_exactly_minus_w`
//! holds the searched fixture that distinguishes the two anchorings.
//!
//! # This is a post-pass, deliberately
//!
//! Lengyel stores two positions per boundary vertex because his vertex program
//! picks between them per frame. That is a renderer's concern: which of a block's
//! neighbours are coarser is not known when the block is meshed, and can change
//! while it is resident. So the core crate meshes the block once, and
//! [`inset_boundary`] applies the taper to a copy when a transition is actually
//! being rendered on some face — the primary mesh is the un-inset one, and the
//! secondary is this.
use crate::;
/// Which faces of a block have a transition rendered on them.
///
/// Bit `axis * 2 + side`, where side `0` is the low face and `1` the high one —
/// the same layout [`marching_cubes::table::face_bit`](crate::marching_cubes::table::face_bit)
/// uses, stated here because a transposition between the two would be invisible.
pub const
/// Every face. The case where all six neighbours are coarser.
pub const ALL_FACES: u8 = 0b0011_1111;
/// Apply Equation 4.2's taper to a block's mesh, in place.
///
/// `origin` is the world position of the block's minimum corner, `cells` is `s` —
/// its size in **its own** cells — and `cell_size` is its own spacing. `width` is
/// the transition width, in world units. `faces` selects which faces to make room
/// on; a face with no coarser neighbour must not be tapered, or the block will
/// pull away from a same-resolution neighbour and open a seam where there was
/// none.
///
/// Only positions move. Normals are left alone: the taper is a shear of at most
/// `w` over one cell and re-deriving normals from it would report the *shear's*
/// geometry rather than the field's. Anything wanting normals from the displaced
/// geometry should say so explicitly through
/// [`normals::recompute`](crate::normals::recompute).
///
/// # Errors
///
/// [`Error::InvalidCellSize`](crate::Error::InvalidCellSize) if `cell_size` is
/// not finite and positive, or if `width` is negative or not finite. A width of
/// zero is allowed and is a no-op — that is the configuration A-011b shipped, and
/// M-74 records what it costs.
///
/// [`Error::GridTooSmall`](crate::Error::GridTooSmall) if `cells` is less than
/// two. The taper needs a first cell and a last cell to be different cells; with
/// one, the two tapers would fight over the same vertices.