1pub use iridia::Partitioning;
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub enum OutputPrimitive {
37 Point,
39 Line,
41 TriangleCw,
43 TriangleCcw,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq)]
50pub struct Factors {
51 pub edges: [f32; 4],
53 pub inside: [f32; 2],
55}
56
57#[derive(Clone, Debug, Default, PartialEq)]
61pub struct Tessellation {
62 pub points: Vec<[f32; 2]>,
64 pub indices: Vec<u32>,
66}
67
68pub fn tessellate_triangle(
70 partitioning: Partitioning,
71 output: OutputPrimitive,
72 factors: Factors,
73) -> Tessellation {
74 let mut t = Tessellator::new(partitioning, output);
75 t.triangle(
76 factors.edges[0],
77 factors.edges[1],
78 factors.edges[2],
79 factors.inside[0],
80 );
81 t.finish()
82}
83
84pub fn tessellate_quad(
86 partitioning: Partitioning,
87 output: OutputPrimitive,
88 factors: Factors,
89) -> Tessellation {
90 let mut t = Tessellator::new(partitioning, output);
91 t.quad(
92 factors.edges[0],
93 factors.edges[1],
94 factors.edges[2],
95 factors.edges[3],
96 factors.inside[0],
97 factors.inside[1],
98 );
99 t.finish()
100}
101
102pub fn tessellate_isolines(
104 partitioning: Partitioning,
105 output: OutputPrimitive,
106 density: f32,
107 detail: f32,
108) -> Tessellation {
109 let mut t = Tessellator::new(partitioning, output);
110 t.isolines(density, detail);
111 t.finish()
112}
113
114const MIN_ODD: f32 = 1.0;
115const MAX_ODD: f32 = 63.0;
116const MIN_EVEN: f32 = 2.0;
117const MAX_EVEN: f32 = 64.0;
118const MIN_ISOLINE_DENSITY: f32 = 1.0;
119const MAX_ISOLINE_DENSITY: f32 = 64.0;
120const EPSILON: f32 = 1.0 / 65536.0;
122const MAX_FACTOR: usize = 64;
123const MAX_POINTS: usize = (MAX_FACTOR + 1) * (MAX_FACTOR + 1);
124const MAX_INDICES: usize = MAX_FACTOR * MAX_FACTOR * 2 * 3;
125
126type Fxp = u32;
128const FRACTION_BITS: u32 = 16;
129const FRACTION_MASK: Fxp = 0x0000_ffff;
130const INTEGER_MASK: Fxp = 0x7fff_0000;
131const FXP_ONE: Fxp = 1 << FRACTION_BITS;
132const FXP_ONE_THIRD: Fxp = 0x0000_5555;
133const FXP_TWO_THIRDS: Fxp = 0x0000_aaaa;
134const FXP_ONE_HALF: Fxp = 0x0000_8000;
135const FIXED_RECIPROCAL: [Fxp; MAX_FACTOR + 1] = [
137 0xffff_ffff,
138 0x10000,
139 0x8000,
140 0x5555,
141 0x4000,
142 0x3333,
143 0x2aab,
144 0x2492,
145 0x2000,
146 0x1c72,
147 0x199a,
148 0x1746,
149 0x1555,
150 0x13b1,
151 0x1249,
152 0x1111,
153 0x1000,
154 0xf0f,
155 0xe39,
156 0xd79,
157 0xccd,
158 0xc31,
159 0xba3,
160 0xb21,
161 0xaab,
162 0xa3d,
163 0x9d9,
164 0x97b,
165 0x925,
166 0x8d4,
167 0x889,
168 0x842,
169 0x800,
170 0x7c2,
171 0x788,
172 0x750,
173 0x71c,
174 0x6eb,
175 0x6bd,
176 0x690,
177 0x666,
178 0x63e,
179 0x618,
180 0x5f4,
181 0x5d1,
182 0x5b0,
183 0x591,
184 0x572,
185 0x555,
186 0x539,
187 0x51f,
188 0x505,
189 0x4ec,
190 0x4d5,
191 0x4be,
192 0x4a8,
193 0x492,
194 0x47e,
195 0x46a,
196 0x457,
197 0x444,
198 0x432,
199 0x421,
200 0x410,
201 0x400,
202];
203
204fn is_nan(a: f32) -> bool {
206 a.is_nan()
207}
208fn flush(a: f32) -> f32 {
209 if a.to_bits() & 0x7fff_ffff < 0x0080_0000 {
210 f32::from_bits(a.to_bits() & 0x8000_0000)
211 } else {
212 a
213 }
214}
215fn culls(factor: f32) -> bool {
217 factor.is_nan() || factor <= 0.0
218}
219fn fmin(a: f32, b: f32) -> f32 {
221 let (fa, fb) = (flush(a), flush(b));
222 if is_nan(fb) {
223 a
224 } else if fa == 0.0 && fb == 0.0 {
225 if fa.to_bits() & 0x8000_0000 != 0 { a } else { b }
226 } else if fa < fb {
227 a
228 } else {
229 b
230 }
231}
232fn fmax(a: f32, b: f32) -> f32 {
234 let (fa, fb) = (flush(a), flush(b));
235 if is_nan(fb) {
236 a
237 } else if fa == 0.0 && fb == 0.0 {
238 if fb.to_bits() & 0x8000_0000 != 0 { a } else { b }
239 } else if fa >= fb {
240 a
241 } else {
242 b
243 }
244}
245
246#[allow(clippy::arithmetic_side_effects)] fn float_to_fixed(input: f32) -> Fxp {
249 const BIAS: i32 = 127;
250 const HIDDEN: i32 = 1 << 23;
251 const MANTISSA_MASK: i32 = HIDDEN - 1;
252 const EXPONENT_MASK: i32 = 0xff << 23;
253 const SIGN: i32 = i32::MIN;
254 const MAX_POSITIVE: i32 = (BIAS + 15) << 23;
257 let mut output = input.to_bits() as i32;
258 let unbiased = ((output & EXPONENT_MASK) >> 23) - BIAS;
259 let negative = output & SIGN != 0;
260 if unbiased == BIAS + 1 && output & MANTISSA_MASK != 0 {
261 output = 0;
262 } else if !negative && output >= MAX_POSITIVE {
263 output = -1;
264 } else if negative {
265 output = 0;
267 } else if unbiased < -(FRACTION_BITS as i32) - 1 {
268 output = 0;
269 } else {
270 output = (output & MANTISSA_MASK) | HIDDEN;
271 let extra = 23 - FRACTION_BITS as i32 - unbiased;
272 if extra >= 0 {
273 let lsb = 1i32 << extra;
274 let extra_mask = lsb - 1;
275 let half = lsb >> 1;
276 if output & lsb != 0 || (output & extra_mask) > half {
277 output += half;
278 }
279 output >>= extra;
280 } else {
281 output <<= -extra;
282 }
283 }
284 output as Fxp
285}
286
287fn fixed_to_float(input: Fxp) -> f32 {
288 (input >> FRACTION_BITS) as f32 + (input & FRACTION_MASK) as f32 / (1u32 << FRACTION_BITS) as f32
289}
290
291#[allow(clippy::cast_possible_truncation)] fn is_even(input: f32) -> bool {
293 (input as i32) & 1 == 0
294}
295
296fn fxp_ceil(input: Fxp) -> Fxp {
297 if input & FRACTION_MASK != 0 {
298 (input & INTEGER_MASK).wrapping_add(FXP_ONE)
299 } else {
300 input
301 }
302}
303
304fn fxp_floor(input: Fxp) -> Fxp {
305 input & INTEGER_MASK
306}
307
308#[allow(clippy::arithmetic_side_effects)] fn remove_msb(val: i32) -> i32 {
311 if val <= 0 {
312 0
313 } else {
314 val & !(1 << (31 - val.leading_zeros()))
315 }
316}
317
318#[derive(Clone, Copy, Debug, PartialEq, Eq)]
319enum Parity {
320 Even,
321 Odd,
322}
323
324#[derive(Clone, Copy, Debug, Default)]
325struct FactorContext {
326 inv_segments_on_floor: Fxp,
327 inv_segments_on_ceil: Fxp,
328 half_fraction: Fxp,
329 num_half_points: i32,
330 split_point_on_floor: i32,
331}
332
333#[derive(Clone, Copy)]
335struct Row {
336 base: i32,
337 half_points: i32,
338 parity: Parity,
339}
340
341#[derive(Clone, Copy, Debug, PartialEq, Eq)]
342enum Diagonals {
343 InsideToOutside,
344 InsideToOutsideExceptMiddle,
345 Mirrored,
346}
347
348#[derive(Clone, Copy, Debug, Default)]
349struct IndexPatch {
350 inside_delta: i32,
351 inside_bad: i32,
352 inside_replacement: i32,
353 outside_base: i32,
354 outside_delta: i32,
355 outside_bad: i32,
356 outside_replacement: i32,
357}
358
359#[derive(Clone, Copy, Debug, Default)]
360struct IndexPatch2 {
361 base_to_invert: i32,
362 inversion_end: i32,
363 corner_bad: i32,
364 corner_replacement: i32,
365}
366
367struct Tessellator {
368 original_parity: Parity,
369 parity: Parity,
370 original_partitioning: Partitioning,
371 partitioning: Partitioning,
372 output: OutputPrimitive,
373 points: Vec<[f32; 2]>,
374 index: Vec<i32>,
375 num_points: i32,
376 num_indices: i32,
377 using_patch: bool,
378 using_patch2: bool,
379 patch: IndexPatch,
380 patch2: IndexPatch2,
381}
382
383struct Quad {
384 outside: [Fxp; 4],
385 inside: [Fxp; 2],
386 outside_parity: [Parity; 4],
387 inside_parity: [Parity; 2],
388 outside_ctx: [FactorContext; 4],
389 inside_ctx: [FactorContext; 2],
390 just_minimum: bool,
391 culled: bool,
392 points_for_outside_edge: [i32; 4],
393 points_for_inside: [i32; 2],
394 inside_edge_point_base: i32,
395}
396
397struct Tri {
398 outside: [Fxp; 3],
399 inside: Fxp,
400 outside_parity: [Parity; 3],
401 inside_parity: Parity,
402 outside_ctx: [FactorContext; 3],
403 inside_ctx: FactorContext,
404 just_minimum: bool,
405 culled: bool,
406 points_for_outside_edge: [i32; 3],
407 points_for_inside: i32,
408 inside_edge_point_base: i32,
409}
410
411struct Isolines {
412 line_density_parity: Parity,
413 line_detail_parity: Parity,
414 line_density_ctx: FactorContext,
415 line_detail_ctx: FactorContext,
416 culled: bool,
417 points_per_line: i32,
418 lines: i32,
419}
420
421impl Tessellator {
422 fn new(partitioning: Partitioning, output: OutputPrimitive) -> Self {
423 let parity = match partitioning {
424 Partitioning::FractionalOdd => Parity::Odd,
425 _ => Parity::Even,
427 };
428 Self {
429 original_parity: parity,
430 parity,
431 original_partitioning: partitioning,
432 partitioning,
433 output,
434 points: vec![[0.0; 2]; MAX_POINTS],
435 index: vec![0; MAX_INDICES],
436 num_points: 0,
437 num_indices: 0,
438 using_patch: false,
439 using_patch2: false,
440 patch: IndexPatch::default(),
441 patch2: IndexPatch2::default(),
442 }
443 }
444
445 #[allow(clippy::indexing_slicing)] fn finish(mut self) -> Tessellation {
447 self.points.truncate(self.num_points.max(0) as usize);
448 Tessellation {
449 points: self.points,
450 indices: self.index[..self.num_indices.max(0) as usize]
451 .iter()
452 .map(|&i| i as u32)
453 .collect(),
454 }
455 }
456
457 fn odd(&self) -> bool {
458 self.parity == Parity::Odd
459 }
460
461 fn hw_integer(&self) -> bool {
463 matches!(self.partitioning, Partitioning::Integer | Partitioning::Pow2)
464 }
465
466 fn bounds(&self) -> (f32, f32) {
468 match self.original_partitioning {
469 Partitioning::Integer | Partitioning::Pow2 => (MIN_ODD, MAX_EVEN),
470 Partitioning::FractionalEven => (MIN_EVEN, MAX_EVEN),
471 Partitioning::FractionalOdd => (MIN_ODD, MAX_ODD),
472 }
473 }
474
475 #[allow(clippy::indexing_slicing)] fn define_point(&mut self, u: Fxp, v: Fxp, at: i32) {
477 self.points[at as usize] = [fixed_to_float(u), fixed_to_float(v)];
478 }
479
480 #[allow(clippy::indexing_slicing)] fn define_index(&mut self, index: i32, at: i32) {
482 self.index[at as usize] = self.patch_index_value(index);
483 }
484
485 #[allow(clippy::arithmetic_side_effects)] fn define_clockwise_triangle(&mut self, i0: i32, i1: i32, i2: i32, at: i32) {
488 self.define_index(i0, at);
489 if self.output == OutputPrimitive::TriangleCw {
490 self.define_index(i1, at + 1);
491 self.define_index(i2, at + 2);
492 } else {
493 self.define_index(i2, at + 1);
494 self.define_index(i1, at + 2);
495 }
496 }
497
498 #[allow(clippy::arithmetic_side_effects)] fn dump_all_points(&mut self) {
500 for p in 0..self.num_points {
501 let at = self.num_indices;
502 self.num_indices += 1;
503 self.define_index(p, at);
504 }
505 }
506
507 #[allow(clippy::arithmetic_side_effects)] fn dump_all_points_as_in_order_line_list(&mut self) {
509 for p in 1..self.num_points {
510 for index in [p - 1, p] {
511 let at = self.num_indices;
512 self.num_indices += 1;
513 self.define_index(index, at);
514 }
515 }
516 }
517
518 #[allow(clippy::arithmetic_side_effects)] fn num_points_for_factor(&self, fxp: Fxp) -> i32 {
520 if self.odd() {
521 (fxp_ceil(FXP_ONE_HALF.wrapping_add(fxp.wrapping_add(1) / 2)).wrapping_mul(2) >> FRACTION_BITS)
522 as i32
523 } else {
524 ((fxp_ceil(fxp.wrapping_add(1) / 2).wrapping_mul(2) >> FRACTION_BITS) + 1) as i32
525 }
526 }
527
528 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn factor_context(&self, fxp: Fxp) -> FactorContext {
530 let mut half = fxp.wrapping_add(1) / 2;
531 if self.odd() || half == FXP_ONE_HALF {
532 half = half.wrapping_add(FXP_ONE_HALF);
534 }
535 let floor_half = fxp_floor(half);
536 let ceil_half = fxp_ceil(half);
537 let half_fraction = half - floor_half;
538 let num_half_points = (ceil_half >> FRACTION_BITS) as i32;
539 let split_point_on_floor = if ceil_half == floor_half {
540 num_half_points + 1
542 } else if self.odd() {
543 if floor_half == FXP_ONE {
544 0
545 } else {
546 (remove_msb(((floor_half >> FRACTION_BITS) as i32) - 1) << 1) + 1
547 }
548 } else {
549 (remove_msb((floor_half >> FRACTION_BITS) as i32) << 1) + 1
550 };
551 let mut floor_segments = ((floor_half.wrapping_mul(2)) >> FRACTION_BITS) as usize;
552 let mut ceil_segments = ((ceil_half.wrapping_mul(2)) >> FRACTION_BITS) as usize;
553 if self.odd() {
554 floor_segments -= 1;
555 ceil_segments -= 1;
556 }
557 FactorContext {
558 inv_segments_on_floor: FIXED_RECIPROCAL[floor_segments],
559 inv_segments_on_ceil: FIXED_RECIPROCAL[ceil_segments],
560 half_fraction,
561 num_half_points,
562 split_point_on_floor,
563 }
564 }
565
566 #[allow(clippy::arithmetic_side_effects)] fn place_point_in_1d(&self, ctx: &FactorContext, point: i32) -> Fxp {
570 let mut point = point;
571 let flip = point >= ctx.num_half_points;
572 if flip {
573 point = (ctx.num_half_points << 1) - point;
574 if self.odd() {
575 point -= 1;
576 }
577 }
578 if point == ctx.num_half_points {
579 return FXP_ONE_HALF;
580 }
581 let on_ceil = point as u32;
582 let mut on_floor = on_ceil;
583 if point > ctx.split_point_on_floor {
584 on_floor = on_floor.wrapping_sub(1);
585 }
586 let location_on_floor = on_floor.wrapping_mul(ctx.inv_segments_on_floor);
587 let location_on_ceil = on_ceil.wrapping_mul(ctx.inv_segments_on_ceil);
588 let mut location = location_on_floor
589 .wrapping_mul(FXP_ONE.wrapping_sub(ctx.half_fraction))
590 .wrapping_add(location_on_ceil.wrapping_mul(ctx.half_fraction));
591 location = location.wrapping_add(FXP_ONE_HALF) >> FRACTION_BITS;
592 if flip {
593 location = FXP_ONE.wrapping_sub(location);
594 }
595 location
596 }
597
598 #[allow(clippy::arithmetic_side_effects)] fn patch_index_value(&self, mut index: i32) -> i32 {
600 if self.using_patch {
601 let p = &self.patch;
602 if index >= p.outside_base {
603 if index == p.outside_bad {
604 index = p.outside_replacement;
605 } else {
606 index += p.outside_delta;
607 }
608 } else if index == p.inside_bad {
609 index = p.inside_replacement;
610 } else {
611 index += p.inside_delta;
612 }
613 } else if self.using_patch2 {
614 let p = &self.patch2;
615 if index >= p.base_to_invert {
616 if index == p.corner_bad {
617 index = p.corner_replacement;
618 } else {
619 index = p.inversion_end - index;
620 }
621 } else if index == p.corner_bad {
622 index = p.corner_replacement;
623 }
624 }
625 index
626 }
627
628 #[allow(clippy::arithmetic_side_effects)] fn stitch_regular(
632 &mut self,
633 trapezoid: bool,
634 diagonals: Diagonals,
635 mut base: i32,
636 inside_points: i32,
637 inside_base: i32,
638 outside_base: i32,
639 ) {
640 let mut inside = inside_base;
641 let mut outside = outside_base;
642 if trapezoid {
643 self.define_clockwise_triangle(outside, outside + 1, inside, base);
644 base += 3;
645 outside += 1;
646 }
647 let mut p;
648 match diagonals {
649 Diagonals::InsideToOutside => {
650 for _ in 0..inside_points - 1 {
651 self.define_clockwise_triangle(inside, outside, outside + 1, base);
652 base += 3;
653 self.define_clockwise_triangle(inside, outside + 1, inside + 1, base);
654 base += 3;
655 inside += 1;
656 outside += 1;
657 }
658 }
659 Diagonals::InsideToOutsideExceptMiddle => {
660 p = 0;
662 while p < inside_points / 2 - 1 {
663 self.define_clockwise_triangle(outside, outside + 1, inside, base);
664 base += 3;
665 self.define_clockwise_triangle(inside, outside + 1, inside + 1, base);
666 base += 3;
667 inside += 1;
668 outside += 1;
669 p += 1;
670 }
671 self.define_clockwise_triangle(outside, inside + 1, inside, base);
673 base += 3;
674 self.define_clockwise_triangle(outside, outside + 1, inside + 1, base);
675 base += 3;
676 inside += 1;
677 outside += 1;
678 p += 2;
679 while p < inside_points {
681 self.define_clockwise_triangle(outside, outside + 1, inside, base);
682 base += 3;
683 self.define_clockwise_triangle(inside, outside + 1, inside + 1, base);
684 base += 3;
685 inside += 1;
686 outside += 1;
687 p += 1;
688 }
689 }
690 Diagonals::Mirrored => {
691 p = 0;
693 while p < inside_points / 2 {
694 self.define_clockwise_triangle(outside, inside + 1, inside, base);
695 base += 3;
696 self.define_clockwise_triangle(outside, outside + 1, inside + 1, base);
697 base += 3;
698 inside += 1;
699 outside += 1;
700 p += 1;
701 }
702 while p < inside_points - 1 {
704 self.define_clockwise_triangle(inside, outside, outside + 1, base);
705 base += 3;
706 self.define_clockwise_triangle(inside, outside + 1, inside + 1, base);
707 base += 3;
708 inside += 1;
709 outside += 1;
710 p += 1;
711 }
712 }
713 }
714 if trapezoid {
715 self.define_clockwise_triangle(outside, outside + 1, inside, base);
716 }
717 }
718
719 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn stitch_transition(&mut self, mut base: i32, inside: Row, outside: Row) {
723 const FINAL_POINT_POSITION: [i32; 33] = [
725 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23, 1, 24, 12, 25, 6, 26, 13, 27, 3,
726 28, 14, 29, 7, 30, 15, 31,
727 ];
728 const LOOP_START: [i32; 33] = [
730 1, 1, 17, 9, 9, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
731 2,
732 ];
733 const LOOP_END: [i32; 33] = [
734 0, 0, 17, 17, 25, 25, 25, 25, 29, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 31, 31, 31,
735 31, 31, 31, 31, 31, 31, 31, 32,
736 ];
737 let (inside_parity, outside_parity) = (inside.parity, outside.parity);
738 let inside_half_points = inside.half_points - i32::from(inside_parity == Parity::Odd);
739 let outside_half_points = outside.half_points - i32::from(outside_parity == Parity::Odd);
740 let mut outside = outside.base;
741 let mut inside = inside.base;
742 let start = LOOP_START[inside_half_points as usize].min(LOOP_START[outside_half_points as usize]);
743 let end = LOOP_END[inside_half_points as usize].max(LOOP_END[outside_half_points as usize]);
744
745 if FINAL_POINT_POSITION[0] < outside_half_points {
747 self.define_clockwise_triangle(outside, outside + 1, inside, base);
749 base += 3;
750 outside += 1;
751 }
752 for i in start..=end {
753 if FINAL_POINT_POSITION[i as usize] < inside_half_points {
754 self.define_clockwise_triangle(inside, outside, inside + 1, base);
755 base += 3;
756 inside += 1;
757 }
758 if FINAL_POINT_POSITION[i as usize] < outside_half_points {
759 self.define_clockwise_triangle(outside, outside + 1, inside, base);
760 base += 3;
761 outside += 1;
762 }
763 }
764 if inside_parity != outside_parity || inside_parity == Parity::Odd {
765 if inside_parity == outside_parity {
766 self.define_clockwise_triangle(inside, outside, inside + 1, base);
768 base += 3;
769 self.define_clockwise_triangle(inside + 1, outside, outside + 1, base);
770 base += 3;
771 inside += 1;
772 outside += 1;
773 } else if inside_parity == Parity::Even {
774 self.define_clockwise_triangle(inside, outside, outside + 1, base);
776 base += 3;
777 outside += 1;
778 } else {
779 self.define_clockwise_triangle(inside, outside, inside + 1, base);
781 base += 3;
782 inside += 1;
783 }
784 }
785 for i in (start..=end).rev() {
787 if FINAL_POINT_POSITION[i as usize] < outside_half_points {
788 self.define_clockwise_triangle(outside, outside + 1, inside, base);
789 base += 3;
790 outside += 1;
791 }
792 if FINAL_POINT_POSITION[i as usize] < inside_half_points {
793 self.define_clockwise_triangle(inside, outside, inside + 1, base);
794 base += 3;
795 inside += 1;
796 }
797 }
798 if FINAL_POINT_POSITION[0] < outside_half_points {
799 self.define_clockwise_triangle(outside, outside + 1, inside, base);
800 }
801 }
802
803 fn quad(&mut self, u0: f32, v0: f32, u1: f32, v1: f32, inside_u: f32, inside_v: f32) {
806 let q = self.quad_process(u0, v0, u1, v1, inside_u, inside_v);
807 if q.culled {
808 self.num_points = 0;
809 self.num_indices = 0;
810 return;
811 }
812 if q.just_minimum {
813 self.define_point(0, 0, 0);
814 self.define_point(FXP_ONE, 0, 1);
815 self.define_point(FXP_ONE, FXP_ONE, 2);
816 self.define_point(0, FXP_ONE, 3);
817 self.num_points = 4;
818 match self.output {
819 OutputPrimitive::TriangleCw | OutputPrimitive::TriangleCcw => {
820 self.define_clockwise_triangle(0, 1, 3, 0);
821 self.define_clockwise_triangle(1, 2, 3, 3);
822 self.num_indices = 6;
823 }
824 OutputPrimitive::Point => self.dump_all_points(),
825 OutputPrimitive::Line => self.dump_all_points_as_in_order_line_list(),
826 }
827 return;
828 }
829 self.quad_generate_points(&q);
830 match self.output {
831 OutputPrimitive::Point => self.dump_all_points(),
832 OutputPrimitive::Line => self.dump_all_points_as_in_order_line_list(),
833 _ => self.quad_generate_connectivity(&q),
834 }
835 }
836
837 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn quad_process(
839 &mut self,
840 mut u0: f32,
841 mut v0: f32,
842 mut u1: f32,
843 mut v1: f32,
844 mut in_u: f32,
845 mut in_v: f32,
846 ) -> Quad {
847 let mut q = Quad {
848 outside: [0; 4],
849 inside: [0; 2],
850 outside_parity: [Parity::Even; 4],
851 inside_parity: [Parity::Even; 2],
852 outside_ctx: [FactorContext::default(); 4],
853 inside_ctx: [FactorContext::default(); 2],
854 just_minimum: false,
855 culled: false,
856 points_for_outside_edge: [0; 4],
857 points_for_inside: [0; 2],
858 inside_edge_point_base: 0,
859 };
860 if culls(u0) || culls(v0) || culls(u1) || culls(v1) {
862 q.culled = true;
863 return q;
864 }
865 let (mut lower, upper) = self.bounds();
866 u0 = fmin(upper, fmax(lower, u0));
867 v0 = fmin(upper, fmax(lower, v0));
868 u1 = fmin(upper, fmax(lower, u1));
869 v1 = fmin(upper, fmax(lower, v1));
870 if self.hw_integer() {
871 u0 = u0.ceil();
872 v0 = v0.ceil();
873 u1 = u1.ceil();
874 v1 = v1.ceil();
875 }
876 if self.original_partitioning == Partitioning::FractionalOdd {
877 let limit = MIN_ODD + EPSILON / 2.0;
878 if u0 > limit || v0 > limit || u1 > limit || v1 > limit || in_u > limit || in_v > limit {
880 lower = MIN_ODD + EPSILON;
881 }
882 }
883 in_u = fmin(upper, fmax(lower, in_u));
884 in_v = fmin(upper, fmax(lower, in_v));
885 if self.hw_integer() {
886 in_u = in_u.ceil();
887 in_v = in_v.ceil();
888 }
889 self.num_points = 0;
890 self.num_indices = 0;
891 let outside = [u0, v0, u1, v1];
892 let inside = [in_u, in_v];
893 if self.hw_integer() {
894 q.outside_parity = outside.map(|f| if is_even(f) { Parity::Even } else { Parity::Odd });
895 q.inside_parity = inside.map(|f| {
896 if is_even(f) || f == 1.0 {
897 Parity::Even
898 } else {
899 Parity::Odd
900 }
901 });
902 } else {
903 q.outside_parity = [self.original_parity; 4];
904 q.inside_parity = [self.original_parity; 2];
905 }
906 q.outside = outside.map(float_to_fixed);
907 q.inside = inside.map(float_to_fixed);
908 if self.hw_integer() || self.odd() {
909 if q.inside.iter().chain(&q.outside).all(|&f| f == FXP_ONE) {
911 q.just_minimum = true;
912 return q;
913 }
914 }
915 q.just_minimum = false;
916 for e in 0..4 {
917 self.parity = q.outside_parity[e];
918 q.outside_ctx[e] = self.factor_context(q.outside[e]);
919 }
920 for a in 0..2 {
921 self.parity = q.inside_parity[a];
922 q.inside_ctx[a] = self.factor_context(q.inside[a]);
923 }
924 for e in 0..4 {
925 self.parity = q.outside_parity[e];
926 q.points_for_outside_edge[e] = self.num_points_for_factor(q.outside[e]);
927 self.num_points += q.points_for_outside_edge[e];
928 }
929 self.num_points -= 4;
930 for a in 0..2 {
931 self.parity = q.inside_parity[a];
932 q.points_for_inside[a] = self.num_points_for_factor(q.inside[a]);
933 let min = if q.inside_parity[a] == Parity::Odd { 4 } else { 3 };
934 q.points_for_inside[a] = min.max(q.points_for_inside[a]);
936 }
937 q.inside_edge_point_base = self.num_points;
938 self.num_points += (q.points_for_inside[0] - 2) * (q.points_for_inside[1] - 2);
939 q
940 }
941
942 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn quad_generate_points(&mut self, q: &Quad) {
944 let mut offset = 0;
946 for edge in 0..4usize {
947 let parity = edge & 1;
948 let end = q.points_for_outside_edge[edge] - 1;
949 for p in 0..end {
950 let n = if edge == 1 || edge == 2 { p } else { end - p };
951 self.parity = q.outside_parity[edge];
952 let param = self.place_point_in_1d(&q.outside_ctx[edge], n);
953 if parity != 0 {
954 self.define_point(param, if edge == 3 { FXP_ONE } else { 0 }, offset);
955 } else {
956 self.define_point(if edge == 2 { FXP_ONE } else { 0 }, param, offset);
957 }
958 offset += 1;
959 }
960 }
961 let min_points = q.points_for_inside[0].min(q.points_for_inside[1]);
963 let rings = min_points >> 1; for ring in 1..rings {
965 let start = ring;
966 let end = [
967 q.points_for_inside[0] - 1 - start,
968 q.points_for_inside[1] - 1 - start,
969 ];
970 for edge in 0..4usize {
971 let parity = [edge & 1, (edge + 1) & 1];
972 let perpendicular_point = if edge < 2 { start } else { end[parity[0]] };
973 self.parity = q.inside_parity[parity[0]];
974 let perpendicular = self.place_point_in_1d(&q.inside_ctx[parity[0]], perpendicular_point);
975 self.parity = q.inside_parity[parity[1]];
976 for p in start..end[parity[1]] {
977 let n = if edge == 1 || edge == 2 {
978 p
979 } else {
980 end[parity[1]] - (p - start)
981 };
982 let param = self.place_point_in_1d(&q.inside_ctx[parity[1]], n);
983 if parity[1] != 0 {
984 self.define_point(perpendicular, param, offset);
985 } else {
986 self.define_point(param, perpendicular, offset);
987 }
988 offset += 1;
989 }
990 }
991 }
992 if q.points_for_inside[0] > q.points_for_inside[1] && q.inside_parity[1] == Parity::Even {
994 let start = rings;
995 let end = q.points_for_inside[0] - 1 - start;
996 self.parity = q.inside_parity[0];
997 for p in start..=end {
998 let param = self.place_point_in_1d(&q.inside_ctx[0], p);
999 self.define_point(param, FXP_ONE_HALF, offset);
1000 offset += 1;
1001 }
1002 } else if q.points_for_inside[1] >= q.points_for_inside[0] && q.inside_parity[0] == Parity::Even {
1003 let start = rings;
1004 let end = q.points_for_inside[1] - 1 - start;
1005 self.parity = q.inside_parity[1];
1006 for p in (start..=end).rev() {
1007 let param = self.place_point_in_1d(&q.inside_ctx[1], p);
1008 self.define_point(FXP_ONE_HALF, param, offset);
1009 offset += 1;
1010 }
1011 }
1012 }
1013
1014 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn quad_generate_connectivity(&mut self, q: &Quad) {
1016 let rows_to_center = [
1018 (q.points_for_inside[0] + 1) >> 1,
1019 (q.points_for_inside[1] + 1) >> 1,
1020 ];
1021 let rings = rows_to_center[0].min(rows_to_center[1]);
1022 let degenerate_ring = [
1024 if q.inside_parity[1] == Parity::Even {
1025 rows_to_center[1] - 1
1026 } else {
1027 -1
1028 },
1029 if q.inside_parity[0] == Parity::Even {
1030 rows_to_center[0] - 1
1031 } else {
1032 -1
1033 },
1034 ];
1035 let mut outside_ctx = [
1036 q.outside_ctx[0],
1037 q.outside_ctx[1],
1038 q.outside_ctx[2],
1039 q.outside_ctx[3],
1040 ];
1041 let mut outside_parity = q.outside_parity;
1042 let mut points_for_outside_edge = q.points_for_outside_edge;
1043 let mut inside_edge_base = q.inside_edge_point_base;
1044 let mut outside_edge_base = 0;
1045 for ring in 1..rings {
1046 let points_for_inside_edge = [
1047 q.points_for_inside[0] - 2 * ring,
1048 q.points_for_inside[1] - 2 * ring,
1049 ];
1050 let edge0_inside_base = inside_edge_base;
1051 let edge0_outside_base = outside_edge_base;
1052 for edge in 0..4usize {
1053 let parity = (edge + 1) & 1;
1054 let triangles = points_for_inside_edge[parity] + points_for_outside_edge[edge] - 2;
1055 let inside_base;
1056 let outside_base;
1057 if edge == 3 {
1058 if ring == degenerate_ring[parity] {
1061 self.patch2.base_to_invert = inside_edge_base + 1;
1062 self.patch2.corner_bad = outside_edge_base + points_for_outside_edge[edge] - 1;
1063 self.patch2.corner_replacement = edge0_outside_base;
1064 self.patch2.inversion_end = (self.patch2.base_to_invert << 1) - 1;
1065 inside_base = self.patch2.base_to_invert;
1066 outside_base = outside_edge_base;
1067 self.using_patch2 = true;
1068 } else {
1069 self.patch.inside_delta = inside_edge_base;
1070 self.patch.inside_bad = points_for_inside_edge[parity] - 1;
1071 self.patch.inside_replacement = edge0_inside_base;
1072 self.patch.outside_base = self.patch.inside_bad + 1;
1073 self.patch.outside_delta = outside_edge_base - self.patch.outside_base;
1074 self.patch.outside_bad = self.patch.outside_base + points_for_outside_edge[edge] - 1;
1075 self.patch.outside_replacement = edge0_outside_base;
1076 inside_base = 0;
1077 outside_base = self.patch.outside_base;
1078 self.using_patch = true;
1079 }
1080 } else if edge == 2 && ring == degenerate_ring[parity] {
1081 self.patch2.base_to_invert = inside_edge_base;
1082 self.patch2.corner_bad = -1;
1083 self.patch2.corner_replacement = -1;
1084 self.patch2.inversion_end = self.patch2.base_to_invert << 1;
1085 inside_base = self.patch2.base_to_invert;
1086 outside_base = outside_edge_base;
1087 self.using_patch2 = true;
1088 } else {
1089 inside_base = inside_edge_base;
1090 outside_base = outside_edge_base;
1091 }
1092 if ring == 1 {
1093 self.stitch_transition(
1094 self.num_indices,
1095 Row {
1096 base: inside_base,
1097 half_points: q.inside_ctx[parity].num_half_points,
1098 parity: q.inside_parity[parity],
1099 },
1100 Row {
1101 base: outside_base,
1102 half_points: outside_ctx[edge].num_half_points,
1103 parity: outside_parity[edge],
1104 },
1105 );
1106 } else {
1107 self.stitch_regular(
1108 true,
1109 Diagonals::Mirrored,
1110 self.num_indices,
1111 points_for_inside_edge[parity],
1112 inside_base,
1113 outside_base,
1114 );
1115 }
1116 self.using_patch = false;
1117 self.using_patch2 = false;
1118 self.num_indices += triangles * 3;
1119 outside_edge_base += points_for_outside_edge[edge] - 1;
1120 if edge == 2 && ring == degenerate_ring[parity] {
1121 inside_edge_base -= points_for_inside_edge[parity] - 1;
1122 } else {
1123 inside_edge_base += points_for_inside_edge[parity] - 1;
1124 }
1125 points_for_outside_edge[edge] = points_for_inside_edge[parity];
1126 }
1127 if ring == 1 {
1128 for edge in 0..4usize {
1129 outside_ctx[edge] = q.inside_ctx[edge & 1];
1130 outside_parity[edge] = q.inside_parity[edge & 1];
1131 }
1132 }
1133 }
1134 if q.points_for_inside[0] > q.points_for_inside[1] && q.inside_parity[1] == Parity::Odd {
1136 self.using_patch2 = true;
1137 let strip_quads = (((q.points_for_inside[0] >> 1) - (q.points_for_inside[1] >> 1)) << 1)
1138 + if q.inside_parity[0] == Parity::Even { 2 } else { 1 };
1139 self.patch2.base_to_invert = outside_edge_base + strip_quads + 2;
1140 self.patch2.corner_bad = self.patch2.base_to_invert;
1141 self.patch2.corner_replacement = outside_edge_base;
1142 self.patch2.inversion_end = self.patch2.base_to_invert + self.patch2.base_to_invert + strip_quads;
1143 self.stitch_regular(
1144 false,
1145 Diagonals::InsideToOutside,
1146 self.num_indices,
1147 strip_quads + 1,
1148 self.patch2.base_to_invert,
1149 outside_edge_base + 1,
1150 );
1151 self.using_patch2 = false;
1152 self.num_indices += strip_quads * 6;
1153 } else if q.points_for_inside[1] >= q.points_for_inside[0] && q.inside_parity[0] == Parity::Odd {
1154 self.using_patch2 = true;
1155 let strip_quads = (((q.points_for_inside[1] >> 1) - (q.points_for_inside[0] >> 1)) << 1)
1156 + if q.inside_parity[1] == Parity::Even { 2 } else { 1 };
1157 self.patch2.base_to_invert = outside_edge_base + strip_quads + 1;
1158 self.patch2.corner_bad = -1;
1159 self.patch2.inversion_end = self.patch2.base_to_invert + self.patch2.base_to_invert + strip_quads;
1160 let diagonals = if q.inside_parity[1] == Parity::Even {
1161 Diagonals::InsideToOutside
1162 } else {
1163 Diagonals::InsideToOutsideExceptMiddle
1164 };
1165 self.stitch_regular(
1166 false,
1167 diagonals,
1168 self.num_indices,
1169 strip_quads + 1,
1170 self.patch2.base_to_invert,
1171 outside_edge_base,
1172 );
1173 self.using_patch2 = false;
1174 self.num_indices += strip_quads * 6;
1175 }
1176 }
1177
1178 fn triangle(&mut self, u0: f32, v0: f32, w0: f32, inside: f32) {
1181 let t = self.tri_process(u0, v0, w0, inside);
1182 if t.culled {
1183 self.num_points = 0;
1184 self.num_indices = 0;
1185 return;
1186 }
1187 if t.just_minimum {
1188 self.define_point(0, FXP_ONE, 0);
1189 self.define_point(0, 0, 1);
1190 self.define_point(FXP_ONE, 0, 2);
1191 self.num_points = 3;
1192 match self.output {
1193 OutputPrimitive::TriangleCw | OutputPrimitive::TriangleCcw => {
1194 self.define_clockwise_triangle(0, 1, 2, self.num_indices);
1195 self.num_indices = 3;
1196 }
1197 OutputPrimitive::Point => self.dump_all_points(),
1198 OutputPrimitive::Line => self.dump_all_points_as_in_order_line_list(),
1199 }
1200 return;
1201 }
1202 self.tri_generate_points(&t);
1203 match self.output {
1204 OutputPrimitive::Point => self.dump_all_points(),
1205 OutputPrimitive::Line => self.dump_all_points_as_in_order_line_list(),
1206 _ => self.tri_generate_connectivity(&t),
1207 }
1208 }
1209
1210 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn tri_process(&mut self, mut u0: f32, mut v0: f32, mut w0: f32, mut inside: f32) -> Tri {
1212 let mut t = Tri {
1213 outside: [0; 3],
1214 inside: 0,
1215 outside_parity: [Parity::Even; 3],
1216 inside_parity: Parity::Even,
1217 outside_ctx: [FactorContext::default(); 3],
1218 inside_ctx: FactorContext::default(),
1219 just_minimum: false,
1220 culled: false,
1221 points_for_outside_edge: [0; 3],
1222 points_for_inside: 0,
1223 inside_edge_point_base: 0,
1224 };
1225 if culls(u0) || culls(v0) || culls(w0) {
1226 t.culled = true;
1227 return t;
1228 }
1229 let (mut lower, upper) = self.bounds();
1230 u0 = fmin(upper, fmax(lower, u0));
1231 v0 = fmin(upper, fmax(lower, v0));
1232 w0 = fmin(upper, fmax(lower, w0));
1233 if self.hw_integer() {
1234 u0 = u0.ceil();
1235 v0 = v0.ceil();
1236 w0 = w0.ceil();
1237 }
1238 if self.original_partitioning == Partitioning::FractionalOdd {
1239 let limit = MIN_ODD + EPSILON / 2.0;
1240 if u0 > limit || v0 > limit || w0 > limit {
1242 lower = MIN_ODD + EPSILON;
1243 }
1244 }
1245 inside = fmin(upper, fmax(lower, inside));
1246 if self.hw_integer() {
1247 inside = inside.ceil();
1248 }
1249 self.num_points = 0;
1250 self.num_indices = 0;
1251 let outside = [u0, v0, w0];
1252 if self.hw_integer() {
1253 t.outside_parity = outside.map(|f| if is_even(f) { Parity::Even } else { Parity::Odd });
1254 t.inside_parity = if is_even(inside) || inside == 1.0 {
1255 Parity::Even
1256 } else {
1257 Parity::Odd
1258 };
1259 } else {
1260 t.outside_parity = [self.original_parity; 3];
1261 t.inside_parity = self.original_parity;
1262 }
1263 t.outside = outside.map(float_to_fixed);
1264 t.inside = float_to_fixed(inside);
1265 if (self.hw_integer() || self.odd()) && t.inside == FXP_ONE && t.outside.iter().all(|&f| f == FXP_ONE)
1266 {
1267 t.just_minimum = true;
1268 return t;
1269 }
1270 t.just_minimum = false;
1271 for e in 0..3 {
1272 self.parity = t.outside_parity[e];
1273 t.outside_ctx[e] = self.factor_context(t.outside[e]);
1274 }
1275 self.parity = t.inside_parity;
1276 t.inside_ctx = self.factor_context(t.inside);
1277 for e in 0..3 {
1278 self.parity = t.outside_parity[e];
1279 t.points_for_outside_edge[e] = self.num_points_for_factor(t.outside[e]);
1280 self.num_points += t.points_for_outside_edge[e];
1281 }
1282 self.num_points -= 3;
1283 self.parity = t.inside_parity;
1284 t.points_for_inside = self.num_points_for_factor(t.inside);
1285 let min = if self.odd() { 4 } else { 3 };
1286 t.points_for_inside = min.max(t.points_for_inside);
1287 t.inside_edge_point_base = self.num_points;
1288 let interior_rings = (t.points_for_inside >> 1) - 1;
1289 let interior_points = if self.odd() {
1290 3 * (interior_rings * (interior_rings + 1) - interior_rings)
1291 } else {
1292 3 * (interior_rings * (interior_rings + 1)) + 1
1293 };
1294 self.num_points += interior_points;
1295 t
1296 }
1297
1298 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn tri_generate_points(&mut self, t: &Tri) {
1300 let mut offset = 0;
1302 for edge in 0..3usize {
1303 let parity = edge & 1;
1304 let end = t.points_for_outside_edge[edge] - 1;
1305 for p in 0..end {
1306 let n = if parity != 0 { p } else { end - p };
1308 self.parity = t.outside_parity[edge];
1309 let param = self.place_point_in_1d(&t.outside_ctx[edge], n);
1310 if edge == 0 {
1311 self.define_point(0, param, offset);
1312 } else {
1313 self.define_point(
1314 param,
1315 if edge == 2 { FXP_ONE.wrapping_sub(param) } else { 0 },
1316 offset,
1317 );
1318 }
1319 offset += 1;
1320 }
1321 }
1322 self.parity = t.inside_parity;
1324 let rings = t.points_for_inside >> 1;
1325 for ring in 1..rings {
1326 let start = ring;
1327 let end = t.points_for_inside - 1 - start;
1328 for edge in 0..3usize {
1329 let parity = edge & 1;
1330 let mut perpendicular = self.place_point_in_1d(&t.inside_ctx, start);
1331 perpendicular = perpendicular.wrapping_mul(FXP_TWO_THIRDS);
1333 perpendicular = perpendicular.wrapping_add(FXP_ONE_HALF) >> FRACTION_BITS;
1334 for p in start..end {
1335 let n = if parity != 0 { p } else { end - (p - start) };
1336 let param = self.place_point_in_1d(&t.inside_ctx, n);
1337 let pushed = param.wrapping_sub(perpendicular.wrapping_add(1) / 2);
1339 match edge {
1340 0 => self.define_point(perpendicular, pushed, offset),
1341 1 => self.define_point(pushed, perpendicular, offset),
1342 _ => self.define_point(
1343 pushed,
1344 FXP_ONE.wrapping_sub(pushed).wrapping_sub(perpendicular),
1345 offset,
1346 ),
1347 }
1348 offset += 1;
1349 }
1350 }
1351 }
1352 if !self.odd() {
1353 self.define_point(FXP_ONE_THIRD, FXP_ONE_THIRD, offset);
1355 }
1356 }
1357
1358 #[allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)] fn tri_generate_connectivity(&mut self, t: &Tri) {
1360 let rings = (t.points_for_inside + 1) >> 1; let mut outside_ctx = t.outside_ctx;
1362 let mut outside_parity = t.outside_parity;
1363 let mut points_for_outside_edge = t.points_for_outside_edge;
1364 let mut inside_edge_base = t.inside_edge_point_base;
1365 let mut outside_edge_base = 0;
1366 for ring in 1..rings {
1367 let points_for_inside_edge = t.points_for_inside - 2 * ring;
1368 let edge0_inside_base = inside_edge_base;
1369 let edge0_outside_base = outside_edge_base;
1370 for edge in 0..3usize {
1371 let triangles = points_for_inside_edge + points_for_outside_edge[edge] - 2;
1372 let (inside_base, outside_base) = if edge == 2 {
1373 self.patch.inside_delta = inside_edge_base;
1374 self.patch.inside_bad = points_for_inside_edge - 1;
1375 self.patch.inside_replacement = edge0_inside_base;
1376 self.patch.outside_base = self.patch.inside_bad + 1;
1377 self.patch.outside_delta = outside_edge_base - self.patch.outside_base;
1378 self.patch.outside_bad = self.patch.outside_base + points_for_outside_edge[edge] - 1;
1379 self.patch.outside_replacement = edge0_outside_base;
1380 self.using_patch = true;
1381 (0, self.patch.outside_base)
1382 } else {
1383 (inside_edge_base, outside_edge_base)
1384 };
1385 if ring == 1 {
1386 self.stitch_transition(
1387 self.num_indices,
1388 Row {
1389 base: inside_base,
1390 half_points: t.inside_ctx.num_half_points,
1391 parity: t.inside_parity,
1392 },
1393 Row {
1394 base: outside_base,
1395 half_points: outside_ctx[edge].num_half_points,
1396 parity: outside_parity[edge],
1397 },
1398 );
1399 } else {
1400 self.stitch_regular(
1401 true,
1402 Diagonals::Mirrored,
1403 self.num_indices,
1404 points_for_inside_edge,
1405 inside_base,
1406 outside_base,
1407 );
1408 }
1409 if edge == 2 {
1410 self.using_patch = false;
1411 }
1412 self.num_indices += triangles * 3;
1413 outside_edge_base += points_for_outside_edge[edge] - 1;
1414 inside_edge_base += points_for_inside_edge - 1;
1415 points_for_outside_edge[edge] = points_for_inside_edge;
1416 }
1417 if ring == 1 {
1418 for edge in 0..3usize {
1419 outside_ctx[edge] = t.inside_ctx;
1420 outside_parity[edge] = t.inside_parity;
1421 }
1422 }
1423 }
1424 if self.odd() {
1425 self.define_clockwise_triangle(
1427 outside_edge_base,
1428 outside_edge_base + 1,
1429 outside_edge_base + 2,
1430 self.num_indices,
1431 );
1432 self.num_indices += 3;
1433 }
1434 }
1435
1436 #[allow(clippy::arithmetic_side_effects)] fn isolines(&mut self, density: f32, detail: f32) {
1440 let l = self.isoline_process(density, detail);
1441 if l.culled {
1442 self.num_points = 0;
1443 self.num_indices = 0;
1444 return;
1445 }
1446 let mut offset = 0;
1448 for line in 0..l.lines {
1449 for point in 0..l.points_per_line {
1450 self.parity = l.line_density_parity;
1451 let v = self.place_point_in_1d(&l.line_density_ctx, line);
1452 self.parity = l.line_detail_parity;
1453 let u = self.place_point_in_1d(&l.line_detail_ctx, point);
1454 self.define_point(u, v, offset);
1455 offset += 1;
1456 }
1457 }
1458 let (mut point_offset, mut index_offset) = (0, 0);
1460 if self.output == OutputPrimitive::Point {
1461 for _ in 0..l.lines {
1462 for _ in 0..l.points_per_line {
1463 self.define_index(point_offset, index_offset);
1464 point_offset += 1;
1465 index_offset += 1;
1466 }
1467 }
1468 } else {
1469 for _ in 0..l.lines {
1470 for point in 0..l.points_per_line {
1471 if point > 0 {
1472 self.define_index(point_offset - 1, index_offset);
1473 index_offset += 1;
1474 self.define_index(point_offset, index_offset);
1475 index_offset += 1;
1476 }
1477 point_offset += 1;
1478 }
1479 }
1480 }
1481 }
1482
1483 #[allow(clippy::arithmetic_side_effects)] fn isoline_process(&mut self, mut density: f32, mut detail: f32) -> Isolines {
1485 let mut l = Isolines {
1486 line_density_parity: Parity::Even,
1487 line_detail_parity: Parity::Even,
1488 line_density_ctx: FactorContext::default(),
1489 line_detail_ctx: FactorContext::default(),
1490 culled: false,
1491 points_per_line: 0,
1492 lines: 0,
1493 };
1494 if culls(density) || culls(detail) {
1495 l.culled = true;
1496 return l;
1497 }
1498 let (lower, upper) = self.bounds();
1499 density = fmin(MAX_ISOLINE_DENSITY, fmax(MIN_ISOLINE_DENSITY, density));
1500 detail = fmin(upper, fmax(lower, detail));
1501 self.num_points = 0;
1502 self.num_indices = 0;
1503 if self.hw_integer() {
1504 detail = detail.ceil();
1505 l.line_detail_parity = if is_even(detail) {
1506 Parity::Even
1507 } else {
1508 Parity::Odd
1509 };
1510 } else {
1511 l.line_detail_parity = self.original_parity;
1512 }
1513 let fxp_detail = float_to_fixed(detail);
1514 self.parity = l.line_detail_parity;
1515 l.line_detail_ctx = self.factor_context(fxp_detail);
1516 l.points_per_line = self.num_points_for_factor(fxp_detail);
1517 self.partitioning = Partitioning::Integer;
1519 density = density.ceil();
1520 l.line_density_parity = if is_even(density) {
1521 Parity::Even
1522 } else {
1523 Parity::Odd
1524 };
1525 self.parity = l.line_density_parity;
1526 let fxp_density = float_to_fixed(density);
1527 l.line_density_ctx = self.factor_context(fxp_density);
1528 l.lines = self.num_points_for_factor(fxp_density) - 1;
1530 self.partitioning = self.original_partitioning;
1531 self.num_points = l.points_per_line * l.lines;
1532 self.num_indices = if self.output == OutputPrimitive::Point {
1533 self.num_points
1534 } else {
1535 l.lines * (l.points_per_line - 1) * 2
1536 };
1537 l
1538 }
1539}