918f60c498
HeadlessRenderer (Feature headless) baut Instance/Adapter/Device ohne Surface (Vulkan, kein Display-Server); render_to_image rendert in eine Rgba8Unorm-Texture (non-sRGB wie ColorMode::Web) und liest mit 256-Byte- Row-Padding zurueck. Draw-Code unveraendert geteilt mit dem Fenster-Pfad. CLI-Bin render_png (Demo-Szene, 990x630); Demo additiv um 45-Grad- Schraffur, Tuerblatt und Schwenkbogen erweitert, damit das Golden alle Pfade abdeckt. Golden-Test mit eingechecktem Referenzbild: 0/623700 Pixel Abweichung, bit-exakt reproduzierbar; bei Abweichung Diff-Dump nach target/. Doku in docs/design/engine-headless.md.
145 lines
4.7 KiB
Rust
145 lines
4.7 KiB
Rust
// Geteilte Demo-Szene fuer die Fenster-Spikes (standalone winit-Bin UND der
|
|
// In-Tauri-Spike). EINE Quelle der Wahrheit, damit beide exakt dasselbe zeichnen
|
|
// und der Renderer/die Szene NICHT dupliziert wird.
|
|
//
|
|
// serde-only + GPU-frei: baut im Standard-Feature mit (kein `render`/`window`
|
|
// noetig), damit die Szene auch headless testbar bleibt.
|
|
|
|
use crate::tessellate::PX_PER_M;
|
|
use crate::types::{Arc, FillPolygon, Line, Outline, Polyline, Scene, Text, TextAlign, ViewBox};
|
|
|
|
/// Demo-Szene in Modell-Metern: ein L-foermiger Wand-Poche (konkav!), ein Raum
|
|
/// mit 45-Grad-Schraffur, ein Tuerschwenk-Bogen und ein paar Striche — genug, um
|
|
/// ALLE Render-Pfade zu sehen (Fuellung, Umriss, Schraffur/widthScreen,
|
|
/// analytischer Bogen, Papier-mm-Linien, Text).
|
|
pub fn demo_scene() -> Scene {
|
|
let wall_grey: [f32; 4] = [0.55, 0.55, 0.55, 1.0];
|
|
let room_blue: [f32; 4] = [0.20, 0.45, 0.85, 0.18];
|
|
let hatch_blue: [f32; 4] = [0.20, 0.45, 0.85, 0.55];
|
|
let ink: [f32; 4] = [0.10, 0.10, 0.10, 1.0];
|
|
|
|
// Konkaves L (Wandflaeche).
|
|
let l = vec![
|
|
[0.0, 0.0],
|
|
[4.0, 0.0],
|
|
[4.0, 2.0],
|
|
[2.0, 2.0],
|
|
[2.0, 4.0],
|
|
[0.0, 4.0],
|
|
];
|
|
// Ein transluzenter Raum daneben.
|
|
let room = vec![[5.0, 0.0], [9.0, 0.0], [9.0, 4.0], [5.0, 4.0]];
|
|
|
|
// 45-Grad-Schraffur im Raum (wie ein SVG-<pattern>): Strichbreite in
|
|
// BILDSCHIRM-Einheiten (`width_screen`), skaliert also mit dem Zoom wie die
|
|
// Musterkachel — genau der Pfad, den echte Material-Schraffuren nehmen.
|
|
// Linien y = x + c, auf das Raum-Rechteck geklippt.
|
|
let (hx0, hx1, hy0, hy1) = (5.0f32, 9.0f32, 0.0f32, 4.0f32);
|
|
let hatch_step = 0.5f32;
|
|
let mut hatch: Vec<Polyline> = Vec::new();
|
|
let mut c = hy0 - hx1 + hatch_step;
|
|
while c < hy1 - hx0 {
|
|
let xa = hx0.max(hy0 - c);
|
|
let xb = hx1.min(hy1 - c);
|
|
if xb > xa {
|
|
hatch.push(Polyline {
|
|
pts: vec![[xa, xa + c], [xb, xb + c]],
|
|
color: hatch_blue,
|
|
// Breite in Bildschirm-Einheiten (viewBox-px), NICHT Papier-mm.
|
|
width_mm: 1.2,
|
|
width_screen: true,
|
|
dash: None,
|
|
// Ueber der Raum-Fuellung (gleiches z=2, Kategorie Polyline kommt
|
|
// in der stabilen Sortierung NACH den Fills), unter dem
|
|
// Raum-Umriss (z=3).
|
|
z: 2,
|
|
});
|
|
}
|
|
c += hatch_step;
|
|
}
|
|
|
|
Scene {
|
|
fills: vec![
|
|
FillPolygon {
|
|
pts: l.clone(),
|
|
color: wall_grey,
|
|
z: 0,
|
|
},
|
|
FillPolygon {
|
|
pts: room.clone(),
|
|
color: room_blue,
|
|
z: 2,
|
|
},
|
|
],
|
|
outlines: vec![
|
|
Outline {
|
|
pts: l,
|
|
color: ink,
|
|
width_mm: 0.35,
|
|
dash: None,
|
|
z: 1,
|
|
},
|
|
Outline {
|
|
pts: room,
|
|
color: ink,
|
|
width_mm: 0.18,
|
|
dash: None,
|
|
z: 3,
|
|
},
|
|
],
|
|
polylines: hatch,
|
|
// Tuerschwenk in der linken Raumwand: analytischer Bogen (SDF-Pipeline),
|
|
// Angel bei (5,1), Blatt offen nach (6,1), Schwenk bis (5,2).
|
|
arcs: vec![Arc {
|
|
center: [5.0, 1.0],
|
|
from: [6.0, 1.0],
|
|
to: [5.0, 2.0],
|
|
r: 1.0,
|
|
color: ink,
|
|
width_mm: 0.18,
|
|
dash: None,
|
|
}],
|
|
lines: vec![
|
|
Line {
|
|
a: [0.0, -1.0],
|
|
b: [9.0, -1.0],
|
|
color: ink,
|
|
width_mm: 0.25,
|
|
dash: None,
|
|
z: 4,
|
|
},
|
|
// Tuerblatt zum Schwenkbogen (die Boegen zeichnen NACH den Linien,
|
|
// der Bogen liegt also wie im echten Plan ueber dem Blatt).
|
|
Line {
|
|
a: [5.0, 1.0],
|
|
b: [6.0, 1.0],
|
|
color: ink,
|
|
width_mm: 0.25,
|
|
dash: None,
|
|
z: 5,
|
|
},
|
|
],
|
|
// Ein Raumstempel-artiger Text (echte Glyphen via Atlas), mittig im Raum.
|
|
texts: vec![Text {
|
|
pos: [7.0, 2.0],
|
|
content: "Raum 16.00 m²".into(),
|
|
size_mm: 3.5,
|
|
color: ink,
|
|
align: TextAlign::Center,
|
|
}],
|
|
}
|
|
}
|
|
|
|
/// viewBox so, dass die Szene mittig einpasst (Bildschirm-Raum = Meter*PX_PER_M).
|
|
pub fn initial_view_box() -> ViewBox {
|
|
// Szene ~ x[0..9], y[-1..4] in Meter -> Bildschirm x[0..810], y[-360..90].
|
|
// Etwas Rand drumherum.
|
|
let pad = 90.0;
|
|
ViewBox::new(
|
|
-pad,
|
|
-4.0 * PX_PER_M - pad,
|
|
9.0 * PX_PER_M + 2.0 * pad,
|
|
5.0 * PX_PER_M + 2.0 * pad,
|
|
)
|
|
}
|