Files
hcie-rust-v3.05/hcie-egui-app/crates/hcie-gui-egui/tests/canvas_engine.rs
T
2026-07-09 02:59:53 +03:00

301 lines
7.7 KiB
Rust

use egui::Widget;
use hcie_engine_api::Engine;
use hcie_engine_api::{Tool, ZOOM_MAX, ZOOM_MIN};
use hcie_gui_egui::app::{AppDocument, SelectionTransform, ToolState, TransformHandle};
use hcie_gui_egui::canvas::CanvasWidget;
use hcie_gui_egui::event_bus::EventBus;
fn build_canvas() -> (
AppDocument,
ToolState,
EventBus,
Option<egui::TextureHandle>,
) {
(
AppDocument::new("Canvas", 100, 100),
ToolState::default(),
EventBus::new(),
None,
)
}
#[test]
fn canvas_widget_creation() {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
fn test_zoom(zoom: f32) {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
doc.zoom = zoom;
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn canvas_widget_zoom_0_1() {
test_zoom(0.1);
}
#[test]
fn canvas_widget_zoom_0_5() {
test_zoom(0.5);
}
#[test]
fn canvas_widget_zoom_1_0() {
test_zoom(1.0);
}
#[test]
fn canvas_widget_zoom_2_0() {
test_zoom(2.0);
}
#[test]
fn canvas_widget_zoom_4_0() {
test_zoom(4.0);
}
#[test]
fn canvas_widget_zoom_8_0() {
test_zoom(8.0);
}
#[test]
fn canvas_widget_zoom_min() {
test_zoom(ZOOM_MIN);
}
#[test]
fn canvas_widget_zoom_max() {
test_zoom(ZOOM_MAX);
}
fn test_tool(tool: Tool) {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
state.active_tool = tool;
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn canvas_widget_pen() {
test_tool(Tool::Pen);
}
#[test]
fn canvas_widget_brush() {
test_tool(Tool::Brush);
}
#[test]
fn canvas_widget_eraser() {
test_tool(Tool::Eraser);
}
#[test]
fn canvas_widget_rect() {
test_tool(Tool::VectorRect);
}
#[test]
fn canvas_widget_line() {
test_tool(Tool::VectorLine);
}
#[test]
fn canvas_widget_select() {
test_tool(Tool::Select);
}
#[test]
fn canvas_widget_lasso() {
test_tool(Tool::Lasso);
}
#[test]
fn canvas_widget_magic_wand() {
test_tool(Tool::MagicWand);
}
#[test]
fn canvas_widget_eyedropper() {
test_tool(Tool::Eyedropper);
}
#[test]
fn canvas_widget_flood_fill() {
test_tool(Tool::FloodFill);
}
#[test]
fn canvas_widget_gradient() {
test_tool(Tool::Gradient);
}
#[test]
fn canvas_widget_move() {
test_tool(Tool::Move);
}
#[test]
fn canvas_widget_crop() {
test_tool(Tool::Crop);
}
#[test]
fn canvas_widget_with_pan() {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
state.pan_offset = egui::Vec2::new(50.0, -30.0);
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn canvas_widget_with_selection() {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
state.selection_rect = Some([10, 10, 50, 50]);
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn canvas_widget_lasso_with_points() {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
state.active_tool = Tool::Lasso;
state.lasso_points = vec![(0, 0), (10, 0), (10, 10), (0, 10)];
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn canvas_widget_text_active() {
egui::__run_test_ui(|ui| {
let (mut doc, mut state, mut bus, mut tex) = build_canvas();
state.active_tool = Tool::Text;
state.text_edit.active = true;
state.text_edit.canvas_x = 50;
state.text_edit.canvas_y = 50;
state.text_edit.draft_text = "Test".into();
let canvas = CanvasWidget::new(&mut doc, &mut state, &mut bus, &mut tex, 0, 0);
canvas.ui(ui);
});
}
#[test]
fn app_document_zoom_roundtrip() {
let mut doc = AppDocument::new("ZoomTest", 200, 200);
let zooms = [
0.1, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0,
];
for &z in &zooms {
doc.zoom = z;
assert!((doc.zoom - z).abs() < 0.001);
}
}
#[test]
fn engine_composite_after_draw() {
let mut doc = AppDocument::new("EngineDraw", 20, 20);
doc.engine
.draw_filled_rect_rgba(5, 5, 10, 10, [0, 255, 0, 255]);
let pixels = doc.engine.get_composite_pixels();
assert_eq!(pixels.len(), 20 * 20 * 4);
let idx = (5 * 20 + 5) * 4;
assert_eq!(pixels[idx], 0);
assert_eq!(pixels[idx + 1], 255);
assert_eq!(pixels[idx + 2], 0);
assert_eq!(pixels[idx + 3], 255);
}
#[test]
fn engine_multi_layer_composite() {
let mut doc = AppDocument::new("MultiLayer", 10, 10);
let id = doc.engine.add_layer("top");
doc.engine.set_active_layer(id);
doc.engine
.draw_filled_rect_rgba(0, 0, 10, 10, [255, 0, 0, 255]);
let pixels = doc.engine.get_composite_pixels();
assert_eq!(pixels[0], 255);
}
#[test]
fn transform_handle_variants() {
assert!(matches!(TransformHandle::None, _));
assert!(matches!(TransformHandle::TopLeft, _));
assert!(matches!(TransformHandle::TopRight, _));
assert!(matches!(TransformHandle::BottomLeft, _));
assert!(matches!(TransformHandle::BottomRight, _));
assert!(matches!(TransformHandle::Top, _));
assert!(matches!(TransformHandle::Bottom, _));
assert!(matches!(TransformHandle::Left, _));
assert!(matches!(TransformHandle::Right, _));
assert!(matches!(TransformHandle::Move, _));
}
#[test]
fn selection_transform_from_engine_empty_mask() {
let engine = Engine::new(10, 10);
let st = SelectionTransform::from_engine(&engine, &[], 10, 10);
assert!(st.pixels.is_empty());
assert_eq!(st.width, 0);
assert_eq!(st.height, 0);
}
#[test]
fn zoom_clamp_values() {
let values = [-1.0, 0.0, 0.005, ZOOM_MIN, 0.5, 1.0, 2.0, ZOOM_MAX, 100.0];
for &v in &values {
let clamped = v.clamp(ZOOM_MIN, ZOOM_MAX);
assert!(
clamped >= ZOOM_MIN && clamped <= ZOOM_MAX,
"{} clamped to {} should be in [{}, {}]",
v,
clamped,
ZOOM_MIN,
ZOOM_MAX
);
}
}
#[test]
fn event_bus_events_dispatched_to_engine() {
let mut doc = AppDocument::new("EventEngine", 10, 10);
doc.engine.draw_filled_rect_rgba(0, 0, 10, 10, [100; 4]);
let before = doc.engine.get_composite_pixels()[0];
doc.engine.undo();
let after = doc.engine.get_composite_pixels()[0];
assert_ne!(before, after);
}
#[test]
fn test_text_layer_rotation() {
let mut doc = AppDocument::new("TextRotate", 200, 200);
let font_path = "../../assets/fonts/Inter.ttf";
if let Ok(bytes) = std::fs::read(font_path) {
let name = "Inter.ttf";
doc.engine.load_font(name, &bytes).unwrap();
let id = doc.engine.add_text(
"Rotated",
name,
24.0,
100.0,
100.0,
[255, 255, 255, 255],
45.0,
hcie_engine_api::TextAlignment::Left,
hcie_engine_api::TextOrientation::Horizontal,
&[],
);
assert_ne!(id, 0);
let pixels = doc.engine.get_composite_pixels();
let mut count = 0;
for i in 0..pixels.len() / 4 {
if pixels[i * 4 + 3] > 0 {
count += 1;
}
}
assert!(
count > 0,
"No rotated text pixels were rasterized onto the canvas!"
);
} else {
panic!("Failed to read Inter.ttf from {}", font_path);
}
}