cranpose-ui-graphics 0.1.170

Pure math/data for drawing & units in Cranpose
Documentation
use super::*;
use crate::{DrawScope, DrawScopeDefault, Size};

#[test]
fn wide_arcs_are_banded_by_sweep_and_radius_and_narrow_ones_stay_quads() {
    let mut scope = DrawScopeDefault::new(Size::new(400.0, 400.0));
    let brush = Brush::Solid(Color::WHITE);
    let center = Point::new(200.0, 200.0);
    scope.draw_annular_sector(brush.clone(), center, 4.0, 8.0, 0.0, 1.0);
    scope.draw_annular_sector(brush.clone(), center, 10.0, 20.0, 0.0, 1.0);
    scope.draw_arc(brush.clone(), center, 50.0, 0.0, 1.0, Stroke::new(3.0));
    scope.draw_arc(brush.clone(), center, 200.0, 0.0, 1.0, Stroke::new(3.0));
    scope.draw_arc(brush.clone(), center, 500.0, 0.0, 1.0, Stroke::new(3.0));
    scope.draw_annular_sector(brush.clone(), center, 0.0, 40.0, 0.0, 3.0);
    scope.draw_annular_sector(brush, center, 30.0, 40.0, 0.0, 0.05);
    let recording = scope.finish();
    let banded: Vec<bool> = recording
        .shapes()
        .iter()
        .map(|record| record.is_banded())
        .collect();
    assert_eq!(
        banded,
        [false, true, true, true, true, false, true],
        "a disc stays a quad: its strip would be the disc and more; a sliver's \
         strip beats the disc the quad path would draw"
    );
    let segments: Vec<u32> = recording
        .shapes()
        .iter()
        .map(|record| record.band_segments())
        .collect();
    assert_eq!(
        segments[1..5],
        [4, 4, 8, 16],
        "a band takes the segments its padded sweep needs at the ring step of its radius"
    );
    assert_eq!(
        segments[6], 2,
        "the wide band needs two segments to cover its padded sweep"
    );
    let classes: Vec<usize> = recording
        .shapes()
        .iter()
        .map(|record| record.band_class())
        .collect();
    assert_eq!(classes, [0, 2, 2, 3, 4, 0, 1]);
    let segment_classes: Vec<u8> = recording
        .tables()
        .segments
        .iter()
        .map(|segment| segment.band_class)
        .collect();
    assert_eq!(
        segment_classes,
        [4],
        "a few records of mixed classes share one segment at the largest \
         class, so one draw keeps record order"
    );
    assert_eq!(band_bucket(1), 0);
    assert_eq!(band_bucket(64), ARC_BUCKETS - 1);
}

#[test]
fn a_strip_pattern_stays_within_its_vertices_including_a_single_segment() {
    for segments in ARC_BUCKET_SEGMENTS {
        let indices: Vec<u32> = strip_index_pattern(segments).collect();
        assert_eq!(indices.len() as u32, strip_indices(segments));
        assert_eq!(
            indices.iter().max().copied(),
            Some(strip_vertices(segments) - 1)
        );
    }
    let ring = BandRing::new(20.0, 22.0, 0.0, 0.01);
    assert_eq!(ring.segments(), BAND_MIN_SEGMENTS);
    assert_eq!(band_bucket(BAND_MIN_SEGMENTS), 0);
}

#[test]
fn minimum_band_keeps_segment_boundaries_exact() {
    let mut values = vec![f32::NAN, -1.0, -0.0, 0.0, f32::MIN_POSITIVE];
    for segments in ARC_BUCKET_SEGMENTS {
        let boundary = segments as f32;
        values.extend([
            f32::from_bits(boundary.to_bits() - 1),
            boundary,
            f32::from_bits(boundary.to_bits() + 1),
        ]);
    }
    for range in values {
        let ring = BandRing {
            mid: 0.0,
            ring_half: 0.0,
            range_start: 0.0,
            range,
            segments_per_radian: 1.0,
        };
        let expected = (range.ceil() as u32)
            .max(BAND_MIN_SEGMENTS)
            .next_power_of_two()
            .min(ARC_BUCKET_SEGMENTS[ARC_BUCKETS - 1]);
        assert_eq!(ring.segments(), expected, "segment count at {range:?}");
    }
}

