2D-Plan-Renderer auf WebGL2 (GPU) + akkumulierter Funktionsstand
Neuer GPU-Renderer fuer den Grundriss (src/plan/glPlan/): Earcut-Tessellierung (konkav-faehig), gehrte Linienzuege (Miter), echte Papier-mm-Strichbreiten im Massstab (repliziert den SVG-printStrokeVb-Pfad), Hybrid mit scharfem SVG-Text- Overlay. GPU ist der Standardpfad; der SVG-Renderer bleibt automatischer Fallback, falls WebGL2/Shader nicht verfuegbar sind. Imperativer Pan (rAF + CSS-transform) fuer fluessige Interaktion ohne React-Re-Render je Frame. Enthaelt zudem den bisher nicht committeten Arbeitsstand des Browser-BIM (Oeffnungen, Treppen, Raeume, Decken, DXF-Export, Materialbibliothek, Kontext- Import, Tauri-Compute-Boundary-PoC).
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// Verifikation Trim (Quick-Trim „stutzen"). Zwei sich kreuzende Linien zeichnen,
|
||||
// dann `trim` und auf den Überstand einer Linie klicken → der Überstand ist bis
|
||||
// zum Schnittpunkt weg (neuer Endpunkt = Schnittpunkt). Liest die gerenderten
|
||||
// `line.draw2d`-Segmente direkt aus dem SVG (Modellkoord. = SVG/PX_PER_M).
|
||||
import puppeteer from "puppeteer";
|
||||
|
||||
const PX = 90; // PX_PER_M in PlanView
|
||||
const b = await puppeteer.launch({
|
||||
headless: "new",
|
||||
args: ["--no-sandbox", "--use-gl=swiftshader", "--enable-unsafe-swiftshader"],
|
||||
});
|
||||
const p = await b.newPage();
|
||||
await p.setViewport({ width: 1280, height: 820, deviceScaleFactor: 1 });
|
||||
const errs = [];
|
||||
p.on("pageerror", (e) => errs.push(e.message));
|
||||
await p.goto("http://localhost:5187/", { waitUntil: "networkidle0", timeout: 20000 }).catch(() => {});
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
await sleep(800);
|
||||
|
||||
const focusCmd = async () => {
|
||||
const el = await p.$(".cmdline-input");
|
||||
const bx = await el.boundingBox();
|
||||
await p.mouse.click(bx.x + bx.width / 2, bx.y + bx.height / 2);
|
||||
await sleep(60);
|
||||
};
|
||||
const typeLine = async (t) => {
|
||||
await p.type(".cmdline-input", t, { delay: 6 });
|
||||
await p.keyboard.press("Enter");
|
||||
await sleep(90);
|
||||
};
|
||||
const segs = () =>
|
||||
p.evaluate((PX) => {
|
||||
return [...document.querySelectorAll(".plan-svg line.draw2d")].map((l) => ({
|
||||
x1: +l.getAttribute("x1") / PX,
|
||||
y1: -l.getAttribute("y1") / PX,
|
||||
x2: +l.getAttribute("x2") / PX,
|
||||
y2: -l.getAttribute("y2") / PX,
|
||||
}));
|
||||
}, PX);
|
||||
// Modellpunkt → Client-Pixel über das aktuelle SVG-CTM.
|
||||
const modelToClient = (mx, my) =>
|
||||
p.evaluate(
|
||||
(mx, my, PX) => {
|
||||
const svg = document.querySelector(".plan-svg");
|
||||
const ctm = svg.getScreenCTM();
|
||||
const q = svg.createSVGPoint();
|
||||
q.x = mx * PX;
|
||||
q.y = -my * PX;
|
||||
const s = q.matrixTransform(ctm);
|
||||
return [s.x, s.y];
|
||||
},
|
||||
mx,
|
||||
my,
|
||||
PX,
|
||||
);
|
||||
|
||||
// ── Zwei sich kreuzende Linien ───────────────────────────────────────────────
|
||||
// A: horizontal y=2, x=0..6 (Überstand rechts ab x=4). B: vertikal x=4, y=0..4.
|
||||
await focusCmd();
|
||||
await typeLine("line");
|
||||
await typeLine("0,2");
|
||||
await typeLine("6,2");
|
||||
await focusCmd();
|
||||
await typeLine("line");
|
||||
await typeLine("4,0");
|
||||
await typeLine("4,4");
|
||||
await p.keyboard.press("Escape");
|
||||
await sleep(120);
|
||||
|
||||
const before = await segs();
|
||||
|
||||
// ── Trim: auf den Überstand von A (x=5, y=2) klicken ─────────────────────────
|
||||
await focusCmd();
|
||||
await typeLine("trim");
|
||||
// Befehl ist aktiv; jetzt in der Plan-Ansicht auf den Überstand klicken.
|
||||
// Eingabefeld behält Fokus → der Klick geht trotzdem an PlanView (pick).
|
||||
const [hx, hy] = await modelToClient(5, 2);
|
||||
await p.mouse.move(hx, hy);
|
||||
await sleep(120);
|
||||
await p.mouse.click(hx, hy);
|
||||
await sleep(200);
|
||||
await p.keyboard.press("Escape");
|
||||
await sleep(120);
|
||||
|
||||
const after = await segs();
|
||||
await p.screenshot({ path: "scripts/probe-trim.png" });
|
||||
|
||||
// ── Auswertung: gibt es ein horizontales Segment (y≈2), das bei x≈4 endet und
|
||||
// NICHT über x=4 hinausragt? Der Überstand x∈(4,6] muss weg sein. ────────────
|
||||
const horiz = after.filter((s) => Math.abs(s.y1 - 2) < 0.05 && Math.abs(s.y2 - 2) < 0.05);
|
||||
const maxX = horiz.reduce((m, s) => Math.max(m, s.x1, s.x2), -Infinity);
|
||||
const trimmedToIntersection = Math.abs(maxX - 4) < 0.05;
|
||||
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
beforeCount: before.length,
|
||||
afterCount: after.length,
|
||||
horizSegments: horiz,
|
||||
maxXofHoriz: maxX,
|
||||
trimmedToIntersection,
|
||||
errs,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
await b.close();
|
||||
Reference in New Issue
Block a user