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
//! Ring-anchored frames: the twist in the glycan twister ribbon.
//!
//! A backbone ribbon is framed by parallel transport, which is deliberately
//! twist-free — a protein's guide atoms are points on a line and carry no plane
//! of their own, so any twist the ribbon showed would be invented.
//!
//! A sugar does carry a plane. Its ring is the body of the residue, and the
//! angle between one ring and the next is the glycosidic geometry itself. So
//! the twister ribbon is framed by those planes instead: the flat face is held
//! in each ring's plane, and the twist the viewer reads between two sugars is
//! the rotation the molecule actually makes, not a rendering artefact.
//!
//! Costs `O(samples)` and allocates nothing; frames are rewritten in place.
use ;
/// Samples per radian of twist, as an extra demand for the adaptive sampler.
///
/// Seven degrees per quad is about where a flat face stops reading as a fold,
/// and a radian is a little over eight of those.
const SAMPLES_PER_RADIAN: f32 = 8.2;
/// Shortest projection of a ring normal onto the plane perpendicular to the
/// tangent that is still a direction rather than rounding error.
const MINIMUM_PROJECTION_SQ: f32 = 1.0e-8;
/// Re-frames sampled points so the ribbon's flat face lies in the ring plane
/// interpolated across each control-point interval.
///
/// Samples whose interval has no usable plane — a collapsed ring, or a ring
/// whose normal runs along the curve so that no flat face is determined — keep
/// the parallel-transport frame they arrived with, so an unorientable sugar
/// degrades to the twist-free ribbon rather than to a degenerate one.
pub
/// Builds the frame whose thickness axis is the blended ring plane.
///
/// Both ring normals are taken into the cross-section plane — the plane
/// perpendicular to the tangent — *before* they are blended, not after. Blending
/// the raw normals and projecting the result lets that result swing towards the
/// tangent direction partway along an interval: the ring plane then says nothing
/// about which way the flat face looks, the surviving perpendicular component is
/// mostly rounding error, and the frame sweeps through most of a right angle in
/// a single sample. On the ribbon that draws as a hard crease, which a reader
/// takes for a fold in the molecule.
///
/// Once both planes are in that one plane, the turn between them is an angle
/// about the tangent, so the blend is a rotation by that angle rather than a
/// lerp of two directions. A lerp crowds most of a wide turn into the middle of
/// the interval, which is exactly where a ribbon facets; rotating spreads it
/// evenly, and it stays defined even for a half turn, where a lerp passes
/// through zero.
///
/// The rotation is eased rather than linear: a constant rate is only C0 across a
/// control point, so the twist rate jumps at every ring. Easing brings the rate
/// to zero at both ends of each interval.
///
/// The width axis then follows from the house convention that the thickness axis
/// is the tangent crossed into the width axis.
/// One ring plane taken into the cross-section plane at this sample.
///
/// A ring whose normal runs along the curve determines no flat face at all, and
/// what is left of it after projection is rounding error, so it reports nothing
/// rather than a direction.
/// Extra per-interval sample demand from how far the ribbon has to turn.
///
/// The adaptive sampler measures midpoint deviation and tangent turn — the
/// shape of the curve — and a glycan is nearly straight between ring centres
/// while its planes rotate through most of a half turn. Left to the shape
/// alone the trace earns one or two samples per sugar and has to do that
/// rotation inside a single quad, which folds. Reporting the rotation buys
/// density where the ribbon actually twists instead of along its whole length,
/// which is the difference between a uniformly dense trace and one that costs
/// what it needs.
///
/// The turn is measured against the chord rather than the true tangent, which
/// is not known yet: the samples it comes from are what this decides. Over one
/// glycosidic link the two differ by little, and the sampler rounds the answer
/// up to a power of two regardless.
pub
/// Rotation about one interval's chord between two ring planes, in radians.