#[test]
fn a_segment_is_cut_where_its_largest_class_would_collapse_more_than_a_draw_is_worth() {
    let mut scope = DrawScopeDefault::new(Size::new(2000.0, 2000.0));
    let brush = Brush::Solid(Color::WHITE);
    let quad = Rect {
        x: 1.0,
        y: 1.0,
        width: 4.0,
        height: 4.0,
    };
    let rects = 40;
    for _ in 0..rects {
        scope.draw_rect_at(quad, brush.clone());
    }
    scope.draw_arc(
        brush.clone(),
        Point::new(500.0, 500.0),
        400.0,
        0.0,
        TAU,
        Stroke::new(3.0),
    );
    for _ in 0..rects {
        scope.draw_rect_at(quad, brush.clone());
    }
    let recording = scope.finish();
    let ring = recording.shapes().get(rects).unwrap();
    assert!(ring.is_banded());
    let ring_quads = ring.band_segments();
    assert!(ring_quads > 1);
    let segments: Vec<(u32, u8)> = recording
        .tables()
        .segments
        .iter()
        .map(|segment| (segment.count, segment.band_class))
        .collect();
    let after = SEGMENT_WASTE_QUADS / (ring_quads - 1);
    assert_eq!(
        segments,
        [
            (rects as u32, 0),
            (1 + after, ring.band_class() as u8),
            (rects as u32 - after, 0)
        ],
        "the ring would collapse {rects} quads times {} vertices each, more than a draw is \
         worth, so it opens a segment; the rects after it join until their own collapse \
         passes the budget",
        ring_quads - 1
    );
}

#[test]
fn a_stroked_circle_is_a_band_and_a_stroked_pill_is_not() {
    let mut scope = DrawScopeDefault::new(Size::new(400.0, 400.0));
    let brush = Brush::Solid(Color::WHITE);
    let square = Rect {
        x: 10.0,
        y: 10.0,
        width: 100.0,
        height: 100.0,
    };
    scope.draw_round_rect_at_stroked(
        square,
        brush.clone(),
        CornerRadii::uniform(50.0),
        Stroke::new(4.0),
    );
    scope.draw_round_rect_at_stroked(
        square,
        brush.clone(),
        CornerRadii::uniform(20.0),
        Stroke::new(4.0),
    );
    scope.draw_round_rect_at(square, brush.clone(), CornerRadii::uniform(50.0));
    scope.draw_round_rect_at_stroked(
        Rect {
            x: 10.0,
            y: 10.0,
            width: 100.0,
            height: 60.0,
        },
        brush,
        CornerRadii::uniform(30.0),
        Stroke::new(4.0),
    );
    let recording = scope.finish();
    let banded: Vec<bool> = recording
        .shapes()
        .iter()
        .map(|record| record.is_banded())
        .collect();
    assert_eq!(banded, [true, false, false, false]);
    let ring = recording.shapes().get(0).unwrap();
    assert_eq!(ring.kind(), RECORD_KIND_ROUND_RECT);
    assert_eq!(ring.fragment_kind(), FRAGMENT_KIND_STROKE);
    assert_eq!(ring.arc, [60.0, 60.0, 50.0, 48.0]);
    assert_eq!(ring.arc_band, [0.0, TAU, 48.0, 52.0]);
    assert_eq!(ring.band_segments(), 16);
    assert_eq!(ring.band_class(), 4);
}

#[test]
fn the_tables_are_shared_until_recorded_into_again() {
    let recording = CommandRecording::from_primitives(vec![DrawPrimitive::Rect {
        rect: Rect {
            x: 0.0,
            y: 0.0,
            width: 1.0,
            height: 1.0,
        },
        brush: Brush::Solid(Color::WHITE),
        stroke: None,
    }]);
    let held = Arc::clone(recording.shape_recorder());
    let recording = CommandRecorder::reusing(recording).finish();
    assert!(!Arc::ptr_eq(&held, recording.shape_recorder()));
    assert_eq!(held.tables().shapes.len(), 1);
    assert!(recording.is_empty());
    assert_ne!(held.tables(), CommandRecording::default().tables());
}