Lightningbeam/lightningbeam-ui/lightningbeam-core/tests/rendering_integration_test.rs

321 lines
11 KiB
Rust

//! Integration tests for rendering scenarios
//!
//! Tests complex rendering scenarios including solo, mute, opacity cascading,
//! and clip instance rendering.
use lightningbeam_core::clip::{ClipInstance, VectorClip};
use lightningbeam_core::document::Document;
use lightningbeam_core::layer::{AnyLayer, LayerTrait, VectorLayer};
use lightningbeam_core::object::ShapeInstance;
use lightningbeam_core::renderer::{render_document, render_document_with_transform, ImageCache};
use lightningbeam_core::shape::{Shape, ShapeColor};
use vello::kurbo::{Affine, Circle, Shape as KurboShape};
use vello::Scene;
/// Create a test document with multiple layers containing shapes
fn setup_rendering_document() -> (Document, Vec<uuid::Uuid>) {
let mut document = Document::new("Test Project");
document.width = 800.0;
document.height = 600.0;
// Layer 1 with a red circle
let mut layer1 = VectorLayer::new("Red Layer");
let circle1 = Circle::new((100.0, 100.0), 50.0);
let shape1 = Shape::new(circle1.to_path(0.1)).with_fill(ShapeColor::rgb(255, 0, 0));
let instance1 = ShapeInstance::new(shape1.id);
layer1.add_shape(shape1);
layer1.add_object(instance1);
// Layer 2 with a green circle
let mut layer2 = VectorLayer::new("Green Layer");
let circle2 = Circle::new((200.0, 200.0), 50.0);
let shape2 = Shape::new(circle2.to_path(0.1)).with_fill(ShapeColor::rgb(0, 255, 0));
let instance2 = ShapeInstance::new(shape2.id);
layer2.add_shape(shape2);
layer2.add_object(instance2);
// Layer 3 with a blue circle
let mut layer3 = VectorLayer::new("Blue Layer");
let circle3 = Circle::new((300.0, 300.0), 50.0);
let shape3 = Shape::new(circle3.to_path(0.1)).with_fill(ShapeColor::rgb(0, 0, 255));
let instance3 = ShapeInstance::new(shape3.id);
layer3.add_shape(shape3);
layer3.add_object(instance3);
let id1 = document.root.add_child(AnyLayer::Vector(layer1));
let id2 = document.root.add_child(AnyLayer::Vector(layer2));
let id3 = document.root.add_child(AnyLayer::Vector(layer3));
(document, vec![id1, id2, id3])
}
#[test]
fn test_render_empty_document() {
let document = Document::new("Empty");
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
// Should not panic
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_document_with_shapes() {
let (document, _ids) = setup_rendering_document();
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
// Should render all 3 layers without error
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_with_transform() {
let (document, _ids) = setup_rendering_document();
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
// Render with zoom and pan
let transform = Affine::translate((100.0, 50.0)) * Affine::scale(2.0);
render_document_with_transform(&document, &mut scene, transform, &mut image_cache, &video_manager, None);
}
#[test]
fn test_render_solo_single_layer() {
let (mut document, ids) = setup_rendering_document();
// Solo layer 2 (green)
if let Some(layer) = document.root.get_child_mut(&ids[1]) {
layer.set_soloed(true);
}
// Count visible layers for rendering
let any_soloed = document.visible_layers().any(|l| l.soloed());
assert!(any_soloed);
let layers_to_render: Vec<_> = document
.visible_layers()
.filter(|l| l.soloed())
.collect();
assert_eq!(layers_to_render.len(), 1);
// Render should work
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_solo_multiple_layers() {
let (mut document, ids) = setup_rendering_document();
// Solo layers 1 and 3
if let Some(layer) = document.root.get_child_mut(&ids[0]) {
layer.set_soloed(true);
}
if let Some(layer) = document.root.get_child_mut(&ids[2]) {
layer.set_soloed(true);
}
// Two layers should render
let layers_to_render: Vec<_> = document
.visible_layers()
.filter(|l| l.soloed())
.collect();
assert_eq!(layers_to_render.len(), 2);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_hidden_layer_not_rendered() {
let (mut document, ids) = setup_rendering_document();
// Hide layer 2
if let Some(layer) = document.root.get_child_mut(&ids[1]) {
layer.set_visible(false);
}
// Only 2 visible layers
assert_eq!(document.visible_layers().count(), 2);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_with_layer_opacity() {
let (mut document, ids) = setup_rendering_document();
// Set different opacities
if let Some(layer) = document.root.get_child_mut(&ids[0]) {
layer.set_opacity(0.5);
}
if let Some(layer) = document.root.get_child_mut(&ids[1]) {
layer.set_opacity(0.25);
}
if let Some(layer) = document.root.get_child_mut(&ids[2]) {
layer.set_opacity(1.0);
}
// Verify opacities
assert_eq!(document.root.get_child(&ids[0]).unwrap().opacity(), 0.5);
assert_eq!(document.root.get_child(&ids[1]).unwrap().opacity(), 0.25);
assert_eq!(document.root.get_child(&ids[2]).unwrap().opacity(), 1.0);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_with_clip_instances() {
let mut document = Document::new("Test");
// Create a vector clip
let mut clip_layer = VectorLayer::new("Clip Content");
let circle = Circle::new((50.0, 50.0), 25.0);
let shape = Shape::new(circle.to_path(0.1)).with_fill(ShapeColor::rgb(255, 255, 0));
let instance = ShapeInstance::new(shape.id);
clip_layer.add_shape(shape);
clip_layer.add_object(instance);
let mut vector_clip = VectorClip::new("Yellow Circle Clip", 5.0, 100.0, 100.0);
vector_clip.layers.roots.push(lightningbeam_core::layer_tree::LayerNode::new(
AnyLayer::Vector(clip_layer),
));
let clip_id = vector_clip.id;
document.vector_clips.insert(clip_id, vector_clip);
// Create a layer with a clip instance
let mut layer = VectorLayer::new("Main Layer");
let mut clip_instance = ClipInstance::new(clip_id);
clip_instance.timeline_start = 0.0;
clip_instance.transform.x = 100.0;
clip_instance.transform.y = 100.0;
layer.clip_instances.push(clip_instance);
document.root.add_child(AnyLayer::Vector(layer));
// Set time within clip range
document.set_time(2.0);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_clip_instance_outside_time_range() {
let mut document = Document::new("Test");
// Create a vector clip
let vector_clip = VectorClip::new("Test Clip", 5.0, 100.0, 100.0);
let clip_id = vector_clip.id;
document.vector_clips.insert(clip_id, vector_clip);
// Create clip instance starting at time 10.0
let mut layer = VectorLayer::new("Main Layer");
let mut clip_instance = ClipInstance::new(clip_id);
clip_instance.timeline_start = 10.0;
layer.clip_instances.push(clip_instance);
document.root.add_child(AnyLayer::Vector(layer));
// Set time before clip starts
document.set_time(5.0);
// Clip shouldn't render (it hasn't started yet)
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_all_layers_hidden() {
let (mut document, ids) = setup_rendering_document();
// Hide all layers
for id in &ids {
if let Some(layer) = document.root.get_child_mut(id) {
layer.set_visible(false);
}
}
// No visible layers
assert_eq!(document.visible_layers().count(), 0);
// Should still render (just background)
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_solo_hidden_layer_interaction() {
let (mut document, ids) = setup_rendering_document();
// Hide and solo layer 1
if let Some(layer) = document.root.get_child_mut(&ids[0]) {
layer.set_visible(false);
layer.set_soloed(true);
}
// Layer 1 is hidden, so not in visible_layers()
// The solo flag on a hidden layer doesn't affect rendering
let visible_soloed: Vec<_> = document
.visible_layers()
.filter(|l| l.soloed())
.collect();
// No visible layer is soloed
assert_eq!(visible_soloed.len(), 0);
// All 2 visible layers should render (layers 2 and 3)
assert_eq!(document.visible_layers().count(), 2);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_background_color() {
let mut document = Document::new("Test");
document.background_color = ShapeColor::rgb(128, 128, 128);
let mut scene = Scene::new();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
#[test]
fn test_render_at_different_times() {
let (mut document, _ids) = setup_rendering_document();
let mut image_cache = ImageCache::new();
let video_manager = std::sync::Arc::new(std::sync::Mutex::new(lightningbeam_core::video::VideoManager::new()));
// Render at different times
for time in [0.0, 0.5, 1.0, 2.5, 5.0, 10.0] {
document.set_time(time);
let mut scene = Scene::new();
render_document(&document, &mut scene, &mut image_cache, &video_manager);
}
}