From 6ec85905d4542d68a0a29029eab9cad7708ae3e4 Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Fri, 2 Oct 2026 14:46:12 +0200 Subject: [PATCH] =?UTF-8?q?[ADD]=20Ger=C3=BCst=20Tr=C3=A4ger=20k=C3=B6nnen?= =?UTF-8?q?=20nun=20=C3=BCber=20das=20Men=C3=BC=20in=20Bricscad=20eingef?= =?UTF-8?q?=C3=BCgt=20werden=20wie=20durch=20die=20Bihler=20Bibliothek;=20?= =?UTF-8?q?Beschriftungsausrichtung=20nach=20Norm=20korrigiert;=20Nachtr?= =?UTF-8?q?=C3=A4gliche=20Beschriftung=20=C3=BCber=20Makro=20f=C3=BCr=20al?= =?UTF-8?q?le=20platzierten=20Tr=C3=A4ger=20m=C3=B6glich?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 5 +- Lisp/GeruestInsert.lsp | 965 +++++++++++++++++++++++++++-- data/geruest/R2-Saeule_dummy.dwg | Bin 43832 -> 40311 bytes data/geruest/dummy_Muffe_1zoll.dwg | Bin 0 -> 17473 bytes data/geruest/dummy_Strebe_30x4.dwg | Bin 0 -> 17446 bytes data/json/geruest.json | 202 +++++- dcl/geruest_traeger.dcl | 39 ++ lang/de_DE.json | 19 +- lang/en_GB.json | 19 +- lib/export_csv.py | 19 +- menu/SSG_LIB.cui | 216 ++++--- menu/SSG_LIB.mnu | 6 +- tests/test_unit.lsp | 254 +++++++- 13 files changed, 1564 insertions(+), 180 deletions(-) create mode 100644 data/geruest/dummy_Muffe_1zoll.dwg create mode 100644 data/geruest/dummy_Strebe_30x4.dwg create mode 100644 dcl/geruest_traeger.dcl diff --git a/CLAUDE.md b/CLAUDE.md index ab00d03..2cb453f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,7 +75,7 @@ Die `start_briscad.bat` ruft `setenv.bat` auf und startet BricsCAD. | VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge | | Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | | Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_LADESCHLEIFE_*, OMNI_EDIT | -| Geruest | `GeruestInsert` | GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG (derselbe Dialog, im jeweiligen Bereich geoeffnet; Rohr-Geruest-Traeger/Gattertraeger noch Platzhalter auf GERUEST) | +| Geruest | `GeruestInsert` | GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG (derselbe Dialog, im jeweiligen Bereich geoeffnet), GERUEST_TRAEGER (eigener Dialog), GERUEST_TRAEGER_BESCHRIFTEN (Untermenue Geruest -> Funktionen -> Traeger beschriften; alle Traeger der Zeichnung neu beschriften); Gattertraeger noch Platzhalter auf GERUEST | | Connections | `Connection_Edit` + `SSG_LIB_Commands` | CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT | | Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON | | Konfiguration | `SSG_LIB_Commands` + `DrawPlanquadrate` | SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) | @@ -107,7 +107,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_LADESCHLEIFE_AP110, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Ladeschleifen (`data/json/omniflo_ladeschleifen.json`, per `ssg-json-laden`, da die Beschreibungen Kommas enthalten; Blockdatei darf `.dwg` statt `.dxf` sein), Edit-Dialog | | `SSG_LIB_Commands.lsp` | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (`ils-insert-station`: wiederholtes Einfuegen von `BTMT-Beladung` bzw. `SC_Entladung`, Drehung interaktiv, Layer aus `LAYER`-Attribut, Dimension via `ssg-ils-block-laden`) + Sensoren (`ils-insert-sensor`) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber `ils-insert-station` mit den Rohnamen `Eingang`/`Ausgang`, Bloecke `Eingang_.dwg`/`Ausgang_.dwg` in data/ils) + Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | | `Tro_Edit.lsp` | TRO_INSERT, TRO_EDIT | TRO-Marker (Block `TRO_SYM_`) fuer sps_skel (lib/tro_annotate.py): TRO_INSERT fuegt eine bestehende Blockdefinition an einem gewaehlten Punkt ein und setzt ID/TYPE (bricht ab, wenn der Typ noch keine Blockdefinition in der Zeichnung hat - die entsteht nur ueber tro_annotate.py); TRO_EDIT aendert ID/TYPE eines bestehenden Markers per Dialog. Typliste aus `tro_types.lsp` (von `tro_annotate.py --emit-lisp` nach DXFM_LISP geschrieben). Der Dispatcher SSG_BLOCKEDIT leitet Blocknamen `TRO_*` zu TRO_EDIT. | -| `GeruestInsert.lsp` | GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. **Ein Dialog fuer alle Bereiche**: oben ein Dropdown `bereich` (Rohrabhaenger / Saeulen / Wandbefestigung, Reihenfolge = Dateireihenfolge, Anzeigename aus dem ASCII-Feld `titel`). DCL hat **kein Reiter-Tile**, und da sich die Teilezeilen je Bereich unterscheiden, schliesst ein Bereichswechsel den Dialog (`done_dialog 4`) und die Schleife in `c:GERUEST` baut ihn neu auf; der Stand jedes Bereichs bleibt in `*geruest-zustand*` erhalten, ein versehentlicher Wechsel verwirft also nichts. 1"- und 2"-Saeulen liegen in EINER Baugruppe `Saeulen` mit gemeinsamer Anbauteil-Liste; **`teileart` und `dummy` duerfen je Geometrie-Variante ueberschrieben werden** (`geruest-variante-wert`, Variante gewinnt vor Baugruppe) - so liefert `R 1"` weiterhin TEILEART `Geruest Saeule 1 Zoll` und die `R 2"`-Varianten `Geruest Saeule 2 Zoll`, die Groessen bleiben im Export also unterscheidbar. **Die Dialogdefinition wird zur Laufzeit erzeugt** (`geruest-dcl-schreiben` -> temporaere Datei per `vl-filename-mktemp`, nach `unload_dialog` wieder geloescht) - es gibt KEINE `dcl/geruest.dcl`. Grund: je Anbauteil wird ein eigenes Mengenfeld gebraucht, und eine DCL-Liste (`list_box`) kann keine Eingabefelder enthalten; DCL kann weder Dateien einbinden noch Tiles zur Laufzeit erzeugen. Inhalt: Basis-Variante aus `geometrie_varianten` (Dropdown), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife), Farbe (`attribute.Teilefarbe`, Freitext bei `frei_option`) und **zwei Befestigungslisten** ("oben"/"unten"), die BEIDE alle `anbaugruppen` enthalten - dasselbe Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen, je mit eigener Stueckzahl (vorbelegt aus `menge`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt, Baugruppen ohne Anbauteile bekommen gar keine Spalten (ein leerer DCL-Cluster ist unzulaessig). Im generierten Label wird `"` zu `''` ersetzt (`geruest-dcl-label`) - fuenf Artikelnamen enthalten ein Anfuehrungszeichen, das das DCL-Label sonst vorzeitig beendet und die Datei zerstoert. **Konfigurationen**: gepflegte Bibliothek je Baugruppe unter `konfigurationen` in `geruest.json` (schreibgeschuetzt) plus eigene in `cfg/geruest_user.json` (gitignored, `geruest-user-schreiben`; getrennte Datei, damit Benutzerdaten und versionierte Bibliothek nicht kollidieren). Eigene Eintraege sind in der Liste mit `*` markiert und nur diese loeschbar. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber `done_dialog 2`/`3` (Dialog schliessen, Aktion, neu zeigen) - `getstring` funktioniert aus einem `action_tile`-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. **Teile-Geometrie**: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die `data/geruest/.dwg` existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (`geruest-teile-dwgs`, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. **TEILEFARBE** ist eine kommagetrennte Liste parallel zu ARTINR (z.B. `,,RAL7030,,RAL7030`): Teile mit `"farbig": true` in `geruest.json` (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (`geruest-teile-paare`/`-paare-n`, `geruest-farbliste`), sind also garantiert gleich lang. `export_sivas.py` schreibt daraus je Artikelnummer das Merkmal `{"108": ""}` in `details` (Nummer aus `cfg/export.cfg` `[Sivas] merkmal_teilefarbe`). Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie die gewaehlte Stueckzahl es vorgibt - erst Basis, dann Befestigung oben, dann unten; bei einer benannten Gruppe (`teile`) wird das ganze Set n-mal wiederholt, die Mengen INNERHALB des Sets bleiben dabei erhalten (`geruest-artikelnummern-n`). BEZEICHNUNG nennt Position und Menge je Teil ("oben 2x ...") und verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | +| `GeruestInsert.lsp` | GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG, GERUEST_TRAEGER, GERUEST_TRAEGER_BESCHRIFTEN | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. **Ein Dialog fuer alle Bereiche**: oben ein Dropdown `bereich` (Rohrabhaenger / Saeulen / Wandbefestigung, Reihenfolge = Dateireihenfolge, Anzeigename aus dem ASCII-Feld `titel`). DCL hat **kein Reiter-Tile**, und da sich die Teilezeilen je Bereich unterscheiden, schliesst ein Bereichswechsel den Dialog (`done_dialog 4`) und die Schleife in `c:GERUEST` baut ihn neu auf; der Stand jedes Bereichs bleibt in `*geruest-zustand*` erhalten, ein versehentlicher Wechsel verwirft also nichts. 1"- und 2"-Saeulen liegen in EINER Baugruppe `Saeulen` mit gemeinsamer Anbauteil-Liste; **`teileart` und `dummy` duerfen je Geometrie-Variante ueberschrieben werden** (`geruest-variante-wert`, Variante gewinnt vor Baugruppe) - so liefert `R 1"` weiterhin TEILEART `Geruest Saeule 1 Zoll` und die `R 2"`-Varianten `Geruest Saeule 2 Zoll`, die Groessen bleiben im Export also unterscheidbar. **Die Dialogdefinition wird zur Laufzeit erzeugt** (`geruest-dcl-schreiben` -> temporaere Datei per `vl-filename-mktemp`, nach `unload_dialog` wieder geloescht) - es gibt KEINE `dcl/geruest.dcl`. Grund: je Anbauteil wird ein eigenes Mengenfeld gebraucht, und eine DCL-Liste (`list_box`) kann keine Eingabefelder enthalten; DCL kann weder Dateien einbinden noch Tiles zur Laufzeit erzeugen. Inhalt: Basis-Variante aus `geometrie_varianten` (Dropdown), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife), Farbe (`attribute.Teilefarbe`, Freitext bei `frei_option`) und **zwei Befestigungslisten** ("oben"/"unten"), die BEIDE alle `anbaugruppen` enthalten - dasselbe Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen, je mit eigener Stueckzahl (vorbelegt aus `menge`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt, Baugruppen ohne Anbauteile bekommen gar keine Spalten (ein leerer DCL-Cluster ist unzulaessig). Im generierten Label wird `"` zu `''` ersetzt (`geruest-dcl-label`) - fuenf Artikelnamen enthalten ein Anfuehrungszeichen, das das DCL-Label sonst vorzeitig beendet und die Datei zerstoert. **Konfigurationen**: gepflegte Bibliothek je Baugruppe unter `konfigurationen` in `geruest.json` (schreibgeschuetzt) plus eigene in `cfg/geruest_user.json` (gitignored, `geruest-user-schreiben`; getrennte Datei, damit Benutzerdaten und versionierte Bibliothek nicht kollidieren). Eigene Eintraege sind in der Liste mit `*` markiert und nur diese loeschbar. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber `done_dialog 2`/`3` (Dialog schliessen, Aktion, neu zeigen) - `getstring` funktioniert aus einem `action_tile`-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. **Teile-Geometrie**: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die `data/geruest/.dwg` existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (`geruest-teile-dwgs`, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. **TEILEFARBE** ist eine kommagetrennte Liste parallel zu ARTINR (z.B. `,,RAL7030,,RAL7030`): Teile mit `"farbig": true` in `geruest.json` (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (`geruest-teile-paare`/`-paare-n`, `geruest-farbliste`), sind also garantiert gleich lang. `export_sivas.py` schreibt daraus je Artikelnummer das Merkmal `{"108": ""}` in `details` (Nummer aus `cfg/export.cfg` `[Sivas] merkmal_teilefarbe`). Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie die gewaehlte Stueckzahl es vorgibt - erst Basis, dann Befestigung oben, dann unten; bei einer benannten Gruppe (`teile`) wird das ganze Set n-mal wiederholt, die Mengen INNERHALB des Sets bleiben dabei erhalten (`geruest-artikelnummern-n`). BEZEICHNUNG nennt Position und Menge je Teil ("oben 2x ...") und verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. **Attribut-Keys in `geruest.json` sind GROSS** (`LAENGE`, `TEILEFARBE`, ...) = die Block-Attribut-Tags; `ssg-json-get` unterscheidet Gross/Klein. **Rohr-Geruest-Traeger** (`GERUEST_TRAEGER`, Baugruppe mit `"einfuegeart": "linie"` - fehlt darum im Bereichs-Dropdown von GERUEST, gefunden ueber `geruest-baugruppe`): eigener fester Dialog `dcl/geruest_traeger.dcl` (Variante, Farbe, je eine Checkbox + Feld fuer `LAENGENABZUGANFANG`/`LAENGENABZUGENDE`, Vorgabe `vorgabe`/`aktiv` aus dem attribute-Block, je Variante ueberschreibbar via `geruest-attr-feld`); danach Einfuege-Schleife bis ESC/Enter: Startpunkt, dann Endpunkt ODER getippte Laenge (+ Richtung). Aktive Abzuege ruecken die Linie beidseitig ein (Strecke 1000, Abzug Anfang 20/Ende 35 -> Start 20 mm hinter dem Klick, Linie 945 mm); `LAENGE` = Netto-Laenge, gegen `LAENGE.min/max` geprueft. KEINE Dummy-DWG: die Geometrie ist eine per `entmake` erzeugte LINE (0,0,0)-(L,0,0) auf dem `layer` der Variante, eingefuegt mit dem Richtungswinkel (ANGBASE/ANGDIR/AUNITS dafuer kurz auf 0). **Querstreben + Muffe** als verschachtelte Dummy-DWGs im selben Block, reine Grafik ohne Export: `querstrebe.dummy` (`data/geruest/dummy_Strebe_30x4.dwg`, Basispunkt Strebenmitte, 30 mm laengs X) an jeder Strebenposition; fehlt die DWG, Rueckfall auf je eine 25-mm-Querlinie an Strebenanfang/-ende (`laenge`/`querlinie`, Standard 30/25 nur im Code). `muffe.dummy` (`dummy_Muffe_1zoll.dwg`, Basispunkt Muffenmitte) am Linienende, NUR bei Varianten mit ausdruecklichem `"mit_muffe": true` (nicht aus dem Namen geraten; bei "mit 2 Muffen" trotzdem nur eine am Ende); `querstrebe`/`muffe` per Variante ueberschreibbar (`geruest-variante-wert`). Dummys werden ohne Drehung eingefuegt (Traeger laeuft im Block entlang +X), ab dem zweiten Mal per Blockname (`geruest-dummy-einfuegen`). Verteilungs-Konfiguration `querstrebe` (`abstand_max`/`rand_min`/`rand_max`, Variante vor Baugruppe), Verteilung `geruest-traeger-streben`: so wenige Streben wie moeglich, beide Raender gleich; Abstand GENAU `abstand_max`, solange der Rand in `rand_min`..`rand_max` bleibt (1300 -> 150|500|500|150), sonst Rand = `rand_min` und Abstand etwas kleiner (1100 -> 100|450|450|100, mit fest 500 geht die Laenge nicht auf); Netto <= 2*rand_max -> eine Strebe mittig, < 2*rand_min -> keine. Schema `*geruest-traeger-attrib-defs*` = `*geruest-attrib-defs*` + die zwei Abzug-Tags; gemeinsamer Block-Schluss mit den anderen Bereichen in `geruest-block-abschliessen`. Reine Berechnung (`geruest-traeger-laenge`/`-start`/`-pruefen`, `geruest-mm-zahl`) in `tests/test_unit.lsp`. **Traeger-Beschriftung** (Checkbox im Dialog, Standard an; z.B. `RT 80/80 L=2650`): EIGENES TEXT-Objekt neben dem Block, abgeleitet NUR aus dem fertigen Block (Einfuegepunkt, DXF 50, Attribute `LAENGE` + `VARIANTE`) - darum nutzen Einfuegen und `GERUEST_TRAEGER_BESCHRIFTEN` dieselbe Funktion `geruest-traeger-beschriften`, und ein von Hand verschobener/gedrehter Traeger wird beim Neubeschriften korrekt erfasst. Verknuepfung Text -> Block per XDATA `SSG_GR_BESCHR` (1005 = Block-Handle): Neubeschriften ersetzt statt verdoppelt, der Sammelbefehl entfernt verwaiste Texte. JSON `beschriftung` (je FELD Variante vor Baugruppe, `geruest-obj-feld`): Inhalt `format` (`%1` Text, `%2` Laenge auf ganze mm) + `text` (fehlt -> Variantenname) und `layer` (je Variante verschieden, darum in JEDER Variante gepflegt - keine Baugruppen-Vorgabe; fehlt er doch, aktueller Layer); Formatierung NUR `stil`/`ausrichtung`/`hoehe` - alles andere regelt der Textstil ausserhalb, es wird kein Stil angelegt (fehlt er: Meldung, aktueller TEXTSTYLE). `ausrichtung` = Codes von TEXT "Ausrichten" (T/M/B + L/C/R), Bezugspunkt auf der Traegerlinie, L/R in LESErichtung. Textwinkel entlang des Traegers, DIN: fuer 90 < w <= 270 Grad um 180 gedreht (`geruest-text-winkel`). Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | | `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). | @@ -124,6 +124,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) | | `tro_insert.dcl` | TRO_INSERT (Tro_Edit.lsp) | | `tro_edit.dcl` | TRO_EDIT (Tro_Edit.lsp) | +| `geruest_traeger.dcl` | GERUEST_TRAEGER (GeruestInsert.lsp, `geruest-traeger-dialog`) | | `vfl_edit.dcl` | Linienzug-Editieren/Fortsetzen (vf_linienzug.lsp, `vfl-dlg-position`, aufgerufen aus `vfl-edit-ent`) | | `vf_linienzug_wizard.dcl` | Linienzug-Assistent (Modus 1, 2 UND 3): zwei generische Einzel-Dialoge (Zahl-Eingabe, Mehrfachauswahl; `vflw-zahl`/`vflw-wahl`, aufgerufen aus `vfl-in-real`/`vfl-in-string`/`vfl-in-int` bei aktivem `*vfl-wizard-mode*`) PLUS sieben Gruppen-Dialoge, die mehrere immer unmittelbar aufeinanderfolgende Fragen in einem Bildschirm zusammenfassen (Kettenstart, AS-Element, GF-Bogen, ES-Element, Vario-Kurve, Gefaelle festlegen, Ziel-Hoehe, Horizontales Stueck - inkl. Punktwahl-Button mit X/Y/Z-Anzeige; `vflw-gruppe-*-impl` in vf_linienzug.lsp fuellen dafuer vorab eine kleine Pending-Queue `*vflw-pending*`, die einzelnen `vfl-in-*`-Aufrufe bleiben dabei unveraendert) PLUS drei Segment-Gruppen-Dialoge fuer die segmentweise Klassifizierung in Modus 2/3 (`vflw_seg_linie_m2` = Typ+GF-Neigung, `vflw_seg_linie_m3` = 4-fach-Typ+kontextabh. Wertfeld, `vflw_seg_bogen` = Typ+Variante; `vflw-seg-*-impl`). Beide Modi heben vor der Frage das aktuelle Pfad-Segment (LINE/ARC) per `vfl-seg-highlight` (`redraw`) in der Zeichnung hervor und zeigen "Segment i/n ..." im Dialogkopf. Details: `doc/variofoerderer.md` | diff --git a/Lisp/GeruestInsert.lsp b/Lisp/GeruestInsert.lsp index 436fe2f..1788ed1 100644 --- a/Lisp/GeruestInsert.lsp +++ b/Lisp/GeruestInsert.lsp @@ -67,6 +67,24 @@ ) ) +;; Rohr-Geruest-Traeger: dasselbe Schema plus die beiden Laengenabzuege und +;; VARIANTE (Name der Geometrie-Variante). Bewusst ein eigenes Schema - die +;; anderen GR_-Bloecke behalten ihres. LAENGE ist beim Traeger die +;; NETTO-Laenge (Strecke minus Abzuege). VARIANTE macht den Block +;; selbstbeschreibend: GERUEST_TRAEGER_BESCHRIFTEN findet darueber die +;; Formatierung der Beschriftung, ohne den Dialog zu kennen. +;; Ohne null-Guard: ein Neuladen der Datei soll ein erweitertes Schema sofort +;; wirksam machen. +(setq *geruest-traeger-attrib-defs* + (append *geruest-attrib-defs* + '(("LAENGENABZUGANFANG" "") + ("LAENGENABZUGENDE" "") + ("VARIANTE" ""))) +) + +;; XDATA-App der Traeger-Beschriftung: Gruppe 1005 = Handle des Traeger-Blocks. +(setq *geruest-beschr-app* "SSG_GR_BESCHR") + (if (null *geruest-block-counter*) (setq *geruest-block-counter* 0)) @@ -116,10 +134,14 @@ ;; Alle Baugruppen als ((id . objekt) ...) in Dateireihenfolge - diese ;; Reihenfolge bestimmt auch die Reihenfolge im Bereichs-Dropdown. +;; Baugruppen mit "einfuegeart": "linie" (Rohr-Geruest-Traeger) fehlen hier: +;; sie haben einen eigenen Dialog und einen eigenen Einfuegeablauf +;; (c:GERUEST_TRAEGER) und gehoeren nicht in das Bereichs-Dropdown. (defun geruest-baugruppen-liste ( / erg) (setq erg nil) (foreach eintrag (geruest-daten) - (if (listp (cdr eintrag)) + (if (and (listp (cdr eintrag)) + (not (equal (ssg-json-get (cdr eintrag) "einfuegeart") "linie"))) (setq erg (append erg (list (cons (ssg-json-get (cdr eintrag) "id") (cdr eintrag))))) ) @@ -623,17 +645,44 @@ (princ) ) -;; Aktuell gewaehlte Farbe als Text. -(defun geruest-dlg-farbwert ( / idx) - (setq idx (atoi *geruest-dlg-farbe*)) +;; Farbliste des Dialogs aus der TEILEFARBE-Definition aufbauen: Optionen + +;; freie Option ans Listenende; deren Index merkt sich der Callback, um das +;; Freitextfeld freizuschalten. Genutzt vom GERUEST- und vom Traeger-Dialog. +(defun geruest-farb-liste-setzen (farb-opts / frei-opt) + (setq *geruest-farb-liste* nil *geruest-frei-idx* -1) + (if farb-opts + (progn + (setq *geruest-farb-liste* (ssg-json-get farb-opts "optionen")) + (if (ssg-json-get farb-opts "frei_erlaubt") + (progn + (setq frei-opt (ssg-json-get farb-opts "frei_option")) + (if (null frei-opt) (setq frei-opt "Individuell")) + (setq *geruest-frei-idx* (length *geruest-farb-liste*)) + (setq *geruest-farb-liste* + (append *geruest-farb-liste* (list frei-opt))) + ) + ) + ) + ) +) + +;; Gewaehlte Farbe als Text. idx-str = Tile-Wert der Farbliste, +;; frei = Inhalt des Freitextfelds. +(defun geruest-farbwert (idx-str frei / idx) + (setq idx (atoi idx-str)) (cond ((null *geruest-farb-liste*) "") - ((= idx *geruest-frei-idx*) *geruest-dlg-frei*) + ((= idx *geruest-frei-idx*) frei) ((nth idx *geruest-farb-liste*) (nth idx *geruest-farb-liste*)) (T "") ) ) +;; Aktuell gewaehlte Farbe des GERUEST-Dialogs als Text. +(defun geruest-dlg-farbwert ( / ) + (geruest-farbwert *geruest-dlg-farbe* *geruest-dlg-frei*) +) + ;; Konfiguration anwenden und die Tiles nachziehen. Wird direkt aus dem ;; popup_list-Rueckruf aufgerufen - set_tile aendert die Anzeige im laufenden ;; Dialog, ein Neuaufbau ist nicht noetig. Index 0 = "(keine)". @@ -919,33 +968,17 @@ ;; ("WECHSEL" bereich-index) - Nutzer hat den Bereich umgestellt ;; nil - abgebrochen (defun geruest-dialog (bg bereich-idx bereiche / dcl-pfad dat attr laenge-def - farb-opts frei-opt min-l max-l fertig rueckgabe + farb-opts min-l max-l fertig rueckgabe ergebnis wert-l oben-m unten-m name neu-idx) (setq *geruest-varianten* (ssg-json-get bg "geometrie_varianten")) (setq *geruest-anbauteile* (ssg-json-get bg "anbaugruppen")) (setq attr (ssg-json-get bg "attribute")) - (setq laenge-def (if attr (ssg-json-get attr "Laenge"))) - (setq farb-opts (if attr (ssg-json-get attr "Teilefarbe"))) + (setq laenge-def (if attr (ssg-json-get attr "LAENGE"))) + (setq farb-opts (if attr (ssg-json-get attr "TEILEFARBE"))) (setq min-l (if laenge-def (ssg-json-get laenge-def "min"))) (setq max-l (if laenge-def (ssg-json-get laenge-def "max"))) - ;; Farboptionen + freie Option ans Listenende; Index davon merkt sich der - ;; Callback, um das Freitextfeld freizuschalten. - (setq *geruest-farb-liste* nil *geruest-frei-idx* -1) - (if farb-opts - (progn - (setq *geruest-farb-liste* (ssg-json-get farb-opts "optionen")) - (if (ssg-json-get farb-opts "frei_erlaubt") - (progn - (setq frei-opt (ssg-json-get farb-opts "frei_option")) - (if (null frei-opt) (setq frei-opt "Individuell")) - (setq *geruest-frei-idx* (length *geruest-farb-liste*)) - (setq *geruest-farb-liste* - (append *geruest-farb-liste* (list frei-opt))) - ) - ) - ) - ) + (geruest-farb-liste-setzen farb-opts) (setq *geruest-konfig-liste* (geruest-konfigs bg)) ;; Frueheren Stand dieses Bereichs wiederherstellen, sonst Ausgangszustand @@ -1136,7 +1169,7 @@ ;; als reine Geometrie bei 0,0,0 mit in den Block wandern. ;; Rueckgabe: (blockname . entity) oder nil. (defun geruest-block-bauen (bg variante werte pt teile-dwgs / datei dummy lastEnt dummyEnt - e ss bname blockEnt dummy-attribs attribs) + dummy-attribs attribs) ;; Eine Variante darf eine eigene Dummy-DWG mitbringen; sonst gilt die der ;; Baugruppe. (setq datei (geruest-variante-wert variante bg "dummy")) @@ -1173,46 +1206,59 @@ ;; ssg-attrib-merge legt alle Tags aus *geruest-attrib-defs* an und ;; uebernimmt zusaetzlich alle Tags der Dummy-DWG. (setq attribs (ssg-attrib-merge dummy-attribs *geruest-attrib-defs*)) - (foreach paar werte - (if (assoc (car paar) attribs) - (setq attribs (subst paar (assoc (car paar) attribs) attribs)) - (setq attribs (append attribs (list paar))) - ) - ) + ;; nil = Drehwinkel interaktiv am Cursor + (geruest-block-abschliessen bg attribs werte lastEnt pt nil) + ) + ) +) - (ssg-attrib-make-defs (ssg-attrib-alist-to-defs attribs) 50.0) +;; Gemeinsamer Schluss aller Geruest-Bloecke: Werte ins Schema eintragen, +;; ATTDEFs erzeugen, alles Neue seit lastEnt zu GR__ zusammenfassen +;; und bei pt einfuegen. +;; attribs = Schema-Alist (Tags + Vorgabewerte), werte = berechnete Werte +;; rot = Drehwinkel in Grad (Zahl) oder nil fuer interaktive Drehung. +;; Bei einer Zahl muss der Aufrufer ANGBASE/ANGDIR/AUNITS auf 0 +;; gesetzt haben, sonst deutet INSERT den Wert anders. +;; Rueckgabe: (blockname . entity) oder nil. +(defun geruest-block-abschliessen (bg attribs werte lastEnt pt rot + / e ss bname blockEnt) + (foreach paar werte + (if (assoc (car paar) attribs) + (setq attribs (subst paar (assoc (car paar) attribs) attribs)) + (setq attribs (append attribs (list paar))) + ) + ) - ;; Alles Neue seit lastEnt einsammeln (Dummy-INSERT + ATTDEFs) - (setq ss (ssadd)) - (setq e (if lastEnt (entnext lastEnt) (entnext))) - (while e - (ssadd e ss) - (setq e (entnext e)) - ) + (ssg-attrib-make-defs (ssg-attrib-alist-to-defs attribs) 50.0) - (setq bname (geruest-blockname (ssg-json-get bg "id"))) - (command "_.-BLOCK" bname (list 0.0 0.0 0.0) ss "") + ;; Alles Neue seit lastEnt einsammeln (Geometrie + ATTDEFs) + (setq ss (ssadd)) + (setq e (if lastEnt (entnext lastEnt) (entnext))) + (while e + (ssadd e ss) + (setq e (entnext e)) + ) - ;; Stand VOR dem Einfuegen merken: bricht der Nutzer die Drehung mit ESC - ;; ab, entsteht kein neues INSERT und (entlast) zeigt auf ein voellig - ;; fremdes Objekt - ohne diesen Vergleich wuerden dessen Attribute - ;; ueberschrieben. - (setq lastEnt (entlast)) - ;; pause = Drehwinkel interaktiv am Cursor - (command "_.INSERT" bname pt 1 1 pause) - ;; Laufende Einfuegung sicher beenden (z.B. ESC waehrend der Drehung) - (if (> (getvar "CMDACTIVE") 0) (command)) - (if (> (getvar "CMDACTIVE") 0) (command)) + (setq bname (geruest-blockname (ssg-json-get bg "id"))) + (command "_.-BLOCK" bname (list 0.0 0.0 0.0) ss "") - (setq blockEnt (entlast)) - (if (and blockEnt (not (eq blockEnt lastEnt)) - (= (cdr (assoc 0 (entget blockEnt))) "INSERT")) - (progn - (ssg-attrib-set-on blockEnt attribs) - (ssg-block-layer-vorgabe blockEnt bname attribs) - (cons bname blockEnt) - ) - ) + ;; Stand VOR dem Einfuegen merken: bricht der Nutzer die Drehung mit ESC + ;; ab, entsteht kein neues INSERT und (entlast) zeigt auf ein voellig + ;; fremdes Objekt - ohne diesen Vergleich wuerden dessen Attribute + ;; ueberschrieben. + (setq lastEnt (entlast)) + (command "_.INSERT" bname pt 1 1 (if rot rot pause)) + ;; Laufende Einfuegung sicher beenden (z.B. ESC waehrend der Drehung) + (if (> (getvar "CMDACTIVE") 0) (command)) + (if (> (getvar "CMDACTIVE") 0) (command)) + + (setq blockEnt (entlast)) + (if (and blockEnt (not (eq blockEnt lastEnt)) + (= (cdr (assoc 0 (entget blockEnt))) "INSERT")) + (progn + (ssg-attrib-set-on blockEnt attribs) + (ssg-block-layer-vorgabe blockEnt bname attribs) + (cons bname blockEnt) ) ) ) @@ -1362,5 +1408,800 @@ (defun c:GERUEST_ROHRABHAENGER ( / ) (geruest-starten "Rohrabhaenger")) (defun c:GERUEST_WANDBEFESTIGUNG ( / ) (geruest-starten "Wandbefestigung")) -(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG") + +;; ============================================================ +;; ROHR-GERUEST-TRAEGER (Baugruppe mit "einfuegeart": "linie") +;; ============================================================ +;; Eigener Dialog (dcl/geruest_traeger.dcl): Variante, Farbe und zwei +;; Laengenabzuege. Die Laenge selbst wird DANACH in der Zeichnung bestimmt - +;; Startpunkt, dann Endpunkt oder getippte Laenge (+ Richtung), wie beim +;; Kreisel. Aktive Abzuege ruecken die Linie beidseitig ein: Strecke 1000, +;; Abzug Anfang 20, Ende 35 -> Linie beginnt 20 mm hinter dem Startklick und +;; ist 945 mm lang. +;; +;; Keine Dummy-DWG: die Geometrie ist eine einzige LINE, die je Block eine +;; andere Laenge hat. Sie entsteht per entmake von (0,0,0) nach (L,0,0) auf +;; dem Layer der Variante; der Block wird am eingerueckten Startpunkt mit dem +;; Richtungswinkel eingefuegt. Es wird wiederholt eingefuegt, bis der Nutzer +;; ESC/Enter drueckt - jeder Traeger ist ein eigener GR_Traeger_-Block. + +;; Baugruppe ueber das ASCII-Feld "id" (auch solche, die +;; geruest-baugruppen-liste auslaesst). +(defun geruest-baugruppe (id / erg) + (setq erg nil) + (foreach eintrag (geruest-daten) + (if (and (null erg) (listp (cdr eintrag)) + (equal (ssg-json-get (cdr eintrag) "id") id)) + (setq erg (cdr eintrag)) + ) + ) + erg +) + +;; Feld einer Attribut-Definition (attribute -> TAG -> feld). Eine Variante +;; darf einen eigenen "attribute"-Block mitbringen, der vor dem der Baugruppe +;; gilt (z.B. ein anderer Laengenabzug bei einer Muffen-Variante). assoc +;; statt ssg-json-get, damit ein in der Variante gesetztes false (= nil) den +;; Wert der Baugruppe ueberschreibt, statt als "fehlt" durchzufallen. +(defun geruest-attr-feld (variante bg tag feld) + (geruest-obj-feld variante bg (list "attribute" tag) feld) +) + +;; Objekt unter einem Schluesselpfad, z.B. ("attribute" "LAENGE"); nil, wenn +;; ein Schluessel fehlt. +(defun geruest-json-pfad-get (obj pfad) + (foreach k pfad (setq obj (if (and obj (listp obj)) (ssg-json-get obj k)))) + obj +) + +;; Allgemeine Form von geruest-attr-feld: Feld unter pfad, Variante vor +;; Baugruppe, je FELD aufgeloest (eine Variante nennt nur, was abweicht). +;; Genutzt fuer "attribute" und "beschriftung". +(defun geruest-obj-feld (variante bg pfad feld / def paar) + (setq def (if variante (geruest-json-pfad-get variante pfad))) + (setq paar (if (and def (listp def)) (assoc feld def))) + (if paar + (cdr paar) + (ssg-json-get (geruest-json-pfad-get bg pfad) feld) + ) +) + +;; Millimeter-Eingabe als Zahl >= 0 (Komma wie Punkt), sonst nil. +;; Leere Eingabe -> nil. +(defun geruest-mm-zahl (s / w) + (setq s (vl-string-translate "," "." (ssg-cfg-trim (if s s "")))) + (if (and (/= s "") (setq w (distof s 2)) (>= w 0.0)) w) +) + +;; Abzug-Feld: leer -> 0, gueltige Zahl -> Zahl, sonst nil. +(defun geruest-abzug-wert (s) + (if (= (ssg-cfg-trim (if s s "")) "") + 0.0 + (geruest-mm-zahl s) + ) +) + +;; Millimeterwert fuer Attribute/Meldungen: ganze Zahl ohne Nachkommastelle, +;; sonst eine Stelle. +(defun geruest-mm-string (w / r) + (setq r (fix (+ w 0.5))) + (if (equal w r 1e-6) (itoa r) (rtos w 2 1)) +) + +;; Netto-Laenge der Linie: Strecke minus beide Abzuege, nil wenn <= 0. +(defun geruest-traeger-laenge (strecke abzug-a abzug-e / netto) + (setq netto (- strecke (if abzug-a abzug-a 0.0) (if abzug-e abzug-e 0.0))) + (if (> netto 1e-6) netto) +) + +;; Eingerueckter Startpunkt: abzug-a entlang winkel ab p1, Z von p1. +(defun geruest-traeger-start (p1 winkel abzug-a / p) + (setq p (polar p1 winkel (if abzug-a abzug-a 0.0))) + (list (car p) (cadr p) (if (caddr p1) (caddr p1) 0.0)) +) + +;; Positionen der Querstreben entlang der Traegerlinie (Abstand ab Linienanfang). +;; Regeln: Abstand zwischen zwei Streben hoechstens abstand-max, beide +;; Endabstaende gleich und in [rand-min, rand-max], so wenige Streben wie +;; moeglich. +;; netto <= 2*rand-max -> eine Strebe in der Mitte +;; sonst -> k = aufgerundet (netto - 2*rand-max) / abstand-max +;; Felder (= wenigste Streben). Rand +;; r = max(rand-min, (netto - k*abstand-max) / 2): +;; der Abstand ist damit GENAU abstand-max, solange der +;; Rand >= rand-min bleibt; nur sonst bleibt der Rand +;; bei rand-min und der Abstand wird etwas kleiner. +;; Mit genau abstand-max geht nicht jede Laenge auf (1100 mm: 2 Streben -> +;; Rand 300, 3 Streben -> Rand 50) - dort ergibt sich 100 | 450 | 450 | 100. +;; r <= rand-max ist durch die Wahl von k garantiert. +;; Kuerzer als 2*rand-min -> keine Strebe (der Mindestrand waere verletzt). +(defun geruest-traeger-streben (netto abstand-max rand-min rand-max / k r s i erg) + (cond + ((or (null netto) (< netto (- (* 2.0 rand-min) 1e-6))) nil) + ((<= netto (+ (* 2.0 rand-max) 1e-6)) (list (/ netto 2.0))) + (T + (setq k (/ (- netto (* 2.0 rand-max)) abstand-max)) + ;; aufrunden, Rundungsrauschen (z.B. 2.0000000001) nicht mitzaehlen + (setq k (if (equal k (fix k) 1e-6) (fix k) (1+ (fix k)))) + (setq r (max rand-min (/ (- netto (* k abstand-max)) 2.0))) + (setq s (/ (- netto (* 2.0 r)) k)) + (setq erg nil i k) + (while (>= i 0) + (setq erg (cons (+ r (* i s)) erg)) + (setq i (1- i)) + ) + erg + ) + ) +) + +;; Querstreben-Konfiguration (Variante vor Baugruppe) als +;; (dummy laenge querlinie abstand-max rand-min rand-max) oder nil, wenn keine +;; "querstrebe" definiert ist. +;; dummy = DWG unter data/geruest (Basispunkt Strebenmitte, 30 mm laengs +;; X) oder nil +;; laenge/querlinie = nur fuer den Rueckfall ohne Dummy-DWG: zwei Querlinien +;; im Abstand laenge, je querlinie lang +;; Fehlende Zahlenwerte -> Standard 30/25/500/100/250. +(defun geruest-traeger-strebe-cfg (variante bg / q w d) + (setq q (geruest-variante-wert variante bg "querstrebe")) + (if (and q (listp q)) + (progn + (setq d (ssg-json-get q "dummy")) + (cons (if (and d (= (type d) 'STR) (/= d "")) d) + (mapcar '(lambda (feld std) + (setq w (ssg-json-get q feld)) + (if (numberp w) (float w) std)) + '("laenge" "querlinie" "abstand_max" "rand_min" "rand_max") + '(30.0 25.0 500.0 100.0 250.0))) + ) + ) +) + +;; Muffen-Dummy einer Variante: nur bei "mit_muffe": true die "dummy"-Datei +;; aus "muffe" (Variante vor Baugruppe), sonst nil. Das Flag steht bewusst +;; ausdruecklich je Variante, statt aus dem Namen ("mit Muffe") geraten zu +;; werden. Bei "mit 2 Muffen" wird trotzdem nur eine Muffe gezeichnet. +(defun geruest-traeger-muffe-dummy (variante bg / m d) + (if (and variante (ssg-json-get variante "mit_muffe")) + (progn + (setq m (geruest-variante-wert variante bg "muffe")) + (setq d (if (and m (listp m)) (ssg-json-get m "dummy"))) + (if (and d (= (type d) 'STR) (/= d "")) d) + ) + ) +) + +;; Liegt netto in [min-l, max-l]? Fehlende Grenzen gelten nicht. +(defun geruest-traeger-pruefen (netto min-l max-l) + (and netto + (or (null min-l) (>= netto min-l)) + (or (null max-l) (<= netto max-l))) +) + +;; BEZEICHNUNG: Teileart + Variante + aktive Abzuege, mit " + " verkettet +;; (Artikelnamen enthalten Kommas, siehe geruest-einfuegen). +(defun geruest-traeger-bezeichnung (teileart variante-name abzug-a abzug-e / teile) + (setq teile (list teileart variante-name)) + (if (> abzug-a 0.0) + (setq teile (append teile (list (ssg-textf "gr-tr-bez-abzug-anfang" + (list (geruest-mm-string abzug-a))))))) + (if (> abzug-e 0.0) + (setq teile (append teile (list (ssg-textf "gr-tr-bez-abzug-ende" + (list (geruest-mm-string abzug-e))))))) + (ssg-join teile " + ") +) + +(defun geruest-traeger-dcl-pfad ( / basis) + (setq basis (cond ((getenv "DXFM_DCL")) + ((getenv "DXFMAKRO") (strcat (getenv "DXFMAKRO") "/dcl")))) + (if basis + (strcat (vl-string-translate "\\" "/" basis) "/geruest_traeger.dcl") + ) +) + +;; --- Dialogzustand (Globals, siehe Abschnitt DIALOGZUSTAND oben) --- +;; Bleibt zwischen zwei GERUEST_TRAEGER-Aufrufen erhalten. + +;; Abzug-Vorgaben (aktiv + Wert) der Variante idx in den Zustand laden. +(defun geruest-tr-vorgaben-laden (idx / v) + (setq v (nth idx (ssg-json-get *geruest-tr-bg* "geometrie_varianten"))) + (setq *geruest-tr-abzug-a* + (if (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGANFANG" "aktiv") "1" "0")) + (setq *geruest-tr-abzug-a-w* + (geruest-zahl-string (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGANFANG" "vorgabe"))) + (setq *geruest-tr-abzug-e* + (if (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGENDE" "aktiv") "1" "0")) + (setq *geruest-tr-abzug-e-w* + (geruest-zahl-string (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGENDE" "vorgabe"))) +) + +(defun geruest-tr-abzug-schreiben ( / ) + (set_tile "abzug_a" *geruest-tr-abzug-a*) + (set_tile "abzug_a_w" *geruest-tr-abzug-a-w*) + (set_tile "abzug_e" *geruest-tr-abzug-e*) + (set_tile "abzug_e_w" *geruest-tr-abzug-e-w*) + (mode_tile "abzug_a_w" (if (= *geruest-tr-abzug-a* "1") 0 1)) + (mode_tile "abzug_e_w" (if (= *geruest-tr-abzug-e* "1") 0 1)) +) + +;; Variantenwechsel im Dialog: Abzug-Vorgaben der neuen Variante uebernehmen. +(defun geruest-tr-variante-gewaehlt (wert / ) + (setq *geruest-tr-variante* wert) + (geruest-tr-vorgaben-laden (atoi wert)) + (geruest-tr-abzug-schreiben) +) + +(defun geruest-tr-dlg-lesen ( / ) + (setq *geruest-tr-variante* (get_tile "variante")) + (setq *geruest-tr-farbe* (get_tile "farbe")) + (setq *geruest-tr-frei* (get_tile "farbe_frei")) + (setq *geruest-tr-abzug-a* (get_tile "abzug_a")) + (setq *geruest-tr-abzug-a-w* (get_tile "abzug_a_w")) + (setq *geruest-tr-abzug-e* (get_tile "abzug_e")) + (setq *geruest-tr-abzug-e-w* (get_tile "abzug_e_w")) + (setq *geruest-tr-beschriftung* (get_tile "beschriftung")) +) + +;; Wert eines Abzugs aus dem Zustand: inaktiv -> 0, aktiv -> Zahl oder nil. +(defun geruest-tr-abzug (aktiv wert) + (if (= aktiv "1") (geruest-abzug-wert wert) 0.0) +) + +;; Traeger-Dialog. Rueckgabe: +;; (variante-index farbe abzug-anfang abzug-ende beschriften-p) oder nil bei +;; Abbruch. +(defun geruest-traeger-dialog (bg / varianten dcl-pfad dat fertig rueckgabe + ergebnis aw ew) + (setq varianten (ssg-json-get bg "geometrie_varianten")) + (setq *geruest-tr-bg* bg) + (geruest-farb-liste-setzen + (ssg-json-get (ssg-json-get bg "attribute") "TEILEFARBE")) + ;; Ausgangszustand beim ersten Aufruf bzw. wenn der gemerkte Index nach + ;; einer JSON-Aenderung nicht mehr passt. + (if (or (null *geruest-tr-variante*) + (>= (atoi *geruest-tr-variante*) (length varianten))) + (progn + (setq *geruest-tr-variante* "0" *geruest-tr-farbe* "0" *geruest-tr-frei* "") + (geruest-tr-vorgaben-laden 0) + ) + ) + ;; Beschriftung standardmaessig an; der Haken bleibt zwischen Aufrufen + (if (null *geruest-tr-beschriftung*) (setq *geruest-tr-beschriftung* "1")) + (setq dcl-pfad (geruest-traeger-dcl-pfad)) + (setq fertig nil rueckgabe nil) + + ;; Schleife: ungueltige Eingaben zeigen den Dialog mit dem bisherigen + ;; Stand erneut - DCL kann Zahlen nicht selbst pruefen. + (while (not fertig) + (setq dat (if (and dcl-pfad (findfile dcl-pfad)) (load_dialog dcl-pfad) -1)) + (if (or (< dat 0) (not (new_dialog "geruest_traeger" dat))) + (progn + (alert (ssg-textf "gr-tr-fehler-dcl" + (list (if dcl-pfad dcl-pfad "geruest_traeger.dcl")))) + (setq fertig T) + ) + (progn + (start_list "variante") + (foreach v varianten (add_list (geruest-eintrag-name v))) + (end_list) + (set_tile "variante" *geruest-tr-variante*) + + (if *geruest-farb-liste* + (progn + (start_list "farbe") + (foreach f *geruest-farb-liste* (add_list f)) + (end_list) + (set_tile "farbe" *geruest-tr-farbe*) + (mode_tile "farbe_frei" + (if (= (atoi *geruest-tr-farbe*) *geruest-frei-idx*) 0 1)) + (action_tile "farbe" + "(mode_tile \"farbe_frei\" (if (= (atoi $value) *geruest-frei-idx*) 0 1))") + ) + (progn + (mode_tile "farbe" 1) + (mode_tile "farbe_frei" 1) + ) + ) + (set_tile "farbe_frei" *geruest-tr-frei*) + (geruest-tr-abzug-schreiben) + (set_tile "beschriftung" *geruest-tr-beschriftung*) + (set_tile "hinweis" (ssg-text "gr-tr-hinweis")) + + (action_tile "variante" "(geruest-tr-variante-gewaehlt $value)") + (action_tile "abzug_a" + "(mode_tile \"abzug_a_w\" (if (= $value \"1\") 0 1))") + (action_tile "abzug_e" + "(mode_tile \"abzug_e_w\" (if (= $value \"1\") 0 1))") + (action_tile "accept" "(geruest-tr-dlg-lesen) (done_dialog 1)") + (action_tile "cancel" "(done_dialog 0)") + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (cond + ((= ergebnis 0) (setq fertig T)) + ((or (null (setq aw (geruest-tr-abzug *geruest-tr-abzug-a* *geruest-tr-abzug-a-w*))) + (null (setq ew (geruest-tr-abzug *geruest-tr-abzug-e* *geruest-tr-abzug-e-w*)))) + (alert (ssg-text "gr-tr-alert-abzug")) + ) + (T + (setq rueckgabe + (list (atoi *geruest-tr-variante*) + (geruest-farbwert *geruest-tr-farbe* *geruest-tr-frei*) + aw ew + (= *geruest-tr-beschriftung* "1"))) + (setq fertig T) + ) + ) + ) + ) + ) + rueckgabe +) + +;; X-Positionen der beiden Querlinien einer Strebe: Anfang und Ende des +;; Flacheisens, laenge mittig um die Strebenposition x. +(defun geruest-traeger-strebe-enden (x laenge) + (list (- x (/ laenge 2.0)) (+ x (/ laenge 2.0))) +) + +;; Querstrebe darstellen: an Anfang und Ende je eine Linie quer zum Traeger +;; (Laenge querlinie, mittig auf der Traegerlinie y = 0). +(defun geruest-traeger-strebe-zeichnen (x laenge querlinie lay / hq) + (setq hq (/ querlinie 2.0)) + (foreach xe (geruest-traeger-strebe-enden x laenge) + (entmake (list '(0 . "LINE") (cons 8 lay) + (list 10 xe (- hq) 0.0) (list 11 xe hq 0.0))) + ) +) + +;; Vollstaendiger Pfad einer Dummy-DWG unter data/geruest oder nil (mit +;; Meldung), wenn sie fehlt. datei = nil -> stilles nil (nichts konfiguriert). +(defun geruest-dummy-finden (datei / pfad) + (if datei + (progn + (setq pfad (geruest-dummy-pfad datei)) + (if (and pfad (findfile pfad)) + pfad + (progn + (princ (ssg-textf "gr-fehler-dummy" (list (if pfad pfad datei)))) + nil + ) + ) + ) + ) +) + +;; Dummy-DWG als verschachtelten Block bei pt (Drehung 0) einfuegen. Ist die +;; Blockdefinition (Dateiname ohne .dwg) schon in der Zeichnung, wird per +;; Blockname eingefuegt - sonst wuerde die DWG bei jeder Strebe neu gelesen +;; (und ggf. "Block neu definieren?" gefragt). ATTREQ/ATTDIA muss der Aufrufer +;; auf 0 gesetzt haben. +(defun geruest-dummy-einfuegen (pfad pt / bname) + (setq bname (vl-filename-base pfad)) + (command "_.INSERT" (if (tblsearch "BLOCK" bname) bname pfad) pt 1 1 0) +) + +;; Linie + Querstreben + Muffe + ATTDEFs zu einem Block zusammenfassen und am +;; Startpunkt mit dem Richtungswinkel einfuegen. Im Block laeuft der Traeger +;; entlang +X, die Dummys werden darum ohne Drehung eingefuegt. +;; strebe = Ergebnis von geruest-traeger-strebe-cfg (nil = keine Streben) +;; strebe-pfad = Pfad der Strebe-DWG oder nil -> Rueckfall auf Querlinien +;; muffe-pfad = Pfad der Muffen-DWG oder nil -> keine Muffe; sonst eine +;; Muffe (Basispunkt Muffenmitte) am Linienende +;; Der Aufrufer sichert die Systemvariablen per ssg-start. +;; Rueckgabe: (blockname . entity) oder nil. +(defun geruest-traeger-block-bauen (bg layer netto strebe strebe-pfad muffe-pfad + werte pt winkel / lastEnt lay) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + (setvar "OSMODE" 0) + ;; Drehwinkel wird als Grad an INSERT uebergeben - nur eindeutig mit + ;; Winkel-Nullrichtung Ost, gegen den Uhrzeigersinn, Einheit Grad. + (setvar "ANGBASE" 0.0) + (setvar "ANGDIR" 0) + (setvar "AUNITS" 0) + ;; Nicht (entlast) - siehe geruest-block-bauen. + (setq lastEnt (ssg-lastent-ohne-attribute)) + + (setq lay (if (and layer (/= layer "")) layer "0")) + ;; Aktiv setzen, damit auch die verschachtelten Dummy-INSERTs auf dem + ;; Traeger-Layer landen (CLAYER sichert ssg-start immer mit). + (ssg-make-layer lay (ssg-layer-farbe-fuer-name lay) T) + (entmake (list '(0 . "LINE") (cons 8 lay) + '(10 0.0 0.0 0.0) (list 11 netto 0.0 0.0))) + ;; strebe = (dummy laenge querlinie abstand-max rand-min rand-max) + (if strebe + (foreach x (geruest-traeger-streben netto (nth 3 strebe) (nth 4 strebe) (nth 5 strebe)) + (if strebe-pfad + (geruest-dummy-einfuegen strebe-pfad (list x 0.0 0.0)) + (geruest-traeger-strebe-zeichnen x (nth 1 strebe) (nth 2 strebe) lay) + ) + ) + ) + (if muffe-pfad + (geruest-dummy-einfuegen muffe-pfad (list netto 0.0 0.0)) + ) + + (geruest-block-abschliessen bg + (ssg-attrib-merge nil *geruest-traeger-attrib-defs*) + werte lastEnt pt (* winkel (/ 180.0 pi))) +) + +;; Einfuege-Schleife: Startpunkt, Endpunkt oder Laenge, bis ESC/Enter. +;; wahl = Rueckgabe von geruest-traeger-dialog. +(defun geruest-traeger-einfuegen (bg wahl / variante teileart layer paare farbe + abzug-a abzug-e min-l max-l bez werte-basis + werte weiter p1 eingabe strecke winkel netto + ergebnis anzahl strebe strebe-pfad muffe-pfad + beschriften) + (setq variante (nth (car wahl) (ssg-json-get bg "geometrie_varianten"))) + (setq farbe (cadr wahl) + abzug-a (caddr wahl) + abzug-e (cadddr wahl) + beschriften (nth 4 wahl)) + (setq *geruest-beschr-gemeldet* nil) + (setq teileart (geruest-variante-wert variante bg "teileart")) + (setq layer (geruest-variante-wert variante bg "layer")) + (setq min-l (geruest-attr-feld variante bg "LAENGE" "min")) + (setq max-l (geruest-attr-feld variante bg "LAENGE" "max")) + (setq paare (geruest-teile-paare variante nil)) + (setq strebe (geruest-traeger-strebe-cfg variante bg)) + ;; DWGs einmal je Durchgang aufloesen - eine fehlende Datei wird so nur + ;; einmal gemeldet, nicht bei jedem Traeger. Fehlt die Strebe-DWG, werden + ;; stattdessen Querlinien gezeichnet; fehlt die Muffe, entfaellt sie. + (setq strebe-pfad (if strebe (geruest-dummy-finden (car strebe)))) + (setq muffe-pfad (geruest-dummy-finden (geruest-traeger-muffe-dummy variante bg))) + (setq bez (geruest-traeger-bezeichnung teileart + (geruest-eintrag-name variante) abzug-a abzug-e)) + + ;; Fuer alle Traeger dieses Durchgangs gleich - nur LAENGE aendert sich. + (setq werte-basis + (list (cons "ARTINR" (ssg-join (mapcar 'car paare) ",")) + (cons "BEZEICHNUNG" bez) + (cons "TEILEART" teileart) + (cons "LAENGENABZUGANFANG" (geruest-mm-string abzug-a)) + (cons "LAENGENABZUGENDE" (geruest-mm-string abzug-e)) + ;; Block selbstbeschreibend - die Beschriftung (auch spaeter per + ;; GERUEST_TRAEGER_BESCHRIFTEN) findet darueber ihre Formatierung + (cons "VARIANTE" (geruest-eintrag-name variante)))) + (if (and layer (/= layer "")) + (setq werte-basis (append werte-basis (list (cons "LAYER" layer))))) + (if (/= farbe "") + (setq werte-basis (append werte-basis (list (cons "TEILEFARBE" + (ssg-join (geruest-farbliste paare farbe) ",")))))) + + (setq weiter T anzahl 0) + (while weiter + (setq strecke nil) + (setq p1 (getpoint (ssg-text "gr-tr-prompt-start"))) + (if (null p1) + (setq weiter nil) + (progn + ;; 128 = beliebige Eingabe: ein Punkt liefert Laenge UND Richtung, + ;; eine getippte Zahl nur die Laenge (Richtung wird nachgefragt). + (initget 128) + (setq eingabe (getpoint p1 (ssg-text "gr-tr-prompt-ende"))) + ;; Reihenfolge wichtig: nil ist auch listp, und "" darf erst nach dem + ;; Punkt-Fall verglichen werden. + (cond + ((null eingabe) (setq weiter nil)) + ((listp eingabe) + ;; Laenge in der XY-Ebene, die Linie liegt auf Z des Startpunkts + (setq strecke (distance (list (car p1) (cadr p1)) + (list (car eingabe) (cadr eingabe)))) + (setq winkel (angle p1 eingabe)) + ) + ((= eingabe "") (setq weiter nil)) + ((setq strecke (geruest-mm-zahl eingabe)) + (setq winkel (getangle p1 (ssg-text "gr-tr-prompt-richtung"))) + (if (null winkel) (setq winkel 0.0)) + ) + (T (princ (ssg-textf "gr-tr-eingabe-ungueltig" (list eingabe)))) + ) + ) + ) + + (if (and weiter strecke) + (progn + (setq netto (geruest-traeger-laenge strecke abzug-a abzug-e)) + (cond + ((null netto) + (princ (ssg-textf "gr-tr-laenge-null" + (list (geruest-mm-string strecke) + (geruest-mm-string (+ abzug-a abzug-e)))))) + ((not (geruest-traeger-pruefen netto min-l max-l)) + (princ (ssg-textf "gr-tr-laenge-bereich" + (list (geruest-mm-string netto) + (if min-l (geruest-mm-string min-l) "0") + (if max-l (geruest-mm-string max-l) "-"))))) + (T + (setq werte (cons (cons "LAENGE" (geruest-mm-string netto)) werte-basis)) + (ssg-start nil '(("OSMODE") ("ATTREQ") ("ATTDIA") + ("ANGBASE") ("ANGDIR") ("AUNITS"))) + (setq ergebnis (geruest-traeger-block-bauen bg layer netto strebe + strebe-pfad muffe-pfad werte + (geruest-traeger-start p1 winkel abzug-a) winkel)) + ;; Beschriftung aus dem fertigen Block ableiten (dieselbe Funktion + ;; wie GERUEST_TRAEGER_BESCHRIFTEN) + (if (and ergebnis beschriften) + (geruest-traeger-beschriften (cdr ergebnis))) + (ssg-end) + (if ergebnis + (progn + (setq anzahl (1+ anzahl)) + (princ (ssg-textf "gr-tr-fertig" + (list (geruest-mm-string netto) (car ergebnis)))) + ) + ) + ) + ) + ) + ) + ) + (princ (ssg-textf "gr-tr-ende" (list (itoa anzahl)))) + (princ) +) + +;; ============================================================ +;; TRAEGER-BESCHRIFTUNG +;; ============================================================ +;; Eigenes TEXT-Objekt neben dem Block, z.B. "RT 80/80 L=2650". Es wird +;; AUSSCHLIESSLICH aus dem fertigen Block abgeleitet (Einfuegepunkt, Drehung, +;; Attribute LAENGE und VARIANTE) - darum nutzen das Einfuegen und +;; GERUEST_TRAEGER_BESCHRIFTEN dieselbe Funktion, und ein von Hand +;; verschobener/gedrehter Traeger wird beim Neubeschriften korrekt erfasst. +;; Der Text traegt per XDATA (*geruest-beschr-app*, 1005) den Handle seines +;; Blocks; so wird er beim Neubeschriften ersetzt statt verdoppelt. +;; +;; Formatierung aus geruest.json, "beschriftung" (Variante vor Baugruppe, je +;; Feld): NUR stil, ausrichtung, hoehe - alles andere regelt der Textstil +;; ausserhalb (es wird kein Stil angelegt). Dazu Inhalt (format/text) und +;; Layer. + +;; Winkel auf [0, 2pi) normieren. +(defun geruest-winkel-norm (w) + (setq w (rem w (* 2.0 pi))) + (if (< w 0.0) (+ w (* 2.0 pi)) w) +) + +;; Textwinkel entlang des Traegers nach DIN-Konvention (lesbar von unten und +;; von rechts): fuer 90 < w <= 270 Grad um 180 Grad gedreht, sonst w. +(defun geruest-text-winkel (w / e) + (setq w (geruest-winkel-norm w) e 1e-9) + (if (and (> w (+ (/ pi 2.0) e)) (<= w (+ (* 1.5 pi) e))) + (geruest-winkel-norm (+ w pi)) + w + ) +) + +;; Ausrichtungs-Code wie bei TEXT "Ausrichten" (T/M/B + L/C/R, z.B. "BC") -> +;; (dxf72 dxf73), nil bei unbekanntem Code. +(defun geruest-ausrichtung-codes (code / h v) + (if (and code (= (type code) 'STR) (= (strlen code) 2)) + (progn + (setq code (strcase code)) + (setq v (cdr (assoc (substr code 1 1) '(("T" . 3) ("M" . 2) ("B" . 1))))) + (setq h (cdr (assoc (substr code 2 1) '(("L" . 0) ("C" . 1) ("R" . 2))))) + (if (and v h) (list h v)) + ) + ) +) + +;; Lage des Textes: (punkt textwinkel dxf72 dxf73). p0 = Traeger-Startpunkt, +;; w = Traegerwinkel, laenge = Traegerlaenge. Der Bezugspunkt liegt auf der +;; Linie: L = in Leserichtung linkes Ende, C = Mitte, R = rechtes Ende. Nach +;; einer 180-Grad-Drehung ist "links" also das Linienende. Unbekannter Code +;; -> BC (die Meldung gibt der Aufrufer aus). +(defun geruest-traeger-text-lage (p0 w laenge code / codes tw p1 links rechts pt) + (setq codes (geruest-ausrichtung-codes code)) + (if (null codes) (setq codes '(1 1))) + (setq tw (geruest-text-winkel w)) + (setq p1 (polar p0 w laenge)) + ;; Projektion auf die Leserichtung entscheidet, welches Ende links liegt + (if (<= (+ (* (car p0) (cos tw)) (* (cadr p0) (sin tw))) + (+ (* (car p1) (cos tw)) (* (cadr p1) (sin tw)))) + (setq links p0 rechts p1) + (setq links p1 rechts p0) + ) + (setq pt (cond ((= (car codes) 0) links) + ((= (car codes) 2) rechts) + (T (polar p0 w (/ laenge 2.0))))) + (list pt tw (car codes) (cadr codes)) +) + +;; Alle Vorkommen von alt in s durch neu ersetzen. +(defun geruest-string-ersetzen (s alt neu / pos) + (setq pos (vl-string-search alt s)) + (while pos + (setq s (strcat (substr s 1 pos) neu (substr s (+ pos (strlen alt) 1)))) + (setq pos (vl-string-search alt s (+ pos (strlen neu)))) + ) + s +) + +;; Beschriftungstext: %1 = Text der Variante, %2 = Laenge auf ganze mm. +(defun geruest-traeger-text-string (fmt txt laenge) + (geruest-string-ersetzen + (geruest-string-ersetzen fmt "%1" txt) + "%2" (itoa (fix (+ laenge 0.5)))) +) + +;; Beschriftungs-Konfiguration als Alist (format text stil ausrichtung hoehe +;; layer), je Feld Variante vor Baugruppe vor Code-Standard. Fehlt "text", +;; gilt der Variantenname bzw. ersatz-text (Block ohne bekannte Variante). +;; stil/layer = nil -> aktueller Textstil/Layer. +(defun geruest-traeger-beschr-cfg (variante bg ersatz-text / txt h) + (setq txt (geruest-beschr-feld variante bg "text")) + (if (not (and txt (= (type txt) 'STR) (/= txt ""))) + (setq txt (if variante (geruest-eintrag-name variante) ersatz-text))) + (setq h (geruest-beschr-feld variante bg "hoehe")) + (list (cons "format" (cond ((geruest-beschr-feld variante bg "format")) ("%1 L=%2"))) + (cons "text" (if txt txt "")) + (cons "stil" (geruest-beschr-feld variante bg "stil")) + (cons "ausrichtung" (cond ((geruest-beschr-feld variante bg "ausrichtung")) ("BC"))) + (cons "hoehe" (if (numberp h) (float h) 50.0)) + (cons "layer" (geruest-beschr-feld variante bg "layer"))) +) + +(defun geruest-beschr-feld (variante bg feld) + (geruest-obj-feld variante bg '("beschriftung") feld) +) + +;; Handle des Traeger-Blocks aus der XDATA einer Beschriftung (oder nil). +(defun geruest-beschr-handle (ent / xd) + (setq xd (cadr (assoc -3 (entget ent (list *geruest-beschr-app*))))) + (if xd (cdr (assoc 1005 (cdr xd)))) +) + +;; Alle Beschriftungen der Zeichnung (Auswahlsatz oder nil). +(defun geruest-beschr-alle ( / ) + (ssget "_X" (list '(0 . "TEXT") (list -3 (list *geruest-beschr-app*)))) +) + +;; Beschriftung(en) zu einem Block-Handle loeschen. +(defun geruest-beschr-entfernen (handle / ss i e) + (if (and handle (setq ss (geruest-beschr-alle))) + (progn + (setq i 0) + (repeat (sslength ss) + (setq e (ssname ss i)) + (if (equal (geruest-beschr-handle e) handle) (entdel e)) + (setq i (1+ i)) + ) + ) + ) +) + +;; Textstil pruefen: vorhanden -> Name, sonst einmal je Lauf melden und den +;; aktuellen TEXTSTYLE nehmen. *geruest-beschr-gemeldet* setzt der Aufrufer +;; je Lauf zurueck. +(defun geruest-beschr-stil (stil / ) + (cond + ((and stil (/= stil "") (tblsearch "STYLE" stil)) stil) + (T + (if (and stil (/= stil "") (not (member stil *geruest-beschr-gemeldet*))) + (progn + (princ (ssg-textf "gr-tr-beschr-stil-fehlt" (list stil (getvar "TEXTSTYLE")))) + (setq *geruest-beschr-gemeldet* (cons stil *geruest-beschr-gemeldet*)) + ) + ) + (getvar "TEXTSTYLE") + ) + ) +) + +;; Einen Traeger-Block beschriften (eine evtl. vorhandene Beschriftung wird +;; ersetzt). Rueckgabe: "ok", "ersatz" (Block ohne bekannte VARIANTE, mit den +;; Vorgaben der Baugruppe beschriftet) oder nil (nicht beschriftbar). +(defun geruest-traeger-beschriften (blockEnt / ed attribs bg vname idx variante + cfg laenge lage lay handle code erg) + (setq ed (entget blockEnt)) + (setq attribs (ssg-attrib-read blockEnt)) + (setq bg (geruest-baugruppe "Traeger")) + (setq laenge (atof (cond ((cdr (assoc "LAENGE" attribs))) ("0")))) + (if (and bg (> laenge 0.0)) + (progn + (setq vname (cdr (assoc "VARIANTE" attribs))) + (setq idx (if (and vname (/= vname "")) + (geruest-name-index vname (ssg-json-get bg "geometrie_varianten")))) + (setq variante (if idx (nth idx (ssg-json-get bg "geometrie_varianten")))) + (setq cfg (geruest-traeger-beschr-cfg variante bg + (cond ((cdr (assoc "TEILEART" attribs))) ("")))) + (setq code (cdr (assoc "ausrichtung" cfg))) + (if (and (null (geruest-ausrichtung-codes code)) + (not (member code *geruest-beschr-gemeldet*))) + (progn + (princ (ssg-textf "gr-tr-beschr-ausrichtung" (list code))) + (setq *geruest-beschr-gemeldet* (cons code *geruest-beschr-gemeldet*)) + ) + ) + (setq lage (geruest-traeger-text-lage (cdr (assoc 10 ed)) (cdr (assoc 50 ed)) + laenge code)) + (setq lay (cdr (assoc "layer" cfg))) + (if (and lay (/= lay "")) + (ssg-make-layer lay (ssg-layer-farbe-fuer-name lay) nil) + (setq lay (getvar "CLAYER")) + ) + (setq handle (cdr (assoc 5 ed))) + (geruest-beschr-entfernen handle) + (if (not (tblsearch "APPID" *geruest-beschr-app*)) (regapp *geruest-beschr-app*)) + ;; 73 ist immer >= 1, damit gilt Gruppe 11 als Ausrichtungspunkt + (if (entmake (list '(0 . "TEXT") + (cons 8 lay) + (cons 7 (geruest-beschr-stil (cdr (assoc "stil" cfg)))) + (cons 10 (nth 0 lage)) + (cons 11 (nth 0 lage)) + (cons 40 (cdr (assoc "hoehe" cfg))) + (cons 1 (geruest-traeger-text-string + (cdr (assoc "format" cfg)) + (cdr (assoc "text" cfg)) laenge)) + (cons 50 (nth 1 lage)) + (cons 72 (nth 2 lage)) + (cons 73 (nth 3 lage)) + (list -3 (list *geruest-beschr-app* (cons 1005 handle))))) + (setq erg (if variante "ok" "ersatz")) + ) + ) + ) + erg +) + +;; Alle Traeger der Zeichnung (neu) beschriften und Beschriftungen, deren +;; Block nicht mehr existiert, entfernen - unabhaengig von der Checkbox im +;; Einfuegedialog. +(defun c:GERUEST_TRAEGER_BESCHRIFTEN ( / ss i n-ok n-ersatz n-fehl n-weg e h ziel erg) + (setq *geruest-beschr-gemeldet* nil) + (setq n-ok 0 n-ersatz 0 n-fehl 0 n-weg 0) + (ssg-start nil nil) + ;; Verwaiste Beschriftungen zuerst entfernen + (if (setq ss (geruest-beschr-alle)) + (progn + (setq i 0) + (repeat (sslength ss) + (setq e (ssname ss i)) + (setq h (geruest-beschr-handle e)) + (setq ziel (if h (handent h))) + (if (not (and ziel (entget ziel) + (= (cdr (assoc 0 (entget ziel))) "INSERT"))) + (progn (entdel e) (setq n-weg (1+ n-weg))) + ) + (setq i (1+ i)) + ) + ) + ) + (if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "GR_Traeger_*")))) + (progn + (setq i 0) + (repeat (sslength ss) + (setq erg (geruest-traeger-beschriften (ssname ss i))) + (cond ((= erg "ok") (setq n-ok (1+ n-ok))) + ((= erg "ersatz") (setq n-ersatz (1+ n-ersatz))) + (T (setq n-fehl (1+ n-fehl)))) + (setq i (1+ i)) + ) + (princ (ssg-textf "gr-tr-beschr-ergebnis" + (list (itoa (+ n-ok n-ersatz)) (itoa n-ersatz) (itoa n-fehl) (itoa n-weg)))) + ) + (princ (ssg-text "gr-tr-beschr-keine")) + ) + (ssg-end) + (princ) +) + +(defun c:GERUEST_TRAEGER ( / bg wahl) + (setq bg (geruest-baugruppe "Traeger")) + (cond + ((null bg) + (princ (ssg-textf "gr-fehler-baugruppe" (list "Traeger")))) + ((null (ssg-json-get bg "geometrie_varianten")) + (princ (ssg-textf "gr-fehler-keine-varianten" (list "Traeger")))) + ((null (setq wahl (geruest-traeger-dialog bg))) + (princ (ssg-text "gr-abgebrochen"))) + (T (geruest-traeger-einfuegen bg wahl)) + ) + (princ) +) + +(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG, GERUEST_TRAEGER, GERUEST_TRAEGER_BESCHRIFTEN") (princ) diff --git a/data/geruest/R2-Saeule_dummy.dwg b/data/geruest/R2-Saeule_dummy.dwg index 7c508280971c1a05dd3d246ee202c691458fb249..d3b446c1615c2570a19707314d62317202423d36 100644 GIT binary patch delta 5747 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

t$izDi48^oh?)^=kuG9vWmv82VyiiO?DsP!G74l zJbV5_Tu1D?jr9DhQw4XI+OB`QEf@-p8f2|YlfZ#8L;@8o)`4DJaJDqqvFrNT5 zeCcg{%*Nc$_g=m}-Snxj>D>E*!GZ6EPu`q;BI2**OU)Km(vHxHFu^h;0=5UqVb1C~ zPx{|iEtLx$QcD-YrEva0&fFiRXDd^LQ=dsixY)}?I%?$#URu5}b#^0N!J8>}MB%bP zCeL9z&H%aX4E77F^&*!g19kla?PWcPPDa75M{Kx5Wv~4CczX6Mj~T&Q_sU94)1Bnr zN&)4|V#k4uueGC}W?oVCv&1(Kn(Bo;n8baDbj{tI?e-glN$Xo}*|CS$!|9lek;h&) z_T1q=A0N(k_a$$X&z(-5d4BrEJ?76F^S7J(s|L#_U!Q$FdGEAv7{lG&ZI8C~g&XzB zk_>|&PD@3P%^BlA-k&L%iXZ!U`d#Hn?5|i+GqzLmy4IOD)>#1=nyi&Jm-Au61Lp2- zGCAW_!X9j4dPY0=LNxNq8i)JGi{@$@Kd!&n9oO3Zt~;r{c&NX4?p@(49#_>1%olN= zIv)u7p3uH5>aXDEm@1U|ZKdYRQdROKYwp46(P&98vJYpVTDFwAibepWZ#}tN$I9M8 zDZTsF$aH(kubMCYZ@*t_T^Fd6|GQX}LeKlzeEsYF>B`#GxP{`Ew-a7<7dEBLeE9z7 zK|(u^wd2;*ziTFwo~;kGG&rPo%tfE!mv-~+Vxbh()6ssnGhb@!?G4tXEOE*uAwPB7 zO=kO~gY1=5eBAapVwRbbC$-37)VvMBMh7|eYz@^v5T)U-e85rZ()6{FTf#d@rqIfn zz*O+kHzxXpY%$(S-0Q@v6s5+vxcV+>K7MRYHiCc6qWy5WE5btC!9n^rHUOsKKgT=3 z@H8V6cf7(Bw8Kyjw_pU^CoV1cP@EjfhJU$okVKd`LGa#-@5mPAIc<2KYu{EG?fMq? zT|)R@**jqr-jFQ{oACG9E|4GB%2A|l=RHVIL^c+Oux5L$Jeuyjh%n0C|Lvd&;;}h$ X+}05D&_>w`DrqguZ$|$O=(_tc diff --git a/data/geruest/dummy_Muffe_1zoll.dwg b/data/geruest/dummy_Muffe_1zoll.dwg new file mode 100644 index 0000000000000000000000000000000000000000..0e6da7551f5c248849aa76f4f717ed3d9f73bb18 GIT binary patch literal 17473 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: ", + "gr-tr-prompt-ende": "\nEndpunkt waehlen oder Laenge (mm) eingeben: ", + "gr-tr-prompt-richtung": "\nRichtung angeben <0>: ", + "gr-tr-eingabe-ungueltig": "\nEingabe '%1' ist weder Punkt noch Laenge.", + "gr-tr-laenge-null": "\nStrecke %1 mm ist nicht laenger als die Abzuege (%2 mm) - kein Traeger eingefuegt.", + "gr-tr-laenge-bereich": "\nTraegerlaenge %1 mm ausserhalb des erlaubten Bereichs %2 bis %3 mm - kein Traeger eingefuegt.", + "gr-tr-fertig": "\nTraeger %1 mm eingefuegt (Block %2).", + "gr-tr-ende": "\n%1 Traeger eingefuegt.", + "gr-tr-bez-abzug-anfang": "Abzug Anfang %1 mm", + "gr-tr-bez-abzug-ende": "Abzug Ende %1 mm", + "gr-tr-beschr-stil-fehlt": "\nTextstil %1 fehlt in der Zeichnung - Beschriftung mit aktuellem Stil %2.", + "gr-tr-beschr-ausrichtung": "\nUnbekannte Ausrichtung %1 fuer die Traeger-Beschriftung - verwende BC.", + "gr-tr-beschr-ergebnis": "\n%1 Traeger beschriftet (davon %2 ohne bekannte Variante), %3 nicht beschriftbar, %4 verwaiste Beschriftungen entfernt.", + "gr-tr-beschr-keine": "\nKeine Rohr-Geruest-Traeger in der Zeichnung." } } diff --git a/lang/en_GB.json b/lang/en_GB.json index bea946d..95a93e9 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -1022,6 +1022,23 @@ "gr-konfig-teil-unbekannt": "\nScaffold: add-on part '%1' from configuration '%2' is unknown for this assembly - skipped.", "gr-konfig-variante-unbekannt": "\nScaffold: variant '%1' from configuration '%2' is unknown for this assembly - selection unchanged.", "gr-fehler-schreiben": "\nScaffold: could not write file: %1", - "gr-fehler-keine-baugruppen": "\nScaffold: the data file contains no assemblies." + "gr-fehler-keine-baugruppen": "\nScaffold: the data file contains no assemblies.", + "gr-tr-fehler-dcl": "Dialog %1 could not be loaded.", + "gr-tr-hinweis": "The length is defined in the drawing after OK.", + "gr-tr-alert-abzug": "Invalid length deduction. Allowed is a number from 0 (mm); an empty field counts as 0.", + "gr-tr-prompt-start": "\nSelect start point of the beam : ", + "gr-tr-prompt-ende": "\nSelect end point or enter length (mm): ", + "gr-tr-prompt-richtung": "\nSpecify direction <0>: ", + "gr-tr-eingabe-ungueltig": "\nInput '%1' is neither a point nor a length.", + "gr-tr-laenge-null": "\nDistance %1 mm is not longer than the deductions (%2 mm) - no beam inserted.", + "gr-tr-laenge-bereich": "\nBeam length %1 mm outside the allowed range %2 to %3 mm - no beam inserted.", + "gr-tr-fertig": "\nBeam %1 mm inserted (block %2).", + "gr-tr-ende": "\n%1 beam(s) inserted.", + "gr-tr-bez-abzug-anfang": "Deduction start %1 mm", + "gr-tr-bez-abzug-ende": "Deduction end %1 mm", + "gr-tr-beschr-stil-fehlt": "\nText style %1 missing in drawing - labelling with current style %2.", + "gr-tr-beschr-ausrichtung": "\nUnknown justification %1 for beam label - using BC.", + "gr-tr-beschr-ergebnis": "\n%1 beam(s) labelled (%2 without known variant), %3 not labellable, %4 orphaned label(s) removed.", + "gr-tr-beschr-keine": "\nNo pipe scaffold beams in the drawing." } } diff --git a/lib/export_csv.py b/lib/export_csv.py index e6e98ed..295f88a 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -319,16 +319,25 @@ def build_geruest_merkmale(block): pattern_geruest in cfg/export.cfg, GeruestInsert.lsp *geruest-attrib-defs*). ARTINR/LAENGE/BEZEICHNUNG wandern in feste CSV-Spalten (teileid/ - bezeichnung ueben in process_blocks unten), TEILEART/FARBE/LAYER bleiben - als Rest-Attribute in der Merkmale-JSON-Spalte - so wie bei Separator/ - Scanner/BTMT bereits ueblich.""" + bezeichnung ueben in process_blocks unten), TEILEART/TEILEFARBE/LAYER + bleiben als Rest-Attribute in der Merkmale-JSON-Spalte - so wie bei + Separator/Scanner/BTMT bereits ueblich. + + Die Laengenabzuege gibt es nur beim Rohr-Geruest-Traeger + (*geruest-traeger-attrib-defs*) und erscheinen darum nur, wenn der Block + sie traegt - die Merkmale der anderen GR_-Bloecke bleiben unveraendert.""" attribs = block.get("attribs", {}) - return { + merkmale = { "TeileArt": attribs.get("TEILEART", ""), - "Farbe": attribs.get("FARBE", ""), + "Farbe": attribs.get("TEILEFARBE", ""), "Laenge": attribs.get("LAENGE", ""), "Layer": attribs.get("LAYER", ""), } + for tag, name in (("LAENGENABZUGANFANG", "LaengenabzugAnfang"), + ("LAENGENABZUGENDE", "LaengenabzugEnde")): + if tag in attribs: + merkmale[name] = attribs[tag] + return merkmale def format_sensor_id(prefix, n): diff --git a/menu/SSG_LIB.cui b/menu/SSG_LIB.cui index 8299faa..0cd7164 100644 --- a/menu/SSG_LIB.cui +++ b/menu/SSG_LIB.cui @@ -539,7 +539,7 @@ Rohr-Geruest-Traeger - ^C^C(ssg-ensure "GeruestInsert") GERUEST + ^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER @@ -560,211 +560,223 @@ ^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG - + + + Gattertraeger + ^C^C(ssg-ensure "GeruestInsert") GERUEST + + + + + Traeger beschriften + ^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN + + + Marker einfuegen ^C^C(ssg-ensure "Tro_Edit") TRO_INSERT - + Marker bearbeiten ^C^C(ssg-ensure "Tro_Edit") TRO_EDIT - + Bearbeiten (Auto) ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT - + Verbindung einfuegen ^C^C(ssg-ensure "Connection_Edit") CONNECTION_INSERT - + Verbindung bearbeiten ^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT - + Eingang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_EINGANG_INSERT - + Ausgang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_AUSGANG_INSERT - + SPS Schliessen ^C^C - + Export Sivas ^C^C(ssg-ensure "export") EXPORTSIVAS - + Export CSV ^C^C(ssg-ensure "export") EXPORTCSV - + Umschalten 2D und 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH - + Alle Bloecke auf 2D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_2D - + Alle Bloecke auf 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_3D - + Sprache ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_SPRACHE - + Planquadrate an/aus ^C^C(ssg-ensure "DrawPlanquadrate") PLANQUADRATE_TOGGLE - + CSV-Export ^C^C(ssg-ensure "export") CSV_EXPORT_KONFIG - + alle Tests ausfuehren und exportieren ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_EXPORT) - + Tests ausfuehren und stehen lassen ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_OFFEN) - + Export CSV/Sivas ALL ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) (c:TEST_EXPORT_ALL) - + Kreisel ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) (c:TEST_KREISEL) - + Foerderer ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) (c:TEST_FOERDERER) - + Foerderer-Linienzug ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) (c:TEST_LINIENZUG) (c:TEST_LINIENZUG_STUFE2) - + Omniflo ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) (c:TEST_OMNIFLO) - + Omniflo Strecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo_strecke.lsp")) (c:TEST_OMNIFLO_STRECKE) - + Gefaellestrecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) (c:TEST_GEFAELLESTRECKE) - + Mubea Gesamtmodell ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA) - + HundM05 Kreisel-Abschnitt ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05) - + KS 2D/3D pruefen (CSV) ^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN - + Kreisel bearbeiten (ssg-ensure "KreiselInsert") KreiselEdit - + Omniflo Element bearbeiten (ssg-ensure "OmniModulInsert") OMNI_EDIT - + Gefaellestrecke bearbeiten (ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT - + VarioFoerderer bearbeiten (ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT - + TRO-Marker bearbeiten (ssg-ensure "Tro_Edit") TRO_EDIT - + Verbindung bearbeiten (ssg-ensure "Connection_Edit") CONNECTION_EDIT - + SSG Bearbeiten (Auto) (ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT @@ -782,10 +794,10 @@ - - - - + + + + ILS @@ -1413,249 +1425,259 @@ Gattertraeger - + + + + + + + Funktionen + + Traeger beschriften + + - + SPS - - + + Eingang - + Ausgang - + SPS Schliessen - + - + TRO Marker einfuegen - + Marker bearbeiten - + Bearbeiten (Auto) - + - + Connections Verbindung einfuegen - + Verbindung bearbeiten - + Bearbeiten (Auto) - + - + Export Export Sivas - + Export CSV - - - - - - - Konfiguration - - Umschalten 2D und 3D + + + Konfiguration - Alle Bloecke auf 2D - - - - - - Alle Bloecke auf 3D + Umschalten 2D und 3D - Sprache + Alle Bloecke auf 2D - Planquadrate an/aus + Alle Bloecke auf 3D - CSV-Export + Sprache + + Planquadrate an/aus + + + + + + CSV-Export + + + + - + Tests alle Tests ausfuehren und exportieren - + Tests ausfuehren und stehen lassen - + Export CSV/Sivas ALL - + Kreisel - + Foerderer - + Foerderer-Linienzug - + Omniflo - + Omniflo Strecke - + Gefaellestrecke - + Mubea Gesamtmodell - + HundM05 Kreisel-Abschnitt - + KS 2D/3D pruefen (CSV) - + - + POP501 SSG_LIB Edit Context Kreisel bearbeiten - + Omniflo Element bearbeiten - + Gefaellestrecke bearbeiten - + VarioFoerderer bearbeiten - + TRO-Marker bearbeiten - + Verbindung bearbeiten - + SSG Bearbeiten (Auto) - + diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index ac9369e..578eece 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -112,11 +112,13 @@ [<-~APBW 110 R550/67,5 58x497]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_58 [<-Element bearbeiten]^C^C(ssg-ensure "OmniModulInsert") OMNI_EDIT [->Geruest]^C^C(ssg-ensure "GeruestInsert") GERUEST -[Rohr-Geruest-Traeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[Rohr-Geruest-Traeger]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER [Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULEN [Rohrabhaenger]^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER [Wandbefestigung]^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG -[<-Gattertraeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[Gattertraeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[->Funktionen] +[<-<-Traeger beschriften]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN [->SPS] [->TRO] [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 8991c61..23d0ef2 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -754,9 +754,9 @@ (if (findfile _datei) (progn (setq _daten (ssg-json-laden _datei)) - ;; Drei Bereiche: Rohrabhaenger, Saeulen (1 und 2 Zoll zusammengefuehrt), - ;; Wandbefestigung - (tu-eq "json/geruest-baugruppen" 3 (length _daten)) + ;; Vier Baugruppen: Rohrabhaenger, Saeulen (1 und 2 Zoll + ;; zusammengefuehrt), Wandbefestigung, Rohr-Geruest-Traeger + (tu-eq "json/geruest-baugruppen" 4 (length _daten)) ;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel ;; traegt Umlaute bzw. Anfuehrungszeichen) (setq _bg nil) @@ -773,11 +773,16 @@ (ssg-json-get _bg "teileart")) (tu-eq "json/geruest-dummy" "Rohrabhaenger_dummy.dwg" (ssg-json-get _bg "dummy")) - ;; Sivas-ID als Zahl geparst, Laengengrenze aus dem attribute-Block + ;; Sivas-ID als Zahl geparst, Laengengrenze aus dem attribute-Block. + ;; Die Keys unter "attribute" sind die Block-Attribut-Tags und darum + ;; GROSS geschrieben (ssg-json-get unterscheidet Gross/Klein). (tu-eq "json/geruest-sivas" 821006026 (ssg-json-get (car (ssg-json-get (car _var) "teile")) "sivas_id")) (tu-eq "json/geruest-max" 6000 - (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "Laenge") "max")) + (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "LAENGE") "max")) + (tu-eq "json/geruest-farboptionen" '("RAL7035" "RAL7030") + (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "TEILEFARBE") + "optionen")) ) ) ) @@ -978,6 +983,245 @@ (geruest-variante-wert (nth _rec (ssg-json-get _saeulen "geometrie_varianten")) _saeulen "teileart"))) + ;; Attribut-Keys GROSS - sonst findet geruest-dialog min/max und + ;; Farben nicht (Laengenfeld ohne Pruefung, leere Farbliste). + (tu-eq "geruest/saeulen-laenge-max" 6000 + (geruest-attr-feld nil _saeulen "LAENGE" "max")) + (tu-true "geruest/saeulen-farboptionen" + (geruest-attr-feld nil _saeulen "TEILEFARBE" "optionen")) + ) + ) + + ;; --- Rohr-Geruest-Traeger: reine Berechnung --- + ;; Netto-Laenge = Strecke minus beide Abzuege, nil wenn nichts uebrig + (tu-eqf "geruest-traeger-laenge/ende" 965.0 (geruest-traeger-laenge 1000.0 0.0 35.0)) + (tu-eqf "geruest-traeger-laenge/beide" 945.0 (geruest-traeger-laenge 1000.0 20.0 35.0)) + (tu-eqf "geruest-traeger-laenge/ohne" 1000.0 (geruest-traeger-laenge 1000.0 nil nil)) + (tu-nil "geruest-traeger-laenge/zu-kurz" (geruest-traeger-laenge 50.0 30.0 30.0)) + (tu-nil "geruest-traeger-laenge/genau-null" (geruest-traeger-laenge 60.0 30.0 30.0)) + ;; Startpunkt wird um den Anfangsabzug entlang der Richtung eingerueckt, + ;; Z bleibt die des Startklicks + (tu-eqf "geruest-traeger-start/0grad" '(20.0 0.0 500.0) + (geruest-traeger-start '(0.0 0.0 500.0) 0.0 20.0)) + (tu-eqf "geruest-traeger-start/90grad" '(100.0 220.0 0.0) + (geruest-traeger-start '(100.0 200.0 0.0) (/ pi 2.0) 20.0)) + (tu-eqf "geruest-traeger-start/2d-punkt" '(5.0 0.0 0.0) + (geruest-traeger-start '(5.0 0.0) 0.0 0.0)) + ;; Querstreben: Abstand max. 500, Rand 100..250 beidseitig gleich, + ;; so wenige wie moeglich + ;; Abstand genau 500, solange der Rand in 100..250 bleibt + (tu-eqf "geruest-traeger-streben/1200" '(100.0 600.0 1100.0) + (geruest-traeger-streben 1200.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1300" '(150.0 650.0 1150.0) + (geruest-traeger-streben 1300.0 500.0 100.0 250.0)) + ;; 1100 geht mit genau 500 nicht auf -> Rand 100, Abstand 450 + (tu-eqf "geruest-traeger-streben/1100" '(100.0 550.0 1000.0) + (geruest-traeger-streben 1100.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1500" '(250.0 750.0 1250.0) + (geruest-traeger-streben 1500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1000" '(250.0 750.0) + (geruest-traeger-streben 1000.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/eine" '(200.0) + (geruest-traeger-streben 400.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/grenze-500" '(250.0) + (geruest-traeger-streben 500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/501" '(100.0 401.0) + (geruest-traeger-streben 501.0 500.0 100.0 250.0)) + (tu-nil "geruest-traeger-streben/zu-kurz" + (geruest-traeger-streben 150.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/min-200" '(100.0) + (geruest-traeger-streben 200.0 500.0 100.0 250.0)) + ;; 6000 mm: 11 Felder a 500 -> 12 Streben, letzte bei 5750 + (setq _rec (geruest-traeger-streben 6000.0 500.0 100.0 250.0)) + (tu-eq "geruest-traeger-streben/6000-anzahl" 12 (length _rec)) + (tu-eqf "geruest-traeger-streben/6000-letzte" 5750.0 (last _rec)) + ;; Konfiguration: fehlende Felder -> Standard, ohne querstrebe -> nil + ;; Ergebnis (dummy laenge querlinie abstand-max rand-min rand-max) + (tu-eqf "geruest-traeger-strebe-cfg/standard" '(nil 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"rand_min\": 100}}"))) + (tu-eqf "geruest-traeger-strebe-cfg/dummy" '("s.dwg" 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"dummy\": \"s.dwg\"}}"))) + (tu-eqf "geruest-traeger-strebe-cfg/wert" '(nil 30.0 25.0 400.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"abstand_max\": 400}}"))) + ;; Querlinien an Anfang und Ende der 30 mm langen Strebe + (tu-eqf "geruest-traeger-strebe-enden" '(235.0 265.0) + (geruest-traeger-strebe-enden 250.0 30.0)) + (tu-nil "geruest-traeger-strebe-cfg/ohne" + (geruest-traeger-strebe-cfg nil (ssg-json-parse "{\"id\": \"X\"}"))) + ;; Muffe nur bei "mit_muffe": true; Variante darf eigene Muffe mitbringen + (setq _bgtest (ssg-json-parse "{\"muffe\": {\"dummy\": \"m.dwg\"}}")) + (tu-eq "geruest-traeger-muffe-dummy/flag" "m.dwg" + (geruest-traeger-muffe-dummy + (ssg-json-parse "{\"name\": \"A\", \"mit_muffe\": true}") _bgtest)) + (tu-nil "geruest-traeger-muffe-dummy/ohne-flag" + (geruest-traeger-muffe-dummy + (ssg-json-parse "{\"name\": \"Rohr mit Muffe\"}") _bgtest)) + (tu-eq "geruest-traeger-muffe-dummy/variante" "m2.dwg" + (geruest-traeger-muffe-dummy + (ssg-json-parse + "{\"mit_muffe\": true, \"muffe\": {\"dummy\": \"m2.dwg\"}}") + _bgtest)) + (tu-nil "geruest-traeger-muffe-dummy/ohne-variante" + (geruest-traeger-muffe-dummy nil _bgtest)) + + ;; --- Traeger-Beschriftung: reine Berechnung --- + ;; Textwinkel entlang des Traegers, DIN: 90 < w <= 270 wird gedreht + (tu-eqf "geruest-text-winkel/0" 0.0 (geruest-text-winkel 0.0)) + (tu-eqf "geruest-text-winkel/45" (/ pi 4.0) (geruest-text-winkel (/ pi 4.0))) + (tu-eqf "geruest-text-winkel/90" (/ pi 2.0) (geruest-text-winkel (/ pi 2.0))) + (tu-eqf "geruest-text-winkel/91" (* 271 (/ pi 180.0)) + (geruest-text-winkel (* 91 (/ pi 180.0)))) + (tu-eqf "geruest-text-winkel/180" 0.0 (geruest-text-winkel pi)) + (tu-eqf "geruest-text-winkel/270" (/ pi 2.0) (geruest-text-winkel (* 1.5 pi))) + (tu-eqf "geruest-text-winkel/271" (* 271 (/ pi 180.0)) + (geruest-text-winkel (* 271 (/ pi 180.0)))) + ;; -90 = 270 -> gedreht auf 90 (von unten nach oben lesbar) + (tu-eqf "geruest-text-winkel/-90" (/ pi 2.0) (geruest-text-winkel (- (/ pi 2.0)))) + ;; Ausrichtungs-Codes wie TEXT "Ausrichten" -> (dxf72 dxf73) + (tu-eq "geruest-ausrichtung/TL" '(0 3) (geruest-ausrichtung-codes "TL")) + (tu-eq "geruest-ausrichtung/TC" '(1 3) (geruest-ausrichtung-codes "TC")) + (tu-eq "geruest-ausrichtung/TR" '(2 3) (geruest-ausrichtung-codes "TR")) + (tu-eq "geruest-ausrichtung/ML" '(0 2) (geruest-ausrichtung-codes "ML")) + (tu-eq "geruest-ausrichtung/MC" '(1 2) (geruest-ausrichtung-codes "MC")) + (tu-eq "geruest-ausrichtung/MR" '(2 2) (geruest-ausrichtung-codes "MR")) + (tu-eq "geruest-ausrichtung/BL" '(0 1) (geruest-ausrichtung-codes "BL")) + (tu-eq "geruest-ausrichtung/BC" '(1 1) (geruest-ausrichtung-codes "BC")) + (tu-eq "geruest-ausrichtung/BR" '(2 1) (geruest-ausrichtung-codes "BR")) + (tu-eq "geruest-ausrichtung/klein" '(1 1) (geruest-ausrichtung-codes "bc")) + (tu-nil "geruest-ausrichtung/unbekannt" (geruest-ausrichtung-codes "XY")) + (tu-nil "geruest-ausrichtung/nil" (geruest-ausrichtung-codes nil)) + ;; Textlage: Bezugspunkt auf der Linie, "links" in Leserichtung + (tu-eqf "geruest-text-lage/0-BC" '((1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BC")) + (tu-eqf "geruest-text-lage/0-BL" '((0.0 0.0 0.0) 0.0 0 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BL")) + (tu-eqf "geruest-text-lage/0-BR" '((2000.0 0.0 0.0) 0.0 2 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BR")) + ;; Traeger nach links (180): Text gedreht auf 0, links = Linienende + (tu-eqf "geruest-text-lage/180-BL" '((-2000.0 0.0 0.0) 0.0 0 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BL")) + (tu-eqf "geruest-text-lage/180-BR" '((0.0 0.0 0.0) 0.0 2 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BR")) + (tu-eqf "geruest-text-lage/180-BC" '((-1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BC")) + ;; Unbekannter Code -> BC + (tu-eqf "geruest-text-lage/ersatz" '((1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "XY")) + ;; Text: %1 = Variantentext, %2 = Laenge auf ganze mm + (tu-eq "geruest-text-string/beispiel" "RT 80/80 L=2650" + (geruest-traeger-text-string "%1 L=%2" "RT 80/80" 2649.5)) + (tu-eq "geruest-text-string/abrunden" "L=965 RT" + (geruest-traeger-text-string "L=%2 %1" "RT" 965.4)) + (tu-eq "geruest-text-string/mehrfach" "A A 10" + (geruest-traeger-text-string "%1 %1 %2" "A" 10.0)) + ;; Konfiguration je Feld: Variante vor Baugruppe vor Code-Standard + (setq _bgtest (ssg-json-parse + (strcat "{\"beschriftung\": {\"format\": \"%1 L=%2\", \"stil\": \"S1\"," + " \"ausrichtung\": \"BC\", \"hoehe\": 50, \"layer\": \"D-5-BEM\"}}"))) + (setq _rec (geruest-traeger-beschr-cfg + (ssg-json-parse + "{\"name\": \"RT 80/80 1 Zoll\", \"beschriftung\": {\"text\": \"RT 80/80\", \"layer\": \"D-6-BEM\"}}") + _bgtest "ERSATZ")) + (tu-eq "geruest-beschr-cfg/text" "RT 80/80" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/layer" "D-6-BEM" (cdr (assoc "layer" _rec))) + (tu-eq "geruest-beschr-cfg/stil" "S1" (cdr (assoc "stil" _rec))) + (tu-eqf "geruest-beschr-cfg/hoehe" 50.0 (cdr (assoc "hoehe" _rec))) + (setq _rec (geruest-traeger-beschr-cfg + (ssg-json-parse "{\"name\": \"Ohne Text\"}") _bgtest "ERSATZ")) + (tu-eq "geruest-beschr-cfg/text-name" "Ohne Text" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/layer-bg" "D-5-BEM" (cdr (assoc "layer" _rec))) + (setq _rec (geruest-traeger-beschr-cfg nil (ssg-json-parse "{\"id\": \"X\"}") "ERSATZ")) + (tu-eq "geruest-beschr-cfg/ersatz" "ERSATZ" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/std-format" "%1 L=%2" (cdr (assoc "format" _rec))) + (tu-eq "geruest-beschr-cfg/std-ausr" "BC" (cdr (assoc "ausrichtung" _rec))) + (tu-eqf "geruest-beschr-cfg/std-hoehe" 50.0 (cdr (assoc "hoehe" _rec))) + (tu-nil "geruest-beschr-cfg/std-layer" (cdr (assoc "layer" _rec))) + ;; Bereichspruefung auf der Netto-Laenge + (tu-true "geruest-traeger-pruefen/innen" (geruest-traeger-pruefen 965.0 0 6000)) + (tu-nil "geruest-traeger-pruefen/ueber" (geruest-traeger-pruefen 6001.0 0 6000)) + (tu-true "geruest-traeger-pruefen/grenze" (geruest-traeger-pruefen 6000.0 0 6000)) + (tu-true "geruest-traeger-pruefen/ohne" (geruest-traeger-pruefen 99999.0 nil nil)) + (tu-nil "geruest-traeger-pruefen/nil" (geruest-traeger-pruefen nil 0 6000)) + ;; Eingaben in mm: Komma wie Punkt, negativ/Text ungueltig + (tu-eqf "geruest-mm-zahl/ganz" 35.0 (geruest-mm-zahl "35")) + (tu-eqf "geruest-mm-zahl/komma" 17.5 (geruest-mm-zahl "17,5")) + (tu-nil "geruest-mm-zahl/text" (geruest-mm-zahl "abc")) + (tu-nil "geruest-mm-zahl/negativ" (geruest-mm-zahl "-5")) + (tu-nil "geruest-mm-zahl/leer" (geruest-mm-zahl "")) + (tu-eqf "geruest-abzug-wert/leer" 0.0 (geruest-abzug-wert "")) + (tu-nil "geruest-abzug-wert/text" (geruest-abzug-wert "x")) + (tu-eqf "geruest-tr-abzug/inaktiv" 0.0 (geruest-tr-abzug "0" "x")) + (tu-eqf "geruest-tr-abzug/aktiv" 35.0 (geruest-tr-abzug "1" "35")) + (tu-eq "geruest-mm-string/ganz" "965" (geruest-mm-string 965.0)) + (tu-eq "geruest-mm-string/dezimal" "17.5" (geruest-mm-string 17.5)) + ;; Variante darf Attribut-Felder ueberschreiben - auch mit false + (setq _bgtest (ssg-json-parse + "{\"attribute\": {\"LAENGENABZUGENDE\": {\"vorgabe\": 35, \"aktiv\": true}}}")) + (tu-eq "geruest-attr-feld/baugruppe" 35 + (geruest-attr-feld nil _bgtest "LAENGENABZUGENDE" "vorgabe")) + (tu-eq "geruest-attr-feld/variante" 50 + (geruest-attr-feld + (ssg-json-parse "{\"attribute\": {\"LAENGENABZUGENDE\": {\"vorgabe\": 50}}}") + _bgtest "LAENGENABZUGENDE" "vorgabe")) + (tu-nil "geruest-attr-feld/variante-false" + (geruest-attr-feld + (ssg-json-parse "{\"attribute\": {\"LAENGENABZUGENDE\": {\"aktiv\": false}}}") + _bgtest "LAENGENABZUGENDE" "aktiv")) + + ;; --- Rohr-Geruest-Traeger in der echten Datendatei --- + ;; Eigener Dialog: im Bereichs-Dropdown von GERUEST darf er fehlen, + ;; ueber geruest-baugruppe muss er aber auffindbar sein. + (setq _rec nil) + (foreach _e (geruest-baugruppen-liste) + (if (equal (car _e) "Traeger") (setq _rec T))) + (tu-nil "geruest/traeger-nicht-im-dropdown" _rec) + (setq _traeger (geruest-baugruppe "Traeger")) + (tu-true "geruest/traeger-vorhanden" _traeger) + (if _traeger + (progn + (tu-eq "geruest/traeger-einfuegeart" "linie" + (ssg-json-get _traeger "einfuegeart")) + (tu-eq "geruest/traeger-varianten-artikel" 0 + (tu-geruest-artikel-unvollstaendig + (ssg-json-get _traeger "geometrie_varianten"))) + (tu-true "geruest/traeger-layer" + (geruest-variante-wert + (car (ssg-json-get _traeger "geometrie_varianten")) + _traeger "layer")) + (tu-eq "geruest/traeger-max" 6000 + (geruest-attr-feld nil _traeger "LAENGE" "max")) + (tu-eqf "geruest/traeger-querstrebe" + '("dummy_Strebe_30x4.dwg" 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil _traeger)) + ;; Muffen-Flag ausdruecklich je Variante: genau die "mit (2) Muffe(n)" + (setq _rec 0) + (foreach _v (ssg-json-get _traeger "geometrie_varianten") + (if (geruest-traeger-muffe-dummy _v _traeger) (setq _rec (1+ _rec)))) + (tu-eq "geruest/traeger-muffen-varianten" 4 _rec) + ;; Jede Variante definiert Beschriftungstext UND -layer selbst - der + ;; Layer ist je Traegervariante verschieden, eine Vorgabe der + ;; Baugruppe gibt es dafuer bewusst nicht. + (setq _rec 0) + (foreach _v (ssg-json-get _traeger "geometrie_varianten") + (setq _cfg (geruest-traeger-beschr-cfg _v _traeger "")) + (if (and (geruest-obj-feld _v _traeger '("beschriftung") "text") + (/= (cdr (assoc "text" _cfg)) "") + (geruest-json-pfad-get _v '("beschriftung" "layer")) + (geruest-ausrichtung-codes (cdr (assoc "ausrichtung" _cfg)))) + (setq _rec (1+ _rec)))) + (tu-eq "geruest/traeger-beschriftung-varianten" + (length (ssg-json-get _traeger "geometrie_varianten")) _rec) + ;; VARIANTE gehoert zum Traeger-Schema (Grundlage fuer das Neubeschriften) + (tu-true "geruest/traeger-attrib-variante" + (assoc "VARIANTE" *geruest-traeger-attrib-defs*)) + ;; Beide Dummy-DWGs liegen unter data/geruest + (tu-true "geruest/traeger-strebe-dwg" + (findfile (geruest-dummy-pfad "dummy_Strebe_30x4.dwg"))) + (tu-true "geruest/traeger-muffe-dwg" + (findfile (geruest-dummy-pfad "dummy_Muffe_1zoll.dwg"))) ) )