1use crate::basics::iround;
7
8pub const LINE_SUBPIXEL_SHIFT: i32 = 8;
13pub const LINE_SUBPIXEL_SCALE: i32 = 1 << LINE_SUBPIXEL_SHIFT; pub const LINE_SUBPIXEL_MASK: i32 = LINE_SUBPIXEL_SCALE - 1; pub const LINE_MAX_COORD: i32 = (1 << 28) - 1;
16pub const LINE_MAX_LENGTH: i32 = 1 << 18; pub const LINE_MR_SUBPIXEL_SHIFT: i32 = 4;
19pub const LINE_MR_SUBPIXEL_SCALE: i32 = 1 << LINE_MR_SUBPIXEL_SHIFT; pub const LINE_MR_SUBPIXEL_MASK: i32 = LINE_MR_SUBPIXEL_SCALE - 1; #[inline]
24pub fn line_mr(x: i32) -> i32 {
25 x >> (LINE_SUBPIXEL_SHIFT - LINE_MR_SUBPIXEL_SHIFT)
26}
27
28#[inline]
30pub fn line_hr(x: i32) -> i32 {
31 x << (LINE_SUBPIXEL_SHIFT - LINE_MR_SUBPIXEL_SHIFT)
32}
33
34#[inline]
36pub fn line_dbl_hr(x: i32) -> i32 {
37 x << LINE_SUBPIXEL_SHIFT
38}
39
40#[inline]
46pub fn line_coord(x: f64) -> i32 {
47 iround(x * LINE_SUBPIXEL_SCALE as f64)
48}
49
50#[inline]
52pub fn line_coord_sat(x: f64) -> i32 {
53 let v = iround(x * LINE_SUBPIXEL_SCALE as f64);
54 v.max(-LINE_MAX_COORD).min(LINE_MAX_COORD)
55}
56
57const S_ORTHOGONAL_QUADRANT: [u8; 8] = [0, 0, 1, 1, 3, 3, 2, 2];
63
64const S_DIAGONAL_QUADRANT: [u8; 8] = [0, 1, 2, 1, 0, 3, 2, 3];
66
67#[derive(Debug, Clone, Copy)]
71pub struct LineParameters {
72 pub x1: i32,
73 pub y1: i32,
74 pub x2: i32,
75 pub y2: i32,
76 pub dx: i32,
77 pub dy: i32,
78 pub sx: i32,
79 pub sy: i32,
80 pub vertical: bool,
81 pub inc: i32,
82 pub len: i32,
83 pub octant: usize,
84}
85
86impl LineParameters {
87 pub fn new(x1: i32, y1: i32, x2: i32, y2: i32, len: i32) -> Self {
88 let dx = (x2 - x1).abs();
89 let dy = (y2 - y1).abs();
90 let sx = if x2 > x1 { 1 } else { -1 };
91 let sy = if y2 > y1 { 1 } else { -1 };
92 let vertical = dy >= dx;
93 let inc = if vertical { sy } else { sx };
94 let octant = ((sy as u32 & 4) | (sx as u32 & 2) | (vertical as u32)) as usize;
96 Self {
97 x1, y1, x2, y2, dx, dy, sx, sy, vertical, inc, len, octant,
98 }
99 }
100
101 pub fn orthogonal_quadrant(&self) -> u8 {
102 S_ORTHOGONAL_QUADRANT[self.octant]
103 }
104
105 pub fn diagonal_quadrant(&self) -> u8 {
106 S_DIAGONAL_QUADRANT[self.octant]
107 }
108
109 pub fn same_orthogonal_quadrant(&self, other: &LineParameters) -> bool {
110 S_ORTHOGONAL_QUADRANT[self.octant] == S_ORTHOGONAL_QUADRANT[other.octant]
111 }
112
113 pub fn same_diagonal_quadrant(&self, other: &LineParameters) -> bool {
114 S_DIAGONAL_QUADRANT[self.octant] == S_DIAGONAL_QUADRANT[other.octant]
115 }
116
117 pub fn divide(&self) -> (LineParameters, LineParameters) {
119 let xmid = (self.x1 + self.x2) >> 1;
120 let ymid = (self.y1 + self.y2) >> 1;
121 let len2 = self.len >> 1;
122
123 let lp1 = LineParameters::new(self.x1, self.y1, xmid, ymid, len2);
124 let lp2 = LineParameters::new(xmid, ymid, self.x2, self.y2, len2);
125
126 (lp1, lp2)
127 }
128}
129
130pub fn bisectrix(l1: &LineParameters, l2: &LineParameters, x: &mut i32, y: &mut i32) {
138 let k = l2.len as f64 / l1.len as f64;
139 let mut tx = l2.x2 as f64 - (l2.x1 - l1.x1) as f64 * k;
140 let mut ty = l2.y2 as f64 - (l2.y1 - l1.y1) as f64 * k;
141
142 if ((l2.x2 - l2.x1) as f64 * (l2.y1 - l1.y1) as f64)
146 < ((l2.y2 - l2.y1) as f64 * (l2.x1 - l1.x1) as f64 + 100.0)
147 {
148 tx -= (tx - l2.x1 as f64) * 2.0;
149 ty -= (ty - l2.y1 as f64) * 2.0;
150 }
151
152 let dx = tx - l2.x1 as f64;
154 let dy = ty - l2.y1 as f64;
155 if (dx * dx + dy * dy).sqrt() as i32 >= LINE_SUBPIXEL_SCALE {
156 *x = iround(tx);
157 *y = iround(ty);
158 } else {
159 *x = (l2.x1 + l2.x1 + (l2.y1 - l1.y1) + (l2.y2 - l2.y1)) >> 1;
160 *y = (l2.y1 + l2.y1 - (l2.x1 - l1.x1) - (l2.x2 - l2.x1)) >> 1;
161 }
162}
163
164#[inline]
166pub fn fix_degenerate_bisectrix_start(lp: &LineParameters, x: &mut i32, y: &mut i32) {
167 let d = iround(
168 ((*x - lp.x2) as f64 * (lp.y2 - lp.y1) as f64
169 - (*y - lp.y2) as f64 * (lp.x2 - lp.x1) as f64)
170 / lp.len as f64,
171 );
172 if d < LINE_SUBPIXEL_SCALE / 2 {
173 *x = lp.x1 + (lp.y2 - lp.y1);
174 *y = lp.y1 - (lp.x2 - lp.x1);
175 }
176}
177
178#[inline]
180pub fn fix_degenerate_bisectrix_end(lp: &LineParameters, x: &mut i32, y: &mut i32) {
181 let d = iround(
182 ((*x - lp.x2) as f64 * (lp.y2 - lp.y1) as f64
183 - (*y - lp.y2) as f64 * (lp.x2 - lp.x1) as f64)
184 / lp.len as f64,
185 );
186 if d < LINE_SUBPIXEL_SCALE / 2 {
187 *x = lp.x2 + (lp.y2 - lp.y1);
188 *y = lp.y2 - (lp.x2 - lp.x1);
189 }
190}
191
192pub fn clip_line_segment(
197 x1: &mut i32,
198 y1: &mut i32,
199 x2: &mut i32,
200 y2: &mut i32,
201 clip: &crate::basics::RectI,
202) -> u32 {
203 let mut fx1 = *x1 as f64;
205 let mut fy1 = *y1 as f64;
206 let mut fx2 = *x2 as f64;
207 let mut fy2 = *y2 as f64;
208 let fclip = crate::basics::Rect::new(
209 clip.x1 as f64,
210 clip.y1 as f64,
211 clip.x2 as f64,
212 clip.y2 as f64,
213 );
214 let ret = crate::clip_liang_barsky::clip_line_segment_f64(
215 &mut fx1, &mut fy1, &mut fx2, &mut fy2, &fclip,
216 );
217 *x1 = iround(fx1);
218 *y1 = iround(fy1);
219 *x2 = iround(fx2);
220 *y2 = iround(fy2);
221 ret
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_constants() {
230 assert_eq!(LINE_SUBPIXEL_SCALE, 256);
231 assert_eq!(LINE_SUBPIXEL_MASK, 255);
232 assert_eq!(LINE_MR_SUBPIXEL_SCALE, 16);
233 }
234
235 #[test]
236 fn test_line_mr_hr() {
237 assert_eq!(line_mr(256), 16); assert_eq!(line_hr(16), 256); assert_eq!(line_dbl_hr(1), 256); }
241
242 #[test]
243 fn test_line_coord() {
244 assert_eq!(line_coord(1.0), 256);
245 assert_eq!(line_coord(0.5), 128);
246 assert_eq!(line_coord(0.0), 0);
247 }
248
249 #[test]
250 fn test_line_parameters_horizontal() {
251 let lp = LineParameters::new(0, 0, 1000, 0, 1000);
252 assert!(!lp.vertical);
253 assert_eq!(lp.dx, 1000);
254 assert_eq!(lp.dy, 0);
255 assert_eq!(lp.sx, 1);
256 assert_eq!(lp.sy, -1); assert_eq!(lp.inc, 1); }
259
260 #[test]
261 fn test_line_parameters_vertical() {
262 let lp = LineParameters::new(0, 0, 0, 1000, 1000);
263 assert!(lp.vertical);
264 assert_eq!(lp.dx, 0);
265 assert_eq!(lp.dy, 1000);
266 assert_eq!(lp.inc, 1); }
268
269 #[test]
270 fn test_line_parameters_divide() {
271 let lp = LineParameters::new(0, 0, 1000, 1000, 1414);
272 let (lp1, lp2) = lp.divide();
273 assert_eq!(lp1.x1, 0);
274 assert_eq!(lp1.y1, 0);
275 assert_eq!(lp1.x2, 500);
276 assert_eq!(lp1.y2, 500);
277 assert_eq!(lp2.x1, 500);
278 assert_eq!(lp2.y1, 500);
279 assert_eq!(lp2.x2, 1000);
280 assert_eq!(lp2.y2, 1000);
281 }
282
283 #[test]
284 fn test_quadrant_lookups() {
285 let lp = LineParameters::new(0, 0, 100, 100, 141);
286 assert_eq!(lp.octant, 1);
288 assert_eq!(lp.orthogonal_quadrant(), 0);
289 assert_eq!(lp.diagonal_quadrant(), 1);
290 }
291
292 #[test]
293 fn test_bisectrix_right_angle() {
294 let l1 = LineParameters::new(0, 0, 256, 0, 256);
296 let l2 = LineParameters::new(256, 0, 256, 256, 256);
297 let (mut x, mut y) = (0, 0);
298 bisectrix(&l1, &l2, &mut x, &mut y);
299 assert!(x > 256, "bisectrix x={x} should be > 256");
301 assert!(y < 0, "bisectrix y={y} should be < 0");
302 }
303
304 #[test]
305 fn test_clip_line_segment_visible() {
306 let clip = crate::basics::RectI::new(0, 0, 1000, 1000);
307 let (mut x1, mut y1, mut x2, mut y2) = (100, 100, 500, 500);
308 let ret = clip_line_segment(&mut x1, &mut y1, &mut x2, &mut y2, &clip);
309 assert_eq!(ret, 0); }
311
312 #[test]
313 fn test_clip_line_segment_clipped() {
314 let clip = crate::basics::RectI::new(100, 100, 400, 400);
315 let (mut x1, mut y1, mut x2, mut y2) = (0, 0, 500, 500);
316 let ret = clip_line_segment(&mut x1, &mut y1, &mut x2, &mut y2, &clip);
317 assert!(ret < 4); assert_ne!(ret, 0); }
320}