jellyflow-runtime 0.1.0

Headless store, rules, schema, profile, and change pipeline for Jellyflow.
Documentation
use super::*;

#[test]
fn viewport_pan_request_translates_screen_delta_by_zoom() {
    let current = ViewportTransform::new(CanvasPoint { x: 10.0, y: 20.0 }, 2.0).unwrap();

    let next = pan_viewport(
        current,
        ViewportPanRequest::new(CanvasPoint { x: 40.0, y: -10.0 }),
    )
    .expect("valid pan");

    assert_eq!(next.zoom, 2.0);
    assert_eq!(next.pan, CanvasPoint { x: 30.0, y: 15.0 });
}

#[test]
fn viewport_zoom_request_keeps_anchor_canvas_point_stable_and_clamps() {
    let current = ViewportTransform::new(CanvasPoint { x: 10.0, y: 20.0 }, 2.0).unwrap();

    let next = zoom_viewport(
        current,
        ViewportZoomRequest::new(CanvasPoint { x: 200.0, y: 120.0 }, 4.0, 0.5, 3.0),
    )
    .expect("valid zoom");

    assert_eq!(next.zoom, 3.0);
    assert!((next.pan.x - (-23.333332)).abs() <= 1.0e-5);
    assert!((next.pan.y - 0.0).abs() <= 1.0e-6);
    assert_eq!(
        current.canvas_point_at_screen(CanvasPoint { x: 200.0, y: 120.0 }),
        next.canvas_point_at_screen(CanvasPoint { x: 200.0, y: 120.0 }),
    );
}

#[test]
fn viewport_constraints_clamp_pan_to_translate_extent() {
    let transform = ViewportTransform::new(
        CanvasPoint {
            x: 400.0,
            y: -300.0,
        },
        1.0,
    )
    .unwrap();

    let constrained = constrain_viewport(
        transform,
        ViewportConstraints::with_translate_extent(
            CanvasSize {
                width: 50.0,
                height: 50.0,
            },
            CanvasRect {
                origin: CanvasPoint { x: 0.0, y: 0.0 },
                size: CanvasSize {
                    width: 100.0,
                    height: 100.0,
                },
            },
        ),
    )
    .expect("constrained viewport");

    assert_eq!(constrained.zoom, 1.0);
    assert_eq!(constrained.pan, CanvasPoint { x: 0.0, y: -50.0 });
}

#[test]
fn viewport_constraints_center_when_visible_area_exceeds_translate_extent() {
    let transform = ViewportTransform::new(CanvasPoint::default(), 0.5).unwrap();

    let constrained = constrain_viewport(
        transform,
        ViewportConstraints::with_translate_extent(
            CanvasSize {
                width: 100.0,
                height: 100.0,
            },
            CanvasRect {
                origin: CanvasPoint { x: 0.0, y: 0.0 },
                size: CanvasSize {
                    width: 80.0,
                    height: 60.0,
                },
            },
        ),
    )
    .expect("constrained viewport");

    assert_eq!(constrained.pan, CanvasPoint { x: 60.0, y: 70.0 },);
}

#[test]
fn viewport_transform_rejects_non_finite_or_non_positive_values() {
    let non_finite_pan = CanvasPoint {
        x: f32::NAN,
        y: 0.0,
    };
    assert!(ViewportTransform::new(non_finite_pan, 1.0).is_none());
    assert!(ViewportTransform::new(CanvasPoint { x: 0.0, y: 0.0 }, 0.0).is_none());

    let current = ViewportTransform::new(CanvasPoint::default(), 1.0).unwrap();
    assert!(
        pan_viewport(
            current,
            ViewportPanRequest::new(CanvasPoint {
                x: f32::INFINITY,
                y: 0.0,
            }),
        )
        .is_none()
    );
    assert!(
        zoom_viewport(
            current,
            ViewportZoomRequest::new(CanvasPoint::default(), f32::NAN, 0.1, 4.0),
        )
        .is_none()
    );

    let invalid_current = ViewportTransform {
        pan: CanvasPoint::default(),
        zoom: f32::INFINITY,
    };
    let no_delta = ViewportPanRequest::new(CanvasPoint::default());
    assert!(pan_viewport(invalid_current, no_delta).is_none());
}