Dach im 3D anwählbar: Ray-Dreieck-Pick über die Dachflächen
Der 3D-Pick kannte nur Wand/Decke/Öffnung/Treppe — Dächer waren im
wgpu-Viewport nicht anwählbar (Nutzer-Report). Neu: pickGeometry liefert je
Dach die Dachflächen + Giebel als World-Dreiecke (fan-trianguliert wie
emitRoofs); pickNearest testet sie über das bestehende doppelseitige
rayTriangle (präzise geneigte Flächen statt Bounding-Prisma — kein Klick-
Diebstahl vor der Fassade dahinter). App-Routing: kind 'roof' →
setSelectedRoofIds (exklusiv, wie Treppe/Öffnung); damit greifen Highlight
und die Dach-3D-Griffe (d81d7d7) jetzt auch per 3D-Klick. +1 Test. 690/690.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import type { Project, Roof } from "../model/types";
|
||||
import { sampleProject } from "../model/sampleProject";
|
||||
import { selectionHighlightLines, projectToWalls3d, projectToModel3d } from "./toWalls3d";
|
||||
import { selectionHighlightLines, projectToWalls3d, projectToModel3d, pickGeometry } from "./toWalls3d";
|
||||
import { resolveHatch } from "./generatePlan";
|
||||
import { roofGeometry, roofBaseElevation } from "../geometry/roof";
|
||||
|
||||
@@ -1453,3 +1453,16 @@ describe("emitOpeningShading (Rollladen-/Sonnenschutzkasten als 3D-Box)", () =>
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pickGeometry — Dächer (Ray-Dreieck-Pick)", () => {
|
||||
it("liefert je Dach World-Dreiecke (Flächen + Giebel), world=[x,Höhe,y]", () => {
|
||||
const { roofs } = pickGeometry(sampleProject);
|
||||
// sampleProject trägt das Demo-Satteldach RF1.
|
||||
expect(roofs.length).toBe(1);
|
||||
expect(roofs[0].roofId).toBe("RF1");
|
||||
// Sattel: 2 Flächen (je 4 Ecken → 2 Dreiecke) + 2 Giebel (je 1 Dreieck) = 6.
|
||||
expect(roofs[0].tris.length).toBe(6);
|
||||
// Höhe liegt in der WORLD-Y-Komponente (Index 1) — alle über der Traufe > 0.
|
||||
for (const tri of roofs[0].tris) for (const p of tri) expect(p[1]).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
+28
-3
@@ -62,7 +62,10 @@ import type { WallCuts } from "../model/joins";
|
||||
import { add, dot, leftNormal, lineIntersect, normalize, scale, sub } from "../model/geometry";
|
||||
import type { Line } from "../model/geometry";
|
||||
import { resolveHatch } from "./generatePlan";
|
||||
import type { PickOpening, PickStair } from "../viewport/raycast3d";
|
||||
import type { PickOpening, PickRoof, PickStair } from "../viewport/raycast3d";
|
||||
|
||||
/** World-Punkt [x, Höhe, y] für den Dach-Pick (Vec3-Konvention von raycast3d). */
|
||||
type RoofPickVec3 = [number, number, number];
|
||||
|
||||
/**
|
||||
* Achswinkel einer GESCHNITTENEN Wand im Schnitt-Koordinatensystem (Modell-
|
||||
@@ -2455,7 +2458,29 @@ export function projectToModel3d(project: Project, opts: Model3dOptions = {}): R
|
||||
*/
|
||||
export function pickGeometry(
|
||||
project: Project,
|
||||
): { walls: RWall[]; slabs: RSlab[]; openings: PickOpening[]; stairs: PickStair[] } {
|
||||
): {
|
||||
walls: RWall[];
|
||||
slabs: RSlab[];
|
||||
openings: PickOpening[];
|
||||
stairs: PickStair[];
|
||||
roofs: PickRoof[];
|
||||
} {
|
||||
// Dächer: Dachflächen + Giebel als WORLD-Dreiecke (fan-trianguliert wie
|
||||
// emitRoofs; model [x,y,z=Höhe] → world [x, z, y]) — präzise geneigte
|
||||
// Flächen für den Ray-Dreieck-Pick (raycast3d.rayRoofTris).
|
||||
const roofs: PickRoof[] = [];
|
||||
for (const roof of project.roofs ?? []) {
|
||||
const eavesZ = roofBaseElevation(project, roof);
|
||||
const g = roofGeometry(roof, eavesZ);
|
||||
const tris: [RoofPickVec3, RoofPickVec3, RoofPickVec3][] = [];
|
||||
const toWorld = (p: RoofVec3): RoofPickVec3 => [p[0], p[2], p[1]];
|
||||
for (const poly of [...g.planes.map((pl) => pl.pts), ...g.gables]) {
|
||||
for (let i = 1; i + 1 < poly.length; i++) {
|
||||
tris.push([toWorld(poly[0]), toWorld(poly[i]), toWorld(poly[i + 1])]);
|
||||
}
|
||||
}
|
||||
if (tris.length > 0) roofs.push({ tris, roofId: roof.id });
|
||||
}
|
||||
const openings: PickOpening[] = [];
|
||||
for (const o of project.openings ?? []) {
|
||||
const wall = project.walls.find((w) => w.id === o.hostWallId);
|
||||
@@ -2492,7 +2517,7 @@ export function pickGeometry(
|
||||
stairId: st.id,
|
||||
});
|
||||
}
|
||||
return { walls: projectToWalls3d(project), slabs: emitSlabs(project), openings, stairs };
|
||||
return { walls: projectToWalls3d(project), slabs: emitSlabs(project), openings, stairs, roofs };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user