Akkumulierten grünen Arbeitsstand landen (Basis für Weiterarbeit)
Bündelt den über mehrere Sessions gewachsenen, uncommitteten Stand in
einem Basis-Commit, damit Folge-Features isoliert darauf aufsetzen.
Verifikation: tsc --noEmit sauber, vitest 600/600 grün.
Enthalten (Details in PENDENZEN.md ✅-Liste / HANDOVER.md):
- truck-Integration: Profil-Extrusion + Verjüngung + Boolean-CSG (csgrs),
Crate src-tauri/trucksolid, Werkzeug `extrude`, ExtrudedSolid-Modell.
- kernel2d-Port nach Rust/WASM (Phasen 1–5, Diff-Harness).
- render3d 3D-Live-Schnitt = 2D-Schnitt: geschichteter Bodenaufbau,
Prioritäts-Verschneidung (section_boolean.rs), einstellbare
Schichttrennlinien, per-Hatch-Strichstärke, relativeToWall-Orientierung.
- Interop-Export IFC4/STL/OBJ (Loch-Ausschnitt wallMeshCut), Schnellexport.
- Projektdatei .obp + OS-Lock (lock.rs, LockConflictDialog).
- Layout-Blätter (Modell/Editor/Panel/PDF), Ausschnitte, Override-Engine,
Tragwerk-Stützen (Column), BIM-Tree-Panel.
- Bauteil-Typsystem (Tür/Fenster/Treppe-Typen), Betontreppe mit schräger
Laufplatte, Text-/Textbox-Werkzeug, Mess-Werkzeug, 2D/3D-Griffe für
Öffnungen/Treppen, Snap-Symbol-Restyle.
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
// Grafische Overrides — reine Auswertungs-Engine (Regel-Engine, DOSSIER A1).
|
||||
//
|
||||
// Wertet die Regelliste `Project.overrideRules` für ein Element aus und
|
||||
// liefert die effektiven Aktionen (Farbe/Strichstärke/Linienstil). Die Regeln
|
||||
// werden ADDITIV angewendet: pro Aktions-Feld gewinnt die OBERSTE (erste)
|
||||
// aktive Regel, deren Bedingung matcht; spätere Regeln füllen nur noch die
|
||||
// verbleibenden Felder. Deaktivierte Regeln werden übersprungen.
|
||||
//
|
||||
// Reines Rendering-Overlay: die Engine kennt weder Project noch Renderer —
|
||||
// der Aufrufer (plan/generatePlan.ts) baut das Subjekt aus Ebene + Element
|
||||
// und legt das Ergebnis als oberste Schicht ÜBER die reguläre
|
||||
// By-Layer/By-Object-Attribut-Auflösung. Elementdaten bleiben unverändert.
|
||||
|
||||
import type {
|
||||
OverrideActions,
|
||||
OverrideCondition,
|
||||
OverrideRule,
|
||||
} from "../model/types";
|
||||
|
||||
/**
|
||||
* Das Subjekt einer Auswertung — die vergleichbaren Eigenschaften EINES
|
||||
* Elements. `layer_name`-Bedingungen matchen gegen Namen ODER Code der Ebene
|
||||
* (LayerCategory), `object_name`-Bedingungen gegen den Element-/Typ-Namen.
|
||||
*/
|
||||
export interface OverrideSubject {
|
||||
/** Name der Ebene (LayerCategory.name) des Elements. */
|
||||
layerName?: string;
|
||||
/** Code der Ebene (LayerCategory.code / Element.categoryCode). */
|
||||
layerCode?: string;
|
||||
/** Element-/Typ-Name (z. B. Wandtyp-Name, Raum-Name, Öffnungs-Label). */
|
||||
objectName?: string;
|
||||
}
|
||||
|
||||
/** Leere Aktionen (kein Override) — geteilte Konstante für den Schnellpfad. */
|
||||
export const EMPTY_OVERRIDES: OverrideActions = Object.freeze({});
|
||||
|
||||
/** Case-insensitiver Feld-Vergleich eines Operators. */
|
||||
function matchValue(
|
||||
operator: OverrideCondition["operator"],
|
||||
candidate: string,
|
||||
value: string,
|
||||
): boolean {
|
||||
switch (operator) {
|
||||
case "equals":
|
||||
return candidate === value;
|
||||
case "contains":
|
||||
return candidate.includes(value);
|
||||
case "starts_with":
|
||||
return candidate.startsWith(value);
|
||||
case "not_equals":
|
||||
return candidate !== value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Bedingung auf ein Subjekt zutrifft (case-insensitiv,
|
||||
* Wert getrimmt). `layer_name` vergleicht gegen Ebenen-NAME und -CODE:
|
||||
* positive Operatoren (equals/contains/starts_with) treffen, wenn EINER der
|
||||
* beiden passt; `not_equals` trifft nur, wenn KEINER der beiden gleich ist
|
||||
* (sonst würde der Code fast jede Namens-Ungleichheit erfüllen).
|
||||
*/
|
||||
export function matchesCondition(
|
||||
condition: OverrideCondition,
|
||||
subject: OverrideSubject,
|
||||
): boolean {
|
||||
const value = condition.value.trim().toLowerCase();
|
||||
const candidates: string[] = [];
|
||||
if (condition.type === "layer_name") {
|
||||
if (subject.layerName != null) candidates.push(subject.layerName.trim().toLowerCase());
|
||||
if (subject.layerCode != null) candidates.push(subject.layerCode.trim().toLowerCase());
|
||||
} else {
|
||||
if (subject.objectName != null) candidates.push(subject.objectName.trim().toLowerCase());
|
||||
}
|
||||
if (candidates.length === 0) {
|
||||
// Subjekt trägt die Eigenschaft nicht: positive Operatoren können nicht
|
||||
// treffen; „ungleich" ist trivial erfüllt.
|
||||
return condition.operator === "not_equals";
|
||||
}
|
||||
if (condition.operator === "not_equals") {
|
||||
return candidates.every((c) => c !== value);
|
||||
}
|
||||
return candidates.some((c) => matchValue(condition.operator, c, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Kollabiert die Regelliste für ein Subjekt zu den effektiven Aktionen.
|
||||
* Additiv, oberste Regel gewinnt PRO FELD: die erste aktive, matchende Regel,
|
||||
* die ein Feld setzt, bestimmt es; weitere Regeln füllen nur offene Felder.
|
||||
* Ohne Treffer (oder ohne Regeln) kommt {@link EMPTY_OVERRIDES} zurück.
|
||||
*/
|
||||
export function effectiveOverrides(
|
||||
rules: readonly OverrideRule[] | undefined,
|
||||
subject: OverrideSubject,
|
||||
): OverrideActions {
|
||||
if (!rules || rules.length === 0) return EMPTY_OVERRIDES;
|
||||
let out: OverrideActions | null = null;
|
||||
for (const rule of rules) {
|
||||
if (!rule.enabled) continue;
|
||||
if (!matchesCondition(rule.condition, subject)) continue;
|
||||
const a = rule.actions;
|
||||
if (a.color === undefined && a.lineweight === undefined && a.linetypeId === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!out) out = {};
|
||||
if (out.color === undefined && a.color !== undefined) out.color = a.color;
|
||||
if (out.lineweight === undefined && a.lineweight !== undefined) {
|
||||
out.lineweight = a.lineweight;
|
||||
}
|
||||
if (out.linetypeId === undefined && a.linetypeId !== undefined) {
|
||||
out.linetypeId = a.linetypeId;
|
||||
}
|
||||
// Alle Felder belegt → keine weitere Regel kann noch etwas beitragen.
|
||||
if (
|
||||
out.color !== undefined &&
|
||||
out.lineweight !== undefined &&
|
||||
out.linetypeId !== undefined
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out ?? EMPTY_OVERRIDES;
|
||||
}
|
||||
|
||||
/** True, wenn mindestens eine Aktion gesetzt ist (Schnelltest für Aufrufer). */
|
||||
export function hasOverrides(actions: OverrideActions): boolean {
|
||||
return (
|
||||
actions.color !== undefined ||
|
||||
actions.lineweight !== undefined ||
|
||||
actions.linetypeId !== undefined
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user