+
+ Geführter Ablauf
+ Step-by-Step
+
+
+ Beginnen Sie mit Fläche & Standtyp, veredeln Sie danach Wände/Boden und
+ platzieren Sie Module. Der Richtpreis aktualisiert sich live.
+
+
+ {guidedSteps.map((step, idx) => (
+
+
{idx + 1}
+
+
+ {step.title}
+
+ {step.done ? "fertig" : "offen"}
+
+
+
{step.desc}
+
+
+ ))}
+
+
+
+
{/* Presets */}
@@ -314,6 +427,31 @@ export default function SidebarControls() {
8×5 · Kopfstand Premium
+
+
+
+
+
+
+
+ Kollisionsschutz
+ AABB + Mindestabstand
+
+
+ Bewegte Objekte (Tresen, Screens, Kabine, Truss-Griff) prallen an einem
+ AABB-Sicherheitsabstand ab. Bei drohender Überschneidung erscheint ein
+ roter Wireframe + Hinweis. Der Mindestabstand lässt sich über
+ modules.collisionClearance im Store konfigurieren
+ (Playground: 0,25 m).
+
{/* Grunddaten */}
diff --git a/ss-messebau-configurator/src/lib/collision.ts b/ss-messebau-configurator/src/lib/collision.ts
new file mode 100644
index 0000000..1fa287a
--- /dev/null
+++ b/ss-messebau-configurator/src/lib/collision.ts
@@ -0,0 +1,149 @@
+import type { StandConfig } from "./pricing";
+
+export type Aabb = {
+ id: string;
+ label: string;
+ minX: number;
+ maxX: number;
+ minZ: number;
+ maxZ: number;
+};
+
+export const DEFAULT_CLEARANCE = 0.2;
+
+export const intersects = (a: Aabb, b: Aabb) =>
+ !(a.maxX <= b.minX || a.minX >= b.maxX || a.maxZ <= b.minZ || a.minZ >= b.maxZ);
+
+export const makeAabb = (
+ id: string,
+ label: string,
+ x: number,
+ z: number,
+ width: number,
+ depth: number,
+ clearance: number = DEFAULT_CLEARANCE
+): Aabb => {
+ const halfW = width / 2 + clearance;
+ const halfD = depth / 2 + clearance;
+ return {
+ id,
+ label,
+ minX: x - halfW,
+ maxX: x + halfW,
+ minZ: z - halfD,
+ maxZ: z + halfD,
+ };
+};
+
+export const findCollision = (
+ candidate: Aabb,
+ boxes: Aabb[],
+ ignored: Set