diff --git a/CLAUDE.md b/CLAUDE.md index ff07507..189ce35 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,7 +126,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `KettenFoerderer.lsp` | KETTENFOERDERER, KF_EDIT, KF_KS_PRUEFEN | Kettenfoerderer/Schraegfoerderer Stahl: baut aus `KF_UM_` -> `KF_LAENGE` -> `KF_AN_` (je KS_AUS auf KS_EIN) plus der Draufsicht `KF_2D` EINEN Block `KF_`. Daten `data/json/kettenfoerderer.json`, Dialog `dcl/kettenfoerderer.dcl`. **Geometrie**: SEITENANSICHT in der XY-Ebene (Hoehe = Y), ungedreht und nicht aufgerichtet, Einfuegepunkt = KS_EIN der Umlenkung; jeder Baustein bekommt nur den aus den KS gemessenen Verkettungswinkel (`insert-block-by-ks`/`insert-block-ks-skaliert`). Die Neigung (26/30 Grad) ist KEIN Rechenparameter: sie steckt in den Varianten-Bloecken, der Einbauwinkel jedes Folgebausteins wird zur Laufzeit aus der RICHTUNG des KS_AUS seines Vorgaengers gemessen (`ks-xy-winkel`/`kf-hz-folge`) - keine gepflegte Winkeltabelle. `KF_LAENGE` wird in X skaliert, die Einbauhoehe H bestimmt seine Laenge ueber die Hoehenbilanz (`kf-laenge-berechnen`). **Hauptblock + Hilfsblock**: `KF_` traegt Beschriftung (`KF_1 26 Grad H=..mm L_horizontal=..mm`, Layer `kettenfoerderer_beschriftung`) und ein Hoehenmass (ActiveX `AddDimRotated`, scheitert es, wird ohne Mass gebaut) und ist der EINZIGE exportierte Block. Die Draufsicht `KF_2D` ist ein getrennter Hilfsblock `KF2D_` (kein Objekt, nur zum Anschliessen von Schienen): flach, auf die PROJIZIERTE Laenge skaliert (`kf-waagerecht-laenge`), beim ersten Einfuegen `ansicht_2d.versatz_y` (-1000) mm unter dem Hauptblock, danach frei verschieb- und loeschbar, gleiche Beschriftung. Beide Bloecke tragen denselben Schluessel als XDATA `SSG_KF_EDIT` (`kf-xdata-key`); KF_EDIT findet damit den - auch verschobenen - Hilfsblock (`kf-hilfsblock-finden`), behaelt dessen Lage und erzeugt ihn bei jedem Edit neu (auch wenn er geloescht war). Der Hilfsblock passt nicht aufs Export-Muster `KF_*`; `export_sivas.py` nimmt ausserdem nur `KF_`. Eine von Hand gesetzte Drehung des Hauptblocks (DXF 50) wird beim Edit wieder aufgesetzt. **Attribut-Schema** `*kf-attrib-defs-liste*` ist EIGENSTAENDIG und bewusst NICHT von `ssg-strecke-attrib-defs` abgeleitet (sonst ~24 geerbte VF-Tags am Block); die Reihenfolge folgt dem Sivas-Merkmalstamm. FOERDERERTYP = Bauart im Klartext, WINKEL = nackte Zahl 26/30 (der Klartext mit Gradzeichen entsteht erst ueber `[kf_typ_anzeige]` in cfg/sivas.cfg, damit Attributwerte ASCII bleiben); kein TEILEART (Festwert `KF_TEILEART` im Export). `L_KF_mm` ist die WAAGERECHTE Laenge des ganzen Foerderers (der Draufsicht `KF_2D`, `kf-waagerecht-laenge`), nicht die Laenge des schraegen Mittelstuecks; sie geht NICHT in die Sivas-Zeile (Spalte Laenge bleibt leer) - Sivas berechnet ZL, EL und Kettenlaenge selbst, uebernommen wird nur die Hoehe (360). Alle Auswahllisten stehen mit ihren Sivas-Optionstexten in der JSON. **Das `KF_`-Praefix ist wie `GR_` nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, darum steht ein `KF_*`-Zweig VOR dem Omniflo-Zweig (Doppelklick -> KF_EDIT, der die vorgewaehlte Auswahl nutzt und nur echte `KF_` zulaesst - die Bausteine `KF_UM_26`, `KF_2D` usw. tragen dasselbe Praefix). **Die Blocknummer wird ueber die Blocktabelle vergeben** (`kf-naechste-nummer` ueberspringt jede Nummer mit vorhandener Block-DEFINITION, wie bei `GF_n`/`VF_n`): nur ueber die Referenzen im Modell zu zaehlen war ein realer Fehler - `KF_EDIT` loescht den alten Block, die Definition lebt aber weiter, `_.-BLOCK` traf auf den vorhandenen Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben, und die Einzelteile blieben lose liegen ("Block aufgeloest"). **Sivas-Export** (`[kettenfoerderer]` in cfg/sivas.cfg, `build_kettenfoerderer_details` in export_sivas.py): baugleiche Foerderer werden zu EINER Zeile zusammengefasst (`kettenfoerderer_signature`), die Gruppengroesse traegt Merkmal 353 (entsteht erst aus der Gruppierung, daher Code); Farbe (354) und Antriebsmotor (355) sind ZWEI Merkmale, und jedes merkmalbildende Attribut steht auch in der Signatur, sonst verschmelzen unterschiedlich ausgefuehrte Foerderer. Reine Kerne in `tests/test_unit.lsp` (`tu-tests-kf`); das Modul wird in der MNL vorgeladen, Laufzeit-Messung ueber den dbg-Schalter `kf-zeit` | | `HwPlanung.lsp` | HW_MATERIALFLUSS, HW_SYMBOLE, HW_SYM_VENTILE, HW_SYM_SENSOREN, HW_SYM_MOTOREN, HW_SYM_MOTORSCHUTZ, HW_SYM_ENCODER, HW_SYM_SCANNER, HW_SYM_LOESCHEN, HW_NUMMERIEREN | Menue "HW Planung" (zwischen Lager und SPS), Ablauf und Renamer-Regeln in `doc/hw_planung.md`. **Ausgabeziel per Dialog** `dcl/hw_ausgabe.dcl` vor jedem Erzeugungsbefehl (`hw-ausgabe-waehlen`, letzte Wahl je Sitzung in `*hw-ausgabe-letzt*`, ohne GUI Standarddatei `_HW.dxf`): **aktuelle Zeichnung** (Python schreibt eine EINHEITENLOSE Zwischendatei `_HW_tmp.dxf` (`--einheitenlos`, sonst skaliert BricsCAD beim Einfuegen), `hw-in-aktuelle-zeichnung` fuegt sie per `vla-InsertBlock` bei 0,0 ein, loest sie per `vla-Explode` auf und loescht ERST DANACH die fruehere Ausgabe - `hw-alte-ausgabe` ueber XDATA `SSG_HW_MF` (Materialfluss) bzw. `SSG_HW_SYM` je Praefix), **bestehende Datei ergaenzen** (getfiled ueber `done_dialog 2`, da getfiled aus einem action_tile nicht geht) oder **neue Datei** (Name, Ordner DXFM_RESULTS; existiert sie, bleibt der Dialog mit Hinweis offen). Reine Kerne `hw-name-ohne-dxf`/`hw-name-gueltig-p`/`hw-neu-pfad`/`hw-ausgabe-pruefen` getestet. In einer Datei ersetzt jeder Befehl nur seinen eigenen Anteil (Materialfluss = XDATA `SSG_HW_MF` bzw. HW_*-Layer, Symbole = XDATA `SSG_HW_SYM` je Praefix) - vom Anwender Gezeichnetes (Renamer-Bereiche) bleibt; die Ursprungsdatei auf der Platte wird nie geaendert. Python-Aufrufe zentral ueber `hw-python` (Log je Befehl in DXFM_LOG, `ok-codes` fuer create_numbers' Exit 1 bei Planungsfehlern). HW_MATERIALFLUSS: EXPORTCSV (alte CSV vorher geloescht) -> `lib/hw_materialfluss.py`. **Symbole**: Regelwerk `cfg/io_regeln.json` (reduzierte Kopie aus kabellaengen: Rollen mit Praefix/Symbolblock, Bauteile, `io_vorlage`; Erkennungs-Regex/Radien/Nummernpaare/altZuNeu entfernt). **Aussehen und Attribute der Symbole stehen in `cfg/hw_symbole.json`** (Vorlagen mit Rahmen/Schraffur + Attribut-Lage/-Hoehe/-Stil, je Symbol Layer/Farbe/`versatz`/Abweichungen je Tag/Werte) - es gibt KEINE Symbol-Bibliothek als Referenzdokument. Die Symbole tragen DIESELBEN Blocknamen wie die ILS-Katalogsymbole in kabellaengen und sind ihnen exakt nachgebaut (inkl. Einfuegepunkt: Eingaenge/Motoren haengen nach unten) - `test_symbole_gleichen_dem_katalog` prueft das gegen `kabellaengen/doc/Symbole/ILS-Symbole.dxf`; aendert sich dort ein Symbol, faellt der Test und die Konfiguration wird nachgezogen. `hw-sym-bauteile-sammeln` findet die Mechanik-Bloecke der Ursprungszeichnung top-level UND verschachtelt in VF_n/GF_n/KREISEL_n (Weltlage ueber `csv:wrapper-welt-records`), `hw-sym-plan` legt je Rolle Symbolblock, flachen ZIELPUNKT (Bauteil-Einfuegepunkt, `hw-sym-ziel`) und ID-VORLAGE (`hw-sym-io-vorlage`, z.B. `MB1@@@`) fest; bei Alternativen in `benoetigt` die erste. `hw-sym-plan->json` (Zahlen ueber `hw-json-zahl`, DIMZIN-unabhaengig) -> `_hw_symbole.json` -> `lib/hw_symbole.py`; der Plan ist IMMER vollstaendig (alle Praefixe), `praefixe` sagt nur, was eingefuegt wird - so haengt die Lage nicht vom Einzelbefehl ab. Die LAGE rechnet Python (kennt die Symbolgeometrie), nicht LISP. Die Nummern vergibt NICHT das Erzeugen, sondern HW_NUMMERIEREN in der geoeffneten _HW.dxf: QSAVE, `lib/create_numbers.py --dryrun --info`, dann `hw-num-anwenden` (Zuordnung je Attribut mit `@` ueber Wert + Einfuegepunkt, `hw-num-eintrag-p`) in die offene Zeichnung. Reine Kerne in `tu-tests-hw` | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | -| `LagerInsert.lsp` | LG_GESCHOSS, LG_GESCHOSS_AKTIV, LG_GESCHOSS_KOPIEREN, LG_BEREICH, LG_REIHE, LG_GANG, LG_EDIT, LG_REGALE, LG_DARSTELLUNG, LAGER_EXPORT | Lagerplanung GOH-Buehnenlager -> technische Daten als JSON fuer die RFQ-Dokumentation (Phase 0+1: Mengen, 2D; Konzept/Schema/Kennwerte in `doc/plan/lager_rfq.md`). Bloecke `LG_GESCHOSS` (Kopfsymbol, XDATA `SSG_LG` `kontur` -> Belagskontur), `LG_BEREICH` (Kontur + Parameter, fuellt sich selbst), `LG_REIHE__`/`LG_GANG___` (Definition je Geometrie wiederverwendet, Werte in den Attributen der Referenz; Kinder tragen XDATA `bereich`; der Belag steht im Gang-Blocknamen, weil die Definition seine Schraffur enthaelt - Muster aus `lager.json belag_schraffur`, per `vla-AddHatch` auf Layer 0/VONBLOCK, `hallenboden` ohne; BricsCAD-`iso.pat` kennt kein `NET`, Gitterrost = `GRID`). Alle tragen `ID`, `GESCHOSS`, `LAYER`. Blockdefinitionen per `entmake` (BLOCK...ENDBLK), Einfuegen per `vla-InsertBlock` in WCS - kein `_.-BLOCK`/`_.INSERT`. **Attribut-Tags haben gemischte Schreibweise** (`HOEHE_BELAG_mm`) - beim Setzen ueber `vla-get-TagString` daher case-insensitiv zuordnen (`lg-wert-fuer-tag`), sonst bleiben diese Attribute still leer (realer Fehler beim Bau). Layer je Geschoss = Basis aus `cfg/layer.cfg [lager]` + `_G`; `LG_GESCHOSS_AKTIV` friert die uebrigen (2D-Grundrisse liegen uebereinander), aktives Geschoss als `vlax-ldata` (Dictionary `SSG_LAGER`). Geschosszuordnung `lg-geschoss-fuer-punkt-kern`: eine Kontur -> eindeutig, mehrere nur ueber Z unterscheidbar, wenn die Konturen auf verschiedenen Hoehen liegen (3D), sonst nil. Jede Kontur gehoert genau einem Block (vergebene Kontur wird kopiert). Auto-Fuellung `lg-fuellung-berechnen` (reiner Kern, Muster Reihe|Gang|Reihe Ruecken an Ruecken bzw. Reihe|Gang, nur volle Module, Streifen an Unter-/Mittel-/Oberkante gegen die Kontur geschnitten, Quergaenge ausgerichtet und nicht am Reihenende); Bereichs-Edit = Neuaufbau aller Kinder. **Randgaenge** `RANDGANG_N/O/S/W_mm` (Nord = +Y Welt) laufen entlang der echten Konturkanten (`lg-randgang-streifen`, Himmelsrichtung der Aussennormale ueber `lg-kompass`, auch L-Form; Ecken: N/S besitzen sie - konvex O/W kuerzen, einspringend N/S verlaengern) und werden wie manuelle Gaenge (`LG_GANG`, `QUELLE=manuell`, Kern `lg-gang-einfuegen`; per Linie oder Option `Rechteck` = Flaeche mit dem echten BricsCAD-Befehl RECHTECK gezeichnet, inkl. aller seiner Optionen (`lg-rechteck-zeichnen`: `_.RECTANG` + `(command pause)`-Schleife bei CMDECHO 1, Polylinie danach ueber `lg-rechteck-ecken-aus-poly` gelesen und geloescht; nur 4 Ecken ohne Boegen, sonst Meldung), Breite/Lage aus dem Rechteck ueber `lg-gang-aus-ecken`, Schleife `lg-linien-schleife` mit `rechteck-fn`) als **Sperrflaechen** aus Reihen/Haupt-/Quergaengen ausgeschnitten (`ssg-poly-band-x`). **Von Hand Gesetztes loest KEINE Neuberechnung aus**: `LG_REIHE`/`LG_GANG` (`lg-manuell-einfuegen`) und das Bearbeiten einer Reihe/eines Gangs (`lg-linie-neu-aufbauen-sperre`) schneiden nur die eigene Flaeche aus den rangniedrigeren Objekten heraus (`lg-sperre-anwenden`, reiner Kern `lg-linie-zuschnitt`; Rang `lg-quelle-rang`: auto < auto_geaendert < manuell) - frueher fuellte ein manueller Gang den ganzen Bereich neu, dabei kamen geloeschte Objekte zurueck und geaenderte wurden ersetzt. Die ausdrueckliche Neuberechnung (`lg-bereich-fuellen`, Bereich bearbeiten) entfernt nur `QUELLE=auto`; `manuell`/`auto_geaendert`-Kinder und unverknuepfte manuelle Objekte in der Kontur (`lg-manuelle-in-kontur`) bleiben und sperren ihre Flaeche. `lg-blockdef` ergaenzt bei Altbestand fehlende ATTDEFs (Neudefinition + `_.ATTSYNC`). `LAGER_EXPORT` schreibt NUR `_lager.json` nach `DXFM_RESULTS` (Format `ssg-lager` Version 2, kein Python-Schritt; das RFQ-Projekt uebernimmt Mapping und Hand-Abgleich): alle Geschosse, Bereiche, Reihen und Gaenge mit Attributen und voller Geometrie (Konturpunkte, Start/Ende/Mitte, 4 Ecken) plus `kennwerte` je Geschoss und `lager` gesamt (lichte Hoehe, Regal-/Schienenlaenge, Haengeebenen, Gaenge je Art und Breite, Belags-/Gitterrostflaeche, Gitterkorb, Regalrohr = Laenge x Haengeebenen), je Geschoss-Kennwert `herkunft` (Formel + Quelle). **`null` = fehlt (offener Punkt), nicht Zutreffendes wird weggelassen** (Marker `'ENTFAELLT`, `lg-json-ohne-entfallend`). Plausibilitaetswarnungen je Geschoss (`lg-plausibilitaet`, Grenzwerte aus dem Regelprofil). Das Reihen-Attribut `SCHIENEN` ist entfallen; `SCHIENEN_JE_GANG` am Bereich (Trolley) wird noch nicht ausgewertet. Eigener JSON-Schreiber `lg-json-*` ueber getaggte Werte `(OBJ ...)`/`(ARR ...)` (`lg-json-text` rueckt ein, Nicht-ASCII als `\uXXXX`), Kennwerte in reinen Kernen (`lg-kennwerte-geschoss`/`-lager`, `lg-haengeebenen-summen`, `lg-gaenge-je-art`, `lg-reihen-offene-kanten`, `lg-reihen-gitterkorb-berechnen`). **Absturzsicherung unter den Reihen** je Bereich (`UNTER_REIHE` = `gitterrost`/`gitterkorb`/`keine`, Vorgabe `lager.json bereich.unter_reihe`, nur auf Buehnen wirksam): Gitterrost zaehlt die Reihengrundflaeche zur Gitterrostflaeche, Gitterkorb liefert Boden (Laenge x Tiefe) und Seitenflaeche = OFFENE Kantenlaenge x `GITTERKORB_HOEHE_mm` (Kanten an einer direkt anliegenden Nachbarreihe desselben Geschosses, z.B. Ruecken an Ruecken, zaehlen nicht; die Korbhoehe soll spaeter aus den Attributen berechnet werden). Regelwerk-Werte stehen noch nicht im Export (`doc/todo.md`). **Darstellung der Absturzsicherung** (`lg-darstellung-geschoss`): auf Buehnen je Reihe eine Schraffur fuer gitterrost bzw. gitterkorb, beim Gitterkorb zusaetzlich die seitliche Abgrenzung als dicke Polylinie NUR an offenen Kanten (reiner Kern `lg-sicherung-geometrie` ueber `lg-kante-offen-intervalle`, gleiche Regel wie der Export). Lose Objekte auf `LG_Sicherung_G` mit XDATA `rolle=sicherung`, bei jedem Neuaufbau komplett ersetzt; automatisch nach Bereich fuellen, Reihe/Gang/Geschoss bearbeiten, `LG_REIHE`/`LG_GANG` und Geschoss kopieren. Farben/Muster aus `lager.json` (`darstellung.reihe.farbe`, `darstellung.unter_reihe.*`, `belag_schraffur..farbe`; ACI oder "r,g,b", `lg-farbe-dxf`/`lg-farbe-setzen`). `LG_DARSTELLUNG` (Menue Lager -> Darstellung aktualisieren) liest lager.json neu, frischt die Definitionen `LG_REIHE_*`/`LG_GANG_*` auf (`lg-darstellung-definitionen`) und zeichnet alle Geschosse neu. Befehl `LG_REGALE` (Menue Lager -> Regale zaehlen) zaehlt die Regalreihen `LG_REIHE_*` (Anzahl, Laenge, Laenge x Haengeebenen je Geschoss aus den Attributen GESCHOSS/LAENGE/HAENGEEBENEN, reine Kerne `lg-reihe-eintrag`/`lg-reihen-auswerten`). **Der parametrische BricsCAD-Block `goh_rack` wird nicht mehr unterstuetzt** (veraltet, 2026-10-30 aus Zaehlung, Export und Geschoss-Kopie entfernt). Vorgaben aus `data/json/lager.json`. Dialoge `dcl/lager.dcl` ueber den generischen Treiber `lg-dialog` (Feldlisten `*lg-felder-*`, Konsolen-Rueckfall `lg-konsole`, ohne GUI Vorgaben). **Geschoss kopieren** (`LG_GESCHOSS_KOPIEREN`, Dialog `lg_geschoss_kopie`, Kern `lg-geschosse-kopieren`): Quelle (Vorgabe oberstes Geschoss, Erweitern G3 -> G4), Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie (0 = deckungsgleich; 3D-Zeichnung zusaetzlich +Z). Kein BricsCAD-KOPIEREN (XDATA-Verweise zeigten aufs Original): jedes Objekt entsteht ueber die normalen Einfuegefunktionen neu (`lg-geschoss-anlegen`, `lg-bereich-block-anlegen` = Bereich ohne Fuellung, `lg-linie-einfuegen`), Kinder 1:1 inkl. `auto_geaendert`/manuell, kein Neufuellen; Belaege gleich dem Quell-Geschossbelag folgen dem neuen (`lg-kopie-belag`). Tests: `tu-tests-lager` (test_unit) und `tests/test_lager.lsp` | +| `LagerInsert.lsp` | LG_GESCHOSS, LG_GESCHOSS_AKTIV, LG_GESCHOSS_KOPIEREN, LG_BEREICH, LG_REIHE, LG_GANG, LG_EDIT, LG_REGALE, LG_DARSTELLUNG, LAGER_EXPORT | Lagerplanung GOH-Buehnenlager -> technische Daten als JSON fuer die RFQ-Dokumentation (Phase 0+1: Mengen, 2D; Konzept/Schema/Kennwerte in `doc/plan/lager_rfq.md`). Bloecke `LG_GESCHOSS` (Kopfsymbol, XDATA `SSG_LG` `kontur` -> Belagskontur), `LG_BEREICH` (Kontur + Parameter, fuellt sich selbst), `LG_REIHE__`/`LG_GANG___` (Definition je Geometrie wiederverwendet, Werte in den Attributen der Referenz; Kinder tragen XDATA `bereich`; der Belag steht im Gang-Blocknamen, weil die Definition seine Schraffur enthaelt - Muster aus `lager.json belag_schraffur`, per `vla-AddHatch` auf Layer 0/VONBLOCK, `hallenboden` ohne; BricsCAD-`iso.pat` kennt kein `NET`, Gitterrost = `GRID`). Alle tragen `ID`, `GESCHOSS`, `LAYER`. Blockdefinitionen per `entmake` (BLOCK...ENDBLK), Einfuegen per `vla-InsertBlock` in WCS - kein `_.-BLOCK`/`_.INSERT`. **Attribut-Tags haben gemischte Schreibweise** (`HOEHE_BELAG_mm`) - beim Setzen ueber `vla-get-TagString` daher case-insensitiv zuordnen (`lg-wert-fuer-tag`), sonst bleiben diese Attribute still leer (realer Fehler beim Bau). Layer je Geschoss = Basis aus `cfg/layer.cfg [lager]` + `_G`; `LG_GESCHOSS_AKTIV` friert die uebrigen (2D-Grundrisse liegen uebereinander), aktives Geschoss als `vlax-ldata` (Dictionary `SSG_LAGER`). Geschosszuordnung `lg-geschoss-fuer-punkt-kern`: eine Kontur -> eindeutig, mehrere nur ueber Z unterscheidbar, wenn die Konturen auf verschiedenen Hoehen liegen (3D), sonst nil. Jede Kontur gehoert genau einem Block (vergebene Kontur wird kopiert). Auto-Fuellung `lg-fuellung-berechnen` (reiner Kern, Muster Reihe|Gang|Reihe Ruecken an Ruecken bzw. Reihe|Gang, nur volle Module, Streifen an Unter-/Mittel-/Oberkante gegen die Kontur geschnitten, Quergaenge ausgerichtet und nicht am Reihenende); Bereichs-Edit = Neuaufbau aller Kinder. **Randgaenge** `RANDGANG_N/O/S/W_mm` (Nord = +Y Welt) laufen entlang der echten Konturkanten (`lg-randgang-streifen`, Himmelsrichtung der Aussennormale ueber `lg-kompass`, auch L-Form; Ecken: N/S besitzen sie - konvex O/W kuerzen, einspringend N/S verlaengern) und werden wie manuelle Gaenge (`LG_GANG`, `QUELLE=manuell`, Kern `lg-gang-einfuegen`; per Linie oder Option `Rechteck` = Flaeche mit dem echten BricsCAD-Befehl RECHTECK gezeichnet, inkl. aller seiner Optionen (`lg-rechteck-zeichnen`: `_.RECTANG` + `(command pause)`-Schleife bei CMDECHO 1, Polylinie danach ueber `lg-rechteck-ecken-aus-poly` gelesen und geloescht; nur 4 Ecken ohne Boegen, sonst Meldung), Breite/Lage aus dem Rechteck ueber `lg-gang-aus-ecken`, Schleife `lg-linien-schleife` mit `rechteck-fn`) als **Sperrflaechen** aus Reihen/Haupt-/Quergaengen ausgeschnitten (`ssg-poly-band-x`). **Von Hand Gesetztes loest KEINE Neuberechnung aus**: `LG_REIHE`/`LG_GANG` (`lg-manuell-einfuegen`) und das Bearbeiten einer Reihe/eines Gangs (`lg-linie-neu-aufbauen-sperre`) schneiden nur die eigene Flaeche aus den rangniedrigeren Objekten heraus (`lg-sperre-anwenden`, reiner Kern `lg-linie-zuschnitt`; Rang `lg-quelle-rang`: auto < auto_geaendert < manuell) - frueher fuellte ein manueller Gang den ganzen Bereich neu, dabei kamen geloeschte Objekte zurueck und geaenderte wurden ersetzt. **Bereich bearbeiten ueberschreibt nichts**: Nicht-Geometrie-Werte (Belag, Haengeebenen) werden auf alle vorhandenen Kinder uebertragen (`lg-bereich-attribute-uebertragen`), neu berechnet wird nur auf Rueckfrage und mit Vorgabe Ja nur bei Geometrieaenderung (`lg-bereich-geometrie-geaendert-p`). Die Neuberechnung (`lg-bereich-fuellen`) entfernt nur `QUELLE=auto`; ALLE `manuell`/`auto_geaendert`-Objekte des Geschosses (`lg-von-hand-auf-geschoss`) sperren ihre Flaeche - auch in den Randgaengen (`lg-randgang-stuecke`), ein manueller Gang ersetzt den Randgang statt von ihm ueberlagert zu werden. `lg-blockdef` ergaenzt bei Altbestand fehlende ATTDEFs (Neudefinition + `_.ATTSYNC`). `LAGER_EXPORT` schreibt NUR `_lager.json` nach `DXFM_RESULTS` (Format `ssg-lager` Version 2, kein Python-Schritt; das RFQ-Projekt uebernimmt Mapping und Hand-Abgleich): alle Geschosse, Bereiche, Reihen und Gaenge mit Attributen und voller Geometrie (Konturpunkte, Start/Ende/Mitte, 4 Ecken) plus `kennwerte` je Geschoss und `lager` gesamt (lichte Hoehe, Regal-/Schienenlaenge, Haengeebenen, Gaenge je Art und Breite, Belags-/Gitterrostflaeche, Gitterkorb, Regalrohr = Laenge x Haengeebenen), je Geschoss-Kennwert `herkunft` (Formel + Quelle). **`null` = fehlt (offener Punkt), nicht Zutreffendes wird weggelassen** (Marker `'ENTFAELLT`, `lg-json-ohne-entfallend`). Plausibilitaetswarnungen je Geschoss (`lg-plausibilitaet`, Grenzwerte aus dem Regelprofil). Das Reihen-Attribut `SCHIENEN` ist entfallen; `SCHIENEN_JE_GANG` am Bereich (Trolley) wird noch nicht ausgewertet. Eigener JSON-Schreiber `lg-json-*` ueber getaggte Werte `(OBJ ...)`/`(ARR ...)` (`lg-json-text` rueckt ein, Nicht-ASCII als `\uXXXX`), Kennwerte in reinen Kernen (`lg-kennwerte-geschoss`/`-lager`, `lg-haengeebenen-summen`, `lg-gaenge-je-art`, `lg-reihen-offene-kanten`, `lg-reihen-gitterkorb-berechnen`). **Absturzsicherung unter den Reihen** je Bereich (`UNTER_REIHE` = `gitterrost`/`gitterkorb`/`keine`, Vorgabe `lager.json bereich.unter_reihe`, nur auf Buehnen wirksam): Gitterrost zaehlt die Reihengrundflaeche zur Gitterrostflaeche, Gitterkorb liefert Boden (Laenge x Tiefe) und Seitenflaeche = OFFENE Kantenlaenge x `GITTERKORB_HOEHE_mm` (Kanten an einer direkt anliegenden Nachbarreihe desselben Geschosses, z.B. Ruecken an Ruecken, zaehlen nicht; die Korbhoehe soll spaeter aus den Attributen berechnet werden). Regelwerk-Werte stehen noch nicht im Export (`doc/todo.md`). **Darstellung der Absturzsicherung** (`lg-darstellung-geschoss`): auf Buehnen je Reihe eine Schraffur fuer gitterrost bzw. gitterkorb, beim Gitterkorb zusaetzlich die seitliche Abgrenzung als dicke Polylinie NUR an offenen Kanten (reiner Kern `lg-sicherung-geometrie` ueber `lg-kante-offen-intervalle`, gleiche Regel wie der Export). Lose Objekte auf `LG_Sicherung_G` mit XDATA `rolle=sicherung`, bei jedem Neuaufbau komplett ersetzt; automatisch nach Bereich fuellen, Reihe/Gang/Geschoss bearbeiten, `LG_REIHE`/`LG_GANG` und Geschoss kopieren. Farben/Muster aus `lager.json` (`darstellung.reihe.farbe`, `darstellung.unter_reihe.*`, `belag_schraffur..farbe`; ACI oder "r,g,b", `lg-farbe-dxf`/`lg-farbe-setzen`). `LG_DARSTELLUNG` (Menue Lager -> Darstellung aktualisieren) liest lager.json neu, frischt die Definitionen `LG_REIHE_*`/`LG_GANG_*` auf (`lg-darstellung-definitionen`) und zeichnet alle Geschosse neu. Befehl `LG_REGALE` (Menue Lager -> Regale zaehlen) zaehlt die Regalreihen `LG_REIHE_*` (Anzahl, Laenge, Laenge x Haengeebenen je Geschoss aus den Attributen GESCHOSS/LAENGE/HAENGEEBENEN, reine Kerne `lg-reihe-eintrag`/`lg-reihen-auswerten`). **Der parametrische BricsCAD-Block `goh_rack` wird nicht mehr unterstuetzt** (veraltet, 2026-10-30 aus Zaehlung, Export und Geschoss-Kopie entfernt). Vorgaben aus `data/json/lager.json`. Dialoge `dcl/lager.dcl` ueber den generischen Treiber `lg-dialog` (Feldlisten `*lg-felder-*`, Konsolen-Rueckfall `lg-konsole`, ohne GUI Vorgaben). **Geschoss kopieren** (`LG_GESCHOSS_KOPIEREN`, Dialog `lg_geschoss_kopie`, Kern `lg-geschosse-kopieren`): Quelle (Vorgabe oberstes Geschoss, Erweitern G3 -> G4), Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie (0 = deckungsgleich; 3D-Zeichnung zusaetzlich +Z). Kein BricsCAD-KOPIEREN (XDATA-Verweise zeigten aufs Original): jedes Objekt entsteht ueber die normalen Einfuegefunktionen neu (`lg-geschoss-anlegen`, `lg-bereich-block-anlegen` = Bereich ohne Fuellung, `lg-linie-einfuegen`), Kinder 1:1 inkl. `auto_geaendert`/manuell, kein Neufuellen; ALLE Gaenge bekommen den gewaehlten Belag, auch manuelle (`lg-kopie-belag`). Tests: `tu-tests-lager` (test_unit) und `tests/test_lager.lsp` | | `LagerPruefung.lsp` | LG_REGELN | Regelprofile der Lagerplanung: `cfg/stahlbau/regelwerk/.json` laden, `erbt_von` aufloesen (`lg-profil-mischen`), je Regel den STRENGSTEN Wert aller zutreffenden Quellen (`lg-regel-aufloesen`: `art` min/max/wert, `bedingung` gegen einen Kontext - fehlt der Schluessel, konservativ einbezogen -, `tabelle` + `tabellen_schluessel`) mit Protokoll. Jede Pruefregel der Lagerplanung wird genau hier implementiert (Python rendert nur). Alle Startwerte `verifiziert: false` | | `DinFormate.lsp` | DIN_FORMATE | Ersatz fuer das alte ACADSST-Makro `DINFORM.LSP`/`.DCL` (SSG2-Menue "DIN-FORMATE..."): fuegt einen DIN-Rahmen (A0-A3, A4 quer/hoch) und optional einen Schriftkopf (KOPF11, Sprachen DE/EN/FR/IT) per Dialog (`dcl/dinformate.dcl`) ein. Block-DWGs liegen flach in `cfg/rahmen/` (kopiert aus `\\infor-ln\SSG_Tools\ACADSST\KOEPFE` - nur die von DINFORM.LSP tatsaechlich referenzierten Dateien: `A0`/`A1`/`A2`/`A3`/`A4Q`/`A4H.DWG` + `Kopf11`/`Kopf11e`/`Kopf11f`/`Kopf11i.dwg`). Rahmen wird im Zeichenmassstab-Faktor eingefuegt, der Kopf im Faktor/100 (Kopf-DWG ist 100x groesser als die Rahmen-DWGs angelegt) - unveraendert aus dem Original-Massstabsschema uebernommen. Blockdefinitionen werden einmalig per `_.-INSERT ... _X 0` nachgeladen (`dinformate-block-laden`), falls noch nicht in der Zeichnung vorhanden. Lazy geladen ueber `(ssg-ensure "DinFormate")`, Menue: Werkzeuge > Din Formate. | | `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, wie `bbox_columns()`/`planquadrat_column()`/`_bbox_min()` in `lib/export_csv.py` bzw. `lib/export_planquadrat.py`), NICHT aus dem reinen Einfuegepunkt - bei einem gedrehten/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). | diff --git a/Lisp/LagerInsert.lsp b/Lisp/LagerInsert.lsp index a59df1f..a81da2a 100644 --- a/Lisp/LagerInsert.lsp +++ b/Lisp/LagerInsert.lsp @@ -1185,11 +1185,30 @@ ) ) ) + ;; Randgaenge selbst an den AEUSSEREN Sperrflaechen (manuelle/geaenderte + ;; Objekte) beschneiden - ein von Hand gesetzter Gang ersetzt den Randgang + ;; dort, wo er liegt, statt von ihm ueberlagert zu werden. (foreach s rand-str - (setq recs (cons (list "randgang" (cadr s) (caddr s) (cadddr s)) recs))) + (foreach st (lg-randgang-stuecke (cadr s) (caddr s) (cadddr s) (lg-param params "sperr" nil) min-g) + (setq recs (cons (list "randgang" (car st) (cadr st) (cadddr s)) recs)))) (list (reverse recs) (- (- ymax oben) y) module) ) +;; Stuecke eines Randgangs (Mittellinie p1-p2, Breite b) ohne die Flaechen +;; der Sperrpolygone (gleiches System). Reiner Kern ueber lg-linie-zuschnitt. +;; Rueckgabe ((q1 q2) ...); ohne Ueberdeckung ((p1 p2)). +(defun lg-randgang-stuecke (p1 p2 b sperr min-g / a l lok st) + (setq a (angle p1 p2) l (distance p1 p2)) + (setq lok (mapcar '(lambda (poly) + (ssg-poly-drehen (mapcar '(lambda (q) (list (- (car q) (car p1)) (- (cadr q) (cadr p1)))) poly) + (- a) '(0.0 0.0))) + sperr)) + (setq st (if lok (lg-linie-zuschnitt l b lok min-g) 'unveraendert)) + (if (eq st 'unveraendert) + (list (list p1 p2)) + (mapcar '(lambda (i) (list (polar p1 a (car i)) (polar p1 a (cadr i)))) st)) +) + ;; Richtung der Reihen als Vorgabe: Winkel der laengsten Konturkante (Grad, ;; auf [0,180) normiert). (defun lg-kontur-richtung (pts / best w l) @@ -1381,6 +1400,15 @@ (append (lg-alle-bloecke "LG_REIHE_*") (lg-alle-bloecke "LG_GANG_*"))) ) +;; Alle von Hand gesetzten oder geaenderten Reihen/Gaenge (QUELLE manuell +;; oder auto_geaendert) des Geschosses gs (String) - gleich welchem Bereich +;; sie gehoeren und wo ihre Mitte liegt. Sie sind Sperre fuer jede Fuellung. +(defun lg-von-hand-auf-geschoss (gs) + (vl-remove-if-not + '(lambda (e) (and (= (cdr (assoc "GESCHOSS" (ssg-attrib-read e))) gs) (lg-von-hand-p e))) + (append (lg-alle-bloecke "LG_REIHE_*") (lg-alle-bloecke "LG_GANG_*"))) +) + ;; Von Hand gesetzte Reihen/Gaenge (QUELLE manuell) desselben Geschosses, ;; die nicht mit einem Bereich verknuepft sind, aber mit ihrer Mitte in der ;; Kontur pts liegen (z.B. Altbestand von LG_REIHE vor der Verknuepfung). @@ -1402,8 +1430,8 @@ ;; Die automatisch erzeugten Kinder (QUELLE auto) werden vorher entfernt ;; (Sektions-Neuaufbau statt Feld-Splice). Von Hand gesetzte (manuell) und ;; von Hand geaenderte (auto_geaendert) Reihen und Gaenge bleiben stehen und -;; sparen ihre Flaeche aus - ebenso unverknuepfte manuelle Objekte in der -;; Kontur. Rueckgabe: (reihen gaenge restbreite). +;; sparen ihre Flaeche aus - das gilt fuer alle von-Hand-Objekte des +;; Geschosses, auch aus den Randgaengen. Rueckgabe: (reihen gaenge restbreite). (defun lg-bereich-fuellen (bereich-ent / a kontur pts w pts-l erg z gs n-reihen n-gaenge werte-reihe werte-gang p1 p2 behalten) (setq a (ssg-attrib-read bereich-ent) @@ -1433,8 +1461,10 @@ (cons "min_gang" (lg-zahl (lg-vorgabe '("bereich" "min_ganglaenge_mm") 100))) (cons "rand" (lg-bereich-rand a)) (cons "richtung" w) + ;; Sperre: ALLE von Hand gesetzten/geaenderten Objekte des + ;; Geschosses (auch fremder Bereiche, auch nur teilweise in der Kontur) (cons "sperr" (mapcar '(lambda (g) (ssg-poly-drehen (lg-linie-polygon g) (- w) '(0.0 0.0))) - behalten))))) + (lg-von-hand-auf-geschoss (cdr (assoc "GESCHOSS" a)))))))) (setq gs (cdr (assoc "GESCHOSS" a)) n-reihen 0 n-gaenge 0) (setq werte-reihe (list (cons "GESCHOSS" gs) (cons "TIEFE_mm" (cdr (assoc "REGALTIEFE_mm" a))) @@ -1573,11 +1603,13 @@ ;; (dx dy) versetzt; in einer 3D-Zeichnung (Konturen auf Belagshoehe) ;; zusaetzlich in Z um den Hoehenabstand. -;; Reiner Kern: Belag eines kopierten Bereichs/Gangs. Entsprach er dem -;; Geschossbelag der Quelle, folgt er dem neuen Geschossbelag; bewusst -;; abweichende (z.B. Gitterrost-Gang auf Spanplatte-Buehne) bleiben. +;; Reiner Kern: Belag eines kopierten Bereichs/Gangs. Ist fuer die Kopie +;; ein Belag gewaehlt, bekommen ALLE Gaenge und Bereiche ihn - auch von Hand +;; gesetzte Rand-/Sondergaenge (sonst blieb der alte Belag stehen). Ohne +;; Wahl (neu leer) bleibt der Belag. alt (Belag der Quelle) wird nicht mehr +;; ausgewertet, bleibt aber fuer die Aufrufer in der Signatur. (defun lg-kopie-belag (belag alt neu) - (if (and belag alt neu (= (strcase belag) (strcase alt))) neu belag) + (if (and belag neu (= (type neu) 'STR) (/= neu "")) neu belag) ) ;; Reiner Kern: Attributwerte einer kopierten Reihe/eines Gangs fuer @@ -2186,6 +2218,67 @@ (princ) ) +;; ------------------------------------------------------------ +;; Bereich bearbeiten: was eingezeichnet ist, wird aktualisiert, nicht +;; ueberschrieben. Nur eine Geometrieaenderung fuehrt (auf Rueckfrage) zur +;; Neuberechnung der automatisch erzeugten Kinder. +;; ------------------------------------------------------------ + +;; Bereichs-Attribute, deren Aenderung die Lage der Reihen/Gaenge aendert. +(setq *lg-bereich-geometrie-tags* + '("GANGBREITE_mm" "REGALTIEFE_mm" "REIHEN_JE_GANG" "QUERGANG_ABSTAND_mm" "QUERGANG_BREITE_mm" + "RICHTUNG" "RANDGANG_N_mm" "RANDGANG_O_mm" "RANDGANG_S_mm" "RANDGANG_W_mm")) + +;; Reiner Kern: T, wenn sich zwischen alt und neu (Attribut-Alists) ein +;; Geometrie-Wert aendert (numerisch verglichen, leer = 0; fehlt ein Tag in +;; neu, gilt er als unveraendert). +(defun lg-bereich-geometrie-geaendert-p (alt neu) + (if (vl-some '(lambda (tag / x y) + (if (lg-wert-fuer-tag tag neu) + (progn + (setq x (cond ((lg-zahl (lg-wert-fuer-tag tag alt))) (0.0)) + y (cond ((lg-zahl (lg-wert-fuer-tag tag neu))) (0.0))) + (not (equal x y 1e-6))))) + *lg-bereich-geometrie-tags*) + T) +) + +;; Geaenderte Nicht-Geometrie-Werte auf ALLE vorhandenen Kinder des Bereichs +;; uebertragen (QUELLE egal, nichts wird geloescht oder neu gelegt): +;; BELAG -> jeder Gang wird mit dem neuen Belag an derselben Stelle neu +;; aufgebaut (Belag steckt im Blocknamen/der Schraffur), HAENGEEBENEN -> +;; Attribut jeder Reihe. Rueckgabe: Anzahl geaenderter Objekte. +(defun lg-bereich-attribute-uebertragen (ent alt neu / b-alt b-neu e-alt e-neu n ka) + (setq b-alt (lg-wert-fuer-tag "BELAG" alt) b-neu (lg-wert-fuer-tag "BELAG" neu) + e-alt (lg-wert-fuer-tag "HAENGEEBENEN" alt) e-neu (lg-wert-fuer-tag "HAENGEEBENEN" neu) + n 0) + (foreach k (lg-bereich-kinder ent) + (setq ka (ssg-attrib-read k)) + (cond + ((wcmatch (strcase (lg-blockname k)) "LG_GANG_*") + (if (and b-neu (/= b-neu "") (not (equal b-alt b-neu)) + (not (equal (lg-wert-fuer-tag "BELAG" ka) b-neu))) + (if (lg-linie-neu-aufbauen k (lg-wert-setzen ka "BELAG" b-neu)) (setq n (1+ n))))) + ((wcmatch (strcase (lg-blockname k)) "LG_REIHE_*") + (if (and e-neu (not (equal e-alt e-neu))) + (progn (lg-attribute-setzen k (list (cons "HAENGEEBENEN" e-neu))) (setq n (1+ n))))) + ) + ) + n +) + +;; Nach dem Bereichsdialog: Werte uebertragen, bei Geometrieaenderung +;; Rueckfrage "neu berechnen?" (Vorgabe Ja; ohne Aenderung Vorgabe Nein - +;; damit laesst sich ein Bereich auch bewusst neu berechnen). +(defun lg-bereich-aktualisieren (ent alt neu / geo n) + (setq n (lg-bereich-attribute-uebertragen ent alt neu) + geo (lg-bereich-geometrie-geaendert-p alt neu)) + (if (> n 0) (princ (ssg-textf "lg-bereich-uebertragen" (list (itoa n))))) + (if (ssg-in-yesno (ssg-text "lg-frage-neu-berechnen") geo) + (lg-bereich-fuellen ent) + (lg-darstellung-geschoss (atoi (cond ((cdr (assoc "GESCHOSS" (ssg-attrib-read ent)))) ("0"))))) +) + ;; Doppelklick-Ziel (SSG_BLOCKEDIT, Zweig LG_*) und Befehl LG_EDIT. (defun lg-edit-ent (ent / name a neu) (setq name (strcase (lg-blockname ent)) @@ -2205,7 +2298,7 @@ ;; Pfeil des Symbols folgt der Reihenrichtung (vla-put-Rotation (vlax-ename->vla-object ent) (* (/ (cond ((lg-zahl (cdr (assoc "RICHTUNG" neu)))) (0.0)) 180.0) pi)) - (lg-bereich-fuellen ent)))) + (lg-bereich-aktualisieren ent a neu)))) ((wcmatch name "LG_REIHE_*") (if (setq neu (lg-dialog "lg_reihe" *lg-felder-reihe* a nil)) (progn diff --git a/Lisp/README.md b/Lisp/README.md index 74e3270..6b83014 100644 --- a/Lisp/README.md +++ b/Lisp/README.md @@ -879,11 +879,11 @@ Phase 0 + 1 (Mengen, 2D). | --- | --- | | `c:LG_GESCHOSS` | Belagskontur waehlen/zeichnen, Dialog `lg_geschoss` (`dcl/lager.dcl`), Block `LG_GESCHOSS` + XDATA-Verknuepfung zur Kontur | | `c:LG_GESCHOSS_AKTIV` | Aktives Geschoss setzen, Layer `LG_*_G` der anderen Geschosse frieren (0 = alle zeigen) | -| `c:LG_GESCHOSS_KOPIEREN` | Geschoss mit Belagskontur, Bereichen, Reihen und Gaengen 1:1 in ein oder mehrere neue Geschosse kopieren (Dialog `lg_geschoss_kopie`: Quelle - Vorgabe oberstes Geschoss -, Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie, 0 = deckungsgleich). Belaege gleich dem Quell-Geschossbelag folgen dem neuen Belag; Verknuepfungen, Layer `_G` und IDs sind neu | +| `c:LG_GESCHOSS_KOPIEREN` | Geschoss mit Belagskontur, Bereichen, Reihen und Gaengen 1:1 in ein oder mehrere neue Geschosse kopieren (Dialog `lg_geschoss_kopie`: Quelle - Vorgabe oberstes Geschoss -, Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie, 0 = deckungsgleich). Alle Gaenge (auch manuelle) bekommen den gewaehlten Belag; Verknuepfungen, Layer `_G` und IDs sind neu | | `c:LG_BEREICH` | Lagerbereich-Kontur + Dialog `lg_bereich`; Auto-Fuellung mit `LG_REIHE__` / `LG_GANG___`, optional Randgaenge Nord/Ost/Sued/West entlang der Konturkanten (auch L-Form) und Absturzsicherung unter den Reihen (`UNTER_REIHE` gitterrost/gitterkorb/keine, `GITTERKORB_HOEHE_mm`) | | `c:LG_REIHE` | Einzelne Regalreihen per Start/Ende oder getippter Laenge; wie bei `LG_GANG` mit dem Bereich verknuepft und ausgeschnitten statt neu berechnet (`lg-manuell-einfuegen`) | | `c:LG_GANG` | Einzelne Gaenge (Dialog `lg_gang`: Breite, Art gang/randgang/quergang, Belag), mittig auf der Linie - oder ueber die Option `Rechteck` als Flaeche, gezeichnet mit dem BricsCAD-Befehl RECHTECK samt aller seiner Optionen (Abmessungen, Flaeche, Drehung ...; `lg-rechteck-zeichnen`, Auswertung `lg-rechteck-ecken-aus-poly`, Fase/Rundung wird abgelehnt) (Lage und Breite aus dem Rechteck, laengere Seite = Gangrichtung; Kern `lg-gang-aus-ecken`, `lg-gang-rechteck-einfuegen`, `Linie` schaltet zurueck); liegt die Mitte in einem Bereich, wird der Gang mit ihm verknuepft. Der Gang schneidet seine Flaeche aus den automatisch erzeugten Reihen/Gaengen heraus (`lg-sperre-anwenden`), OHNE den Bereich neu zu berechnen - von Hand geloeschte oder geaenderte Objekte bleiben, wie sie sind; bei jeder Neuberechnung bleibt er stehen (Sperrflaeche) | -| `c:LG_EDIT` | Bearbeiten (auch per Doppelklick ueber `SSG_BLOCKEDIT`, Zweig `LG_*`): Bereich -> Neuberechnung (von Hand eingefuegte und geaenderte Reihen/Gaenge bleiben stehen und werden ausgespart), Reihe/Gang -> Neuaufbau, danach Zuschnitt der rangniedrigeren Objekte (`auto` < `auto_geaendert` < `manuell`, `lg-quelle-rang`) statt Neuberechnung des Bereichs | +| `c:LG_EDIT` | Bearbeiten (auch per Doppelklick ueber `SSG_BLOCKEDIT`, Zweig `LG_*`): Bereich -> geaenderter Belag/Haengeebenen werden auf alle vorhandenen Reihen/Gaenge uebertragen, Neuberechnung nur auf Rueckfrage (Vorgabe Ja bei Geometrieaenderung; von Hand eingefuegte und geaenderte Reihen/Gaenge des Geschosses bleiben stehen und werden ausgespart, auch aus den Randgaengen), Reihe/Gang -> Neuaufbau, danach Zuschnitt der rangniedrigeren Objekte (`auto` < `auto_geaendert` < `manuell`, `lg-quelle-rang`) statt Neuberechnung des Bereichs | | `c:LG_REGALE` | Regalreihen `LG_REIHE_*` zaehlen (`lg-reihe-eintrag` + `lg-reihen-auswerten`): Anzahl, Laenge und Laenge x Haengeebenen je Geschoss plus Gesamtsumme in der Befehlszeile | | `c:LG_DARSTELLUNG` | Darstellung aktualisieren: Farben/Schraffuren aus `lager.json` (`darstellung`, `belag_schraffur`) auf die Reihen-/Gang-Definitionen, Absturzsicherung unter den Reihen aller Geschosse neu zeichnen (Gitterrost/Gitterkorb als Schraffur, Gitterkorb-Abgrenzung nur an offenen Kanten, Layer `LG_Sicherung_G`). Geschieht nach Aenderungen automatisch fuer das betroffene Geschoss | | `c:LAGER_EXPORT` | `_lager.json` in `DXFM_RESULTS`: alle Geschosse, Bereiche, Reihen und Gaenge mit Attributen und Geometrie, Kennwerte je Geschoss und gesamt (inkl. Gitterkorb mit offenen Kanten), Herkunft je Kennwert, Warnungen. Kein Python-Schritt | diff --git a/doc/plan/lager_rfq.md b/doc/plan/lager_rfq.md index e9d782f..f0f7ab5 100644 --- a/doc/plan/lager_rfq.md +++ b/doc/plan/lager_rfq.md @@ -145,8 +145,9 @@ Stattdessen entsteht jedes Objekt ueber die normalen Einfuegefunktionen neu: - Reihen und Gaenge 1:1 aus der Quelle (`lg-linie-einfuegen`). Es wird nicht neu gefuellt, damit Handaenderungen (`auto_geaendert`, manuelle Reihen/Gaenge) erhalten bleiben. -- Belaege, die dem Geschossbelag der Quelle entsprachen, folgen dem neuen Belag, - inklusive Schraffur. Bewusst abweichende bleiben. +- Ist im Dialog ein Belag gewaehlt, bekommen ALLE Gaenge und Bereiche des neuen + Geschosses ihn, inklusive Schraffur - auch von Hand gesetzte Rand- und + Sondergaenge (`lg-kopie-belag`). ### Auto-Fuellung (`lg-fuellung-berechnen`) @@ -162,12 +163,25 @@ liegen (konkave Konturen werden korrekt beschnitten). Quergaenge: Luecken der Breite `QUERGANG_BREITE_mm` nach je `QUERGANG_ABSTAND_mm`, ueber alle Reihen ausgerichtet; keine Luecke am Reihenende, wenn dahinter kein Mindeststueck mehr passt. Hauptgaenge laufen durch. Der Rest quer zu den Reihen steht in -`RESTBREITE_mm`. Edit des Bereichs = Neuaufbau aller Kinder (Sektions-Neuaufbau, -kein Feld-Splice) - aber nur der automatisch erzeugten (`QUELLE=auto`). Von Hand -eingefuegte (`manuell`) und von Hand geaenderte (`auto_geaendert`) Reihen und Gaenge -bleiben stehen und werden ausgespart, ebenso unverknuepfte manuelle Objekte, deren -Mitte in der Kontur liegt; die Anzahl wird gemeldet. Von Hand geloeschte -automatische Objekte entstehen bei dieser ausdruecklichen Neuberechnung neu. +`RESTBREITE_mm`. + +**Bereich bearbeiten: aktualisieren statt ueberschreiben.** Was eingezeichnet ist, +bleibt stehen. Geaenderte Werte ohne Geometrie werden auf ALLE vorhandenen Kinder +uebertragen (`lg-bereich-attribute-uebertragen`): `BELAG` -> jeder Gang wird mit dem +neuen Belag an derselben Stelle neu aufgebaut, `HAENGEEBENEN` -> Attribut jeder +Reihe. Nur wenn sich ein Geometrie-Wert aendert (`lg-bereich-geometrie-geaendert-p`: +Gangbreite, Regaltiefe, Reihen je Gang, Quergaenge, Richtung, Randgang-Breiten), +fragt der Befehl "Bereich neu berechnen?" (Vorgabe Ja; ohne Aenderung Vorgabe +Nein - so laesst sich ein Bereich auch bewusst neu berechnen). + +Die Neuberechnung (`lg-bereich-fuellen`) ersetzt nur die automatisch erzeugten +Kinder (`QUELLE=auto`). Sperre sind ALLE von Hand eingefuegten (`manuell`) und +geaenderten (`auto_geaendert`) Reihen und Gaenge des Geschosses +(`lg-von-hand-auf-geschoss`) - auch solche eines anderen Bereichs oder nur +teilweise in der Kontur. Sie werden aus Reihen, Haupt-, Quer- UND Randgaengen +ausgeschnitten (`lg-randgang-stuecke`): ein manueller Gang ersetzt den Randgang +dort, wo er liegt, statt von ihm ueberlagert zu werden. Von Hand geloeschte +automatische Objekte entstehen bei der Neuberechnung neu. **Randgaenge** (`RANDGANG_N/O/S/W_mm`, Nord = +Y Welt): jede Konturkante bekommt den Randgang ihrer Himmelsrichtung (Aussennormale, auf N/O/S/W eingerastet, diff --git a/lang/de_DE.json b/lang/de_DE.json index 079c758..0ec3e81 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -1280,6 +1280,8 @@ "lg-geaendert": "\nGeaendert.", "lg-bereich-ohne-kontur": "\n[Lager] Die Kontur des Bereichs fehlt - Fuellung nicht moeglich.", "lg-bereich-beibehalten": "\nBereich %2: %1 von Hand eingefuegte oder geaenderte Reihe(n)/Gaenge bleiben erhalten und werden ausgespart.", + "lg-bereich-uebertragen": "\n[Lager] %1 vorhandene Reihe(n)/Gang/Gaenge an die geaenderten Werte angepasst.", + "lg-frage-neu-berechnen": "\nBereich neu berechnen? (automatisch erzeugte Reihen/Gaenge neu legen; von Hand gesetzte und geaenderte bleiben)", "lg-bereich-gefuellt": "\nBereich %1: %2 Regalreihe(n), %3 Gang-/Quergangflaeche(n), Restbreite %4 mm.", "lg-prompt-reihe-start": "\nStartpunkt der Regalreihe : ", "lg-prompt-reihe-ende": "\nEndpunkt oder Laenge (mm): ", diff --git a/lang/en_GB.json b/lang/en_GB.json index 7926687..f8d1c9d 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -1280,6 +1280,8 @@ "lg-geaendert": "\nChanged.", "lg-bereich-ohne-kontur": "\n[Storage] The contour of the area is missing - cannot fill.", "lg-bereich-beibehalten": "\nArea %2: %1 manually inserted or changed row(s)/aisle(s) are kept and left out.", + "lg-bereich-uebertragen": "\n[Storage] %1 existing row(s)/aisle(s) updated to the changed values.", + "lg-frage-neu-berechnen": "\nRecalculate area? (re-create automatically generated rows/aisles; manually placed and modified ones stay)", "lg-bereich-gefuellt": "\nArea %1: %2 rack row(s), %3 aisle/cross aisle area(s), remaining width %4 mm.", "lg-prompt-reihe-start": "\nStart point of the rack row : ", "lg-prompt-reihe-ende": "\nEnd point or length (mm): ", diff --git a/tests/test_lager.lsp b/tests/test_lager.lsp index e6f97dd..78f830c 100644 --- a/tests/test_lager.lsp +++ b/tests/test_lager.lsp @@ -100,7 +100,7 @@ (vl-some '(lambda (g) (if (= (ssg-json-get g "nr") nr) g)) (ssg-json-get daten "geschosse"))) (defun lager:tests ( / k0 k1 g0 g1 kb bereich reihen gaenge r0 neu raw daten pfad f eg0 eg1 kr sl - kb2 b2 kinder mg mg2 hg alt eb + kb2 b2 kinder mg mg2 hg alt eb mr ba r1 gg1 n-alt g2 r2 gg2 ids b2k n-kinder bb rg rk zl b3 rh3 mg3 ge3 ag3 n3 mr3 alt3) (setvar "CMDECHO" 0) @@ -244,6 +244,39 @@ (setq mg2 (lg-linie-neu-aufbauen mg (lg-wert-setzen (ssg-attrib-read mg) "BELAG" "spanplatte"))) (tlg-eq "manuell: belag geaendert" "LG_GANG_4000_1000_SPANPLATTE" (if mg2 (cdr (assoc 2 (entget mg2))))) (tlg-eq "manuell: bleibt kind" T (if (member mg2 (lg-bereich-kinder b2)) T nil)) + ;; Manueller Gang IM Nord-Randgang: Neuberechnung legt keinen Randgang darueber + (setq mr (lg-gang-einfuegen '(21000.0 5500.0 0.0) '(23000.0 5500.0 0.0) + (list (cons "GESCHOSS" "1") (cons "BREITE_mm" "1000") (cons "ART" "randgang") + (cons "BELAG" "gitterrost")))) + (lg-bereich-fuellen b2) + (tlg-eq "randgang: manueller bleibt" T (if (entget mr) T nil)) + (tlg-eq "randgang: kein ueberlappender auto-randgang" nil + (vl-some '(lambda (k / bb) + (setq bb (ssg-poly-bbox (lg-linie-polygon k))) + (if (and (= (tlg-attr k "QUELLE") "auto") + (> (- (min (car (cadr bb)) 23000.0) (max (car (car bb)) 21000.0)) 1.0) + (> (- (min (cadr (cadr bb)) 6000.0) (max (cadr (car bb)) 5000.0)) 1.0)) + k)) + (tlg-nur (lg-bereich-kinder b2) "LG_GANG_*"))) + (tlg-eq "randgang: nord ausgespart (2 stuecke + sued)" 3 + (tlg-anzahl-art (vl-remove-if-not '(lambda (k) (= (tlg-attr k "QUELLE") "auto")) (lg-bereich-kinder b2)) + "randgang")) + ;; Belag des Bereichs aendern: vorhandene Gaenge werden umgestellt, nichts neu gelegt + (setq ba (ssg-attrib-read b2) n-kinder (length (lg-bereich-kinder b2))) + (tlg-eq "uebertragen: geaendert" T + (> (lg-bereich-attribute-uebertragen b2 ba (lg-wert-setzen ba "BELAG" "gitterrost")) 0)) + (setq kinder (lg-bereich-kinder b2)) + (tlg-eq "uebertragen: anzahl gleich" n-kinder (length kinder)) + (tlg-eq "uebertragen: alle gitterrost" (length (tlg-nur kinder "LG_GANG_*")) + (tlg-anzahl-attr (tlg-nur kinder "LG_GANG_*") "BELAG" "gitterrost")) + (tlg-eq "uebertragen: manuell bleibt manuell" 2 (tlg-anzahl-attr kinder "QUELLE" "manuell")) + ;; Zustand fuer die folgenden Tests wiederherstellen + (lg-bereich-attribute-uebertragen b2 (lg-wert-setzen ba "BELAG" "gitterrost") ba) + (tlg-eq "uebertragen: zurueck auf spanplatte" 0 + (tlg-anzahl-attr (tlg-nur (lg-bereich-kinder b2) "LG_GANG_*") "BELAG" "gitterrost")) + (entdel (vl-some '(lambda (k) (if (wcmatch (cdr (assoc 2 (entget k))) "LG_GANG_2000_1000_*") k)) + (lg-bereich-kinder b2))) + (lg-bereich-fuellen b2) ;; Hallenboden: keine Schraffur; ausserhalb jedes Bereichs: nicht verknuepft (setq hg (lg-gang-einfuegen '(40000.0 0.0 0.0) '(42000.0 0.0 0.0) (list (cons "GESCHOSS" "1") (cons "BREITE_mm" "1500") (cons "ART" "gang") @@ -362,12 +395,14 @@ (setq nr (tlg-attr b "NR")) (vl-every '(lambda (k) (= (tlg-attr k "BEREICH") nr)) (lg-bereich-kinder b))) (tlg-gs "LG_BEREICH" 2))) - ;; Belag folgt dem Geschoss (Spanplatte -> Gitterrost), Hallenboden-Gang bleibt + ;; ALLE Gaenge bekommen den gewaehlten Belag (Gitterrost) - auch manuelle + ;; und der Hallenboden-Gang (tlg-eq "kopie: belag folgt" 0 (tlg-anzahl-attr gg2 "BELAG" "spanplatte")) + (tlg-eq "kopie: alle gaenge neuer belag" (length gg2) (tlg-anzahl-attr gg2 "BELAG" "gitterrost")) (tlg-eq "kopie: manueller gang gitterrost" 1 (length (tlg-nur (vl-remove-if-not '(lambda (e) (= (tlg-attr e "QUELLE") "manuell")) gg2) "LG_GANG_4000_1000_GITTERROST"))) - (tlg-eq "kopie: hallenboden bleibt" 1 (length (tlg-nur gg2 "LG_GANG_2000_1500_HALLENBODEN"))) + (tlg-eq "kopie: hallenboden-gang folgt" 1 (length (tlg-nur gg2 "LG_GANG_2000_1500_GITTERROST"))) (tlg-eq "kopie: bereich belag" T (vl-every '(lambda (b) (= (tlg-attr b "BELAG") "gitterrost")) (tlg-gs "LG_BEREICH" 2))) (tlg-eq "kopie: unter_reihe gitterkorb" 1 (tlg-anzahl-attr (tlg-gs "LG_BEREICH" 2) "UNTER_REIHE" "gitterkorb")) diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index d4c06f5..0641c41 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -3312,9 +3312,38 @@ ;; --- Geschoss kopieren --- (setq _kand2 (list (list 1 0.0 _sq0) (list 2 3100.0 _sq0))) + ;; Kopie: ALLE Gaenge bekommen den gewaehlten Belag, auch abweichende (tu-eq "lg-kopie-belag/folgt" "spanplatte" (lg-kopie-belag "hallenboden" "hallenboden" "spanplatte")) - (tu-eq "lg-kopie-belag/eigen" "gitterrost" (lg-kopie-belag "gitterrost" "hallenboden" "spanplatte")) + (tu-eq "lg-kopie-belag/eigen" "spanplatte" (lg-kopie-belag "gitterrost" "hallenboden" "spanplatte")) + (tu-eq "lg-kopie-belag/ohne-wahl" "gitterrost" (lg-kopie-belag "gitterrost" "hallenboden" "")) (tu-nil "lg-kopie-belag/nil" (lg-kopie-belag nil "hallenboden" "spanplatte")) + ;; Randgang an Sperrflaechen (manueller Gang) beschnitten + (tu-eqf "lg-randgang-stuecke/geteilt" + '(((0.0 5800.0) (4000.0 5800.0)) ((5000.0 5800.0) (10000.0 5800.0))) + (lg-randgang-stuecke '(0.0 5800.0) '(10000.0 5800.0) 400.0 + '(((4000.0 5500.0) (5000.0 5500.0) (5000.0 6000.0) (4000.0 6000.0))) 100.0)) + (tu-eqf "lg-randgang-stuecke/frei" '(((0.0 5800.0) (10000.0 5800.0))) + (lg-randgang-stuecke '(0.0 5800.0) '(10000.0 5800.0) 400.0 nil 100.0)) + (tu-nil "lg-randgang-stuecke/ganz-ueberdeckt" + (lg-randgang-stuecke '(0.0 5800.0) '(10000.0 5800.0) 400.0 + '(((-10.0 5500.0) (10010.0 5500.0) (10010.0 6000.0) (-10.0 6000.0))) 100.0)) + (setq _fr (lg-fuellung-berechnen '((0.0 0.0) (10000.0 0.0) (10000.0 6000.0) (0.0 6000.0)) + (list (cons "rand" '(("N" . 1000.0) ("O" . 0.0) ("S" . 0.0) ("W" . 0.0))) + (cons "sperr" '(((4000.0 5000.0) (5000.0 5000.0) (5000.0 6000.0) (4000.0 6000.0))))))) + (tu-eq "lg-fuellung/randgang-ausgespart" 2 + (length (vl-remove-if-not '(lambda (r) (= (car r) "randgang")) (car _fr)))) + ;; Bereich bearbeiten: nur Geometrie fuehrt zur Neuberechnung + (setq _ba '(("GANGBREITE_mm" . "1000") ("REGALTIEFE_mm" . "700") ("BELAG" . "spanplatte") + ("HAENGEEBENEN" . "2300/1200") ("RANDGANG_N_mm" . "0"))) + (tu-nil "lg-bereich-geometrie/belag" + (lg-bereich-geometrie-geaendert-p _ba (lg-wert-setzen (lg-wert-setzen _ba "BELAG" "gitterrost") + "HAENGEEBENEN" "2400/1000"))) + (tu-nil "lg-bereich-geometrie/gleich-zahl" + (lg-bereich-geometrie-geaendert-p _ba (lg-wert-setzen _ba "GANGBREITE_mm" "1000.0"))) + (tu-eq "lg-bereich-geometrie/gangbreite" T + (lg-bereich-geometrie-geaendert-p _ba (lg-wert-setzen _ba "GANGBREITE_mm" "1200"))) + (tu-eq "lg-bereich-geometrie/randgang" T + (lg-bereich-geometrie-geaendert-p _ba (lg-wert-setzen _ba "RANDGANG_N_mm" "1600"))) (setq _w (lg-kopie-werte '(("ID" . "2A") ("GESCHOSS" . "1") ("LAYER" . "LG_Gang_G1") ("LAENGE" . "9000") ("BREITE_mm" . "1000") ("ART" . "gang") ("BELAG" . "hallenboden") ("BEREICH" . "3") ("QUELLE" . "manuell"))