From d3ddcb9d594fb3a6a2c9969ee114c207fcd4f780 Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Fri, 9 Oct 2026 14:38:11 +0200 Subject: [PATCH] =?UTF-8?q?[ADD]=20weitere=20Ger=C3=BCstkomponenten=20hinz?= =?UTF-8?q?ugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 2 +- Lisp/GeruestInsert.lsp | 103 +++++++++++++++++++++++---------- data/geruest/400000782.dwg | Bin 0 -> 18148 bytes data/geruest/821004200.dwg | Bin 40599 -> 44600 bytes data/json/geruest.json | 22 +------ data/json/geruest_traeger.json | 2 +- lang/de_DE.json | 3 +- lang/en_GB.json | 3 +- tests/test_unit.lsp | 73 ++++++++++++++--------- 9 files changed, 127 insertions(+), 81 deletions(-) create mode 100644 data/geruest/400000782.dwg diff --git a/CLAUDE.md b/CLAUDE.md index 84ed02c..698128a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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_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; fuer `dummy` ueber `geruest-dummy-wahl`: fehlt die DWG der Variante, Meldung + Rueckfall auf den Baugruppen-Dummy; ist der Dummy zugleich eine Teile-DWG, wird er per `geruest-ohne-pfad` nicht doppelt eingefuegt) - 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 Anzahl. **Zwei Mengenfelder im JSON, bewusst verschieden benannt**: `stueckzahl` = wie oft ein Teil TATSAECHLICH geschrieben wird (je Variante bzw. je Satz/Anbauteil, fehlt = 1), `anzahl_vorgabe` = NUR die Vorbelegung des Anzahl-Feldes im Dialog (Anbaugruppen, fehlt = 1); ein Anbauteil wird Anzahl x `stueckzahl` mal geschrieben, bei einer Gruppe (`teile`) zaehlt die Anzahl die Saetze (`geruest-stueckzahl`/`geruest-anzahl-vorgabe`). In Konfigurationen (`oben`/`unten`) heisst der gespeicherte Dialogwert `anzahl`. 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 Anzahl x `stueckzahl` es vorgeben - 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`): Traeger-Baugruppen mit Linien-Logik liegen in einer EIGENEN Datei `data/json/geruest_traeger.json` (gleicher Aufbau wie geruest.json, eigener Lader/Cache `geruest-traeger-daten`, Suche `geruest-baugruppe id daten`; gemeinsamer Lader `geruest-json-laden-cache`) - sie tauchen darum nicht im Bereichs-Dropdown von GERUEST auf; dort kann spaeter auch der Gattertraeger hin. 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) | +| `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; fuer `dummy` ueber `geruest-dummy-wahl`: fehlt die DWG der Variante, Meldung + Rueckfall auf den Baugruppen-Dummy; ist der Dummy zugleich eine Teile-DWG, wird er per `geruest-ohne-pfad` nicht doppelt eingefuegt) - 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 Anzahl. **Zwei Mengenfelder im JSON, bewusst verschieden benannt**: `stueckzahl` = wie oft ein Teil TATSAECHLICH geschrieben wird (je Variante bzw. je Satz/Anbauteil, fehlt = 1), `anzahl_vorgabe` = NUR die Vorbelegung des Anzahl-Feldes im Dialog (Anbaugruppen, fehlt = 1); ein Anbauteil wird Anzahl x `stueckzahl` mal geschrieben, bei einer Gruppe (`teile`) zaehlt die Anzahl die Saetze (`geruest-stueckzahl`/`geruest-anzahl-vorgabe`). In Konfigurationen (`oben`/`unten`) heisst der gespeicherte Dialogwert `anzahl`. 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 Anzahl x `stueckzahl` es vorgeben - 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`): Traeger-Baugruppen mit Linien-Logik liegen in einer EIGENEN Datei `data/json/geruest_traeger.json` (gleicher Aufbau wie geruest.json, eigener Lader/Cache `geruest-traeger-daten`, Suche `geruest-baugruppe id daten`; gemeinsamer Lader `geruest-json-laden-cache`) - sie tauchen darum nicht im Bereichs-Dropdown von GERUEST auf; dort kann spaeter auch der Gattertraeger hin. 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`/`rand_min`/`rand_max`, Variante vor Baugruppe), Plan `geruest-traeger-streben-plan` -> (rand positionen): der Abstand zwischen zwei Streben ist IMMER genau `abstand`, beide Raender gleich (netto = k*abstand + 2*rand); gewaehlt wird die Strebenzahl, deren Rand am naechsten an `rand_min`..`rand_max` liegt (Gleichstand -> weniger Streben). Mit 500/100..250 geht das nur auf, wenn netto mod 500 = 0 oder in [200, 500) liegt (z.B. 1300 -> 150|500|500|150); sonst (1001..1199, unter 200 mm) liegt der Rand ausserhalb (1100 -> 300|500|300) - `geruest-traeger-rand-pruefen` zeigt dann eine Messagebox (ohne GUI: Konsole), der Traeger wird trotzdem eingefuegt. 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). | diff --git a/Lisp/GeruestInsert.lsp b/Lisp/GeruestInsert.lsp index e331874..e38fde2 100644 --- a/Lisp/GeruestInsert.lsp +++ b/Lisp/GeruestInsert.lsp @@ -1553,43 +1553,81 @@ (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) +;; Abweichung eines Randes vom erlaubten Bereich [rand-min, rand-max] +;; (0 = innerhalb). +(defun geruest-rand-abweichung (r rand-min rand-max) + (cond ((< r rand-min) (- rand-min r)) + ((> r rand-max) (- r rand-max)) + (T 0.0)) +) + +;; Querstreben-Plan: (rand positionen). Regel: der Abstand zwischen zwei +;; Streben ist IMMER genau abstand, beide Raender sind gleich: +;; netto = k * abstand + 2 * rand (k = Felder, k+1 Streben) +;; Gewaehlt wird das k, dessen Rand am naechsten am Bereich [rand-min, +;; rand-max] liegt (bei Gleichstand das mit weniger Streben). Weil der +;; Bereich schmaler ist als abstand, geht nicht jede Laenge auf: mit 500/ +;; 100..250 liegt der Rand genau dann im Bereich, wenn netto mod 500 = 0 oder +;; in [200, 500) ist - bei 1001..1199 mm oder unter 200 mm z.B. nicht. Der Plan +;; wird dann trotzdem geliefert, die Pruefung (geruest-rand-abweichung) und +;; die Meldung uebernimmt der Aufrufer. netto <= 0 -> nil. +(defun geruest-traeger-streben-plan (netto abstand rand-min rand-max + / k kmax r dev best-k best-r best-dev i erg) + (if (and netto (> netto 1e-6) abstand (> abstand 1e-6)) + (progn + (setq kmax (fix (+ (/ netto abstand) 1e-9)) k 0 best-k nil) + (while (<= k kmax) + (setq r (/ (- netto (* k abstand)) 2.0)) + (if (>= r -1e-9) + (progn + (setq dev (geruest-rand-abweichung r rand-min rand-max)) + ;; strikt kleiner: bei Gleichstand bleibt das kleinere k + (if (or (null best-k) (< dev (- best-dev 1e-6))) + (setq best-k k best-r (max r 0.0) best-dev dev)) + ) + ) + (setq k (1+ k)) + ) + (setq erg nil i best-k) (while (>= i 0) - (setq erg (cons (+ r (* i s)) erg)) + (setq erg (cons (+ best-r (* i abstand)) erg)) (setq i (1- i)) ) - erg + (list best-r erg) ) ) ) +;; Rand des Streben-Plans gegen [rand-min, rand-max] pruefen und bei +;; Abweichung den Benutzer informieren (Messagebox, ohne GUI auf die +;; Konsole). strebe = Ergebnis von geruest-traeger-strebe-cfg. +;; Rueckgabe: T = Rand im Bereich. +(defun geruest-traeger-rand-pruefen (netto strebe / plan txt) + (setq plan (geruest-traeger-streben-plan netto (nth 3 strebe) (nth 4 strebe) (nth 5 strebe))) + (if (and plan + (> (geruest-rand-abweichung (car plan) (nth 4 strebe) (nth 5 strebe)) 1e-6)) + (progn + (setq txt (ssg-textf "gr-tr-strebe-rand" + (list (geruest-mm-string netto) + (geruest-mm-string (car plan)) + (geruest-mm-string (nth 4 strebe)) + (geruest-mm-string (nth 5 strebe)) + (geruest-mm-string (nth 3 strebe)) + (itoa (length (cadr plan)))))) + (if (ssg-gui-p) (alert txt) (princ (strcat "\n" txt))) + nil + ) + T + ) +) + +;; Nur die Positionen der Querstreben (Abstand ab Linienanfang). +(defun geruest-traeger-streben (netto abstand rand-min rand-max) + (cadr (geruest-traeger-streben-plan netto abstand rand-min rand-max)) +) + ;; Querstreben-Konfiguration (Variante vor Baugruppe) als -;; (dummy laenge querlinie abstand-max rand-min rand-max) oder nil, wenn keine +;; (dummy laenge querlinie abstand rand-min rand-max) oder nil, wenn keine ;; "querstrebe" definiert ist. ;; dummy = DWG unter data/geruest (Basispunkt Strebenmitte, 30 mm laengs ;; X) oder nil @@ -1605,7 +1643,7 @@ (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") + '("laenge" "querlinie" "abstand" "rand_min" "rand_max") '(30.0 25.0 500.0 100.0 250.0))) ) ) @@ -1863,7 +1901,7 @@ (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) + ;; strebe = (dummy laenge querlinie abstand rand-min rand-max) (if strebe (foreach x (geruest-traeger-streben netto (nth 3 strebe) (nth 4 strebe) (nth 5 strebe)) (if strebe-pfad @@ -1969,6 +2007,9 @@ (if min-l (geruest-mm-string min-l) "0") (if max-l (geruest-mm-string max-l) "-"))))) (T + ;; Streben-Rand ausserhalb der Grenzen: informieren, aber trotzdem + ;; einfuegen (der Abstand zwischen den Streben bleibt immer exakt) + (if strebe (geruest-traeger-rand-pruefen netto strebe)) (setq werte (cons (cons "LAENGE" (geruest-mm-string netto)) werte-basis)) (ssg-start nil '(("OSMODE") ("ATTREQ") ("ATTDIA") ("ANGBASE") ("ANGDIR") ("AUNITS"))) diff --git a/data/geruest/400000782.dwg b/data/geruest/400000782.dwg new file mode 100644 index 0000000000000000000000000000000000000000..7c2a7bc084a2885746097e124747a35c75731bb4 GIT binary patch literal 18148 zcmc({4OmoF*D$>HIcLuN!wf(2kB2ieFoMVcgMfgX83q`1)KO7UQ3o9q5d{?#66=Vh zsHj+EXlhuKB3W8#X<1H2WyW1-R#s$KXl8$wR`%<+#=762kG=!_^}}J7FNhcg)|dK^kd8@T{O^Pc;nq+0 z#UK4P^ke0B-@Wj~m!ItWD0{7H^bY-ObtL;-;l*bH#+|mluxXz^J!AET)rO(7&cFHF zH_zl}1*$hFB0qXEWyDVBH?I1Tzu#As^5ezTMg1>M2>RZ`%3{us9r1e^ae92J z+cFlT4@X~&JbXoXv^Dj_P{U&B`H@EB?rCRociS`xi3|WmKF9H#I0&0g0i2=#>%;w5 zYi@9kFH|eS6e_8qV^srZ(UCU064}RgC|4rf;emHPm>^}u^zrw+`PS$yg`YJ&mzyI%{qF^DPyhTi zNz(os^C!d|UNq|q=^^W_%lCY2&dvIAb6D5id&EFER8{b5`_`>1bFuxgz*nZQW?;n5 zA0D1B&;qo~yhja$Tlsq$X4ZxSCG=lD$%>$P@ec%$Z<-Z9`^(^Lzl0(n0BB(9r9CPc z41f6$xdURe%g5tr3dU+8Gyo_?#1(wE8ryzrNacJy|LCxH7Wd$74)jm|fj`mI!CV;} zS7XCttWhC@4L*3odic_x^3IMh-R(^O{oPNLGWasRbMBdOSSIcD-b}q+c>CfsLR_DO z-J13D7yX~CQgIM@-HiQP=Kqni^`~uX{(8l~rro%><=5OhLnp5X-@uU{{Oy6CzL}$e zE%(Td?OgSaKPA+%K)4aK_+OHX{**uH1>Cr4zr6f73^4x;f?p-pWBt@o%_PK=Y3`W5Nak$L74LA+Ox>F z2Kmw$WuyJ?ug!V=bKiW|fCXysuL#$}`A`iuD1%yz4p@lmV=dPBS1V8;6yplI5EkJ2 zSq4U!1jBH0r$8F6t(h22cj7;w@>g;`wyU_)Gl!!sgevTXgO%9Fa(q_ZdDdcTIi^-% znH$q{p#gu(5I+tkAf~>Rn?pRyv6c@5A7sI{RS5ZN&AZd55$oD8--lP(9SVKr^DteD z&+0p^s$tok(m#9l)%M}wtM%vZ@aOpdy7h?vH*s(PvT@~JhP}I?4tuHnUtC=Vi~rNe zOAuOo2hSYf!HR8sqb`R$#KDF+9Hy$VMh(_mg7lC7hx#0r|5y3o>uE0H;^VX%=G@`w zvO9e9alkkBpKr=nZyDmV0Bic*`Z65zU)BEm8}`*-h&}k~mfhj~9E5!%{Mp)9w%|_x zf4+r(-G3F*=zAkRDJVj^efT6JMydG6LlTVs7d-yInWdR6g}52GKV#g4G2ySsk3VU? zKIOi<2@V#3(Q*e%H;lR?_4P=zuf#VK^DwUzrXlv@5&sF8nv8J49r>z7UX&trzIW@R zaRj!jMXCSiFqN#Bd-?}|%$EF9(P6}IE#joYI2NN1M_&|XXTR&5@yhD|rP2{6;r^-Z zzXdE%ffitx?xp5SRqs7HsjcXa8u$#Vf$M#0;PqKj=$-DiymU9}W*FbZv@h=aaOTqZu@X@G(aT^UDNHXu+YXcNzF-v zCdQ;U*-UOzv&m!XF%2d&quoM*9Z8R z!a>2#2zI?-w+VK)U~d-e9>Lxt*aw9oMkvw?MK+M4-+6i5dP zrw7dPFvwf=1m+#}(@txQK zj_QR)k3_%|L?c=&tultUC3%v1QiNc*w?~*WD7YBG#reIUfG90ky9GhU7=>TQq*0Gk z3NbI6cd32SpHfN=isvH!0A%xvoCU_=Gp|q3 zcr}6+LcD=OAOr}3qE-m<2E!19w1PHB!Li+DLuFHOLQjS%ID+F0m*+ekO*BZ05Y37Kf(Esd8c`GGZb@#DP=NT~2AAiXK-rYL1W(lTJs2Jiko{^=cth3>HI$NHJUp z6+*=b2otp6pd{WX2p1x}5mhXS7Bn|Ib>p)8EV>$-z+RWQ7>$IJnEK|(RLC3?Mmq^Q z93!_O2Z|BTBIL<5Yd%c7z#p+lg1lN}3SwL)^9JHz0fJTt_8y*>7DF*5b0HP+rHO*b z7Rr{&j?3PVot4KZViXqTL}i(>LHU6ubeQ(yQtibC?Ij^_*9aJ=5+-!7DJD+F)P=95 znRrP%IbF2XVWS|jnY5C2>KW0<#j|l&j0z(c^;#?}Rtc?huB|Diuu*U$$si}lF|77r zg&ZOZO#2EXf`^Do*s>?!2Q5eNEwN6iiEU$R*a61GI@tl~G=`%fpVc@h z7^MM@acLNr09-i7O#!=}C>P?IIu{agr>w^HHWAb1xNkl_ppeP8y%)b`+k0i(-pkzf z-t=wnRfRS@ zIdNd=Ye9}HvkowhZtz2403+MP*x2nwqa$I8%4>_?3K>#>S3qz8Yo7wl7)8uA<2C*a z9NL`>6II?hn(~l7pA{I)1n2)8i7Ok`b)6P9pfWLP297`vT&0?r&PSpj+49Kn&cNGs z>b}%>fqP7S`ieTPtX%CfKS4~7se7-epMR=O?M!`w2#=}zuBZn)>(p&n)A^YC{AIOJ zm*+CiqFs-vFI-XQY+4*%hEUC8>i*BwH^Vch%q}yR(&e+GYG+H1pj-zLGkMST-PBrt zb0h6`&G$bIDL+DMJ;J+a_uO*--qasqKR)N0Ptb06xqn~kPoPm7XLuT~b$Ha4K0zs{ zy-GnNYK1@{`lJ0B-cgf^#8$J1s#>}@{Cw(@BsrnMf3vxZa@8*MzmWPXeEw2O`OwXo zTUfOwX@%D<%4?s|Dou3*gpVK`lWrodJEN2_3D&8TFjFr4|0>?Sb3gL8zCmT8_}pz!eW6^c7uIhtQtD8}SYP_aW%2MDQDJ z&I@-6YiCa%-=BJo8M5~d=zh>W)ck2s?1?*2&63j9=1;UTB~_JbO9@I$Bd$|socwwcs!K?2B&E79gL=TLJ7J7n z8P^szz9z_gOR{)Ee!4(tTsj4~Fea#iHNIlZskPS!neA+OZDz(;Qc8CX`)Sy7ONwg( z%_3V~GBLwMX2Ba%-c6O$oHMUi**IxaKz0#6NrOcDTwTEFn^QjW)h2N z=QZ>CnGI9F$#gI+Q;)<{n7^Frmyy7q(l&{4!U%tBh&KR2yg?9!j1Tul0U!T@)=@TW zwsQU1I7en2B+)?-=ne8FP)RX?b;^%DA#Np6iAo{)S-5qfPWkm8LsZ_>XCcXNzH-B( zCx*~x0cWLvIdAP;p*-9jH;_CFB+yg zH+H-@#POJ7$i*24Jh}pHtIFIW)!8+1w+xhk@7$4K4Pc{DqWYA_{CQ)z=@&5cH2AB{ z9X^7pd<%3{F`&p4aEsOBHts;pgu}(J5zd1bm)>4DpYSNZ zeqjm!Uig+HW=>KTI6c!ubdeDyp_CoPI5Z!+hCRbq{W7L39p3FZFwJtr0!Uncf;(F zRD@)jn4%E+oxJv+TxEGT%~jZVVTOreP3uq%FhO*G5F!Cd|J+pMo59U6i;|$4A(?3De__6tyfo<{piaKNseSshj3Ih+dsvZby(^O!>nBF*iC;^Hn@Lv*-vISO{w14 z#WBfIY;RgLJHg@0RMFTYHVzzdg!-oJr+bYQZ+qpyj~HXrvsf5*pUa)ZhlH=;SXfK##|C!Bs<{k4#HslTml@+1Ktn zR+<~EC5GY4fkc23*R&`I6M0v-EL{BPsc|!gi0z}65bvb7hR{yk;NoN(ktDhlSUB{t z6+zx@sYj_tjto;uZK#}%M=RKd3fZ`n%~VXa@t%cKmKeQfQ*9*qcUszLWNJ}EK^rn- zKB00}$Dbc{lfE*Qc2rGCzey|Ko01Mg{e=8(530l}9aqct5f5rk=gA2ND0_)wGO9T~ z7X>;-s)%9|&Nv@s_iE z#Tp`pc(welRlo?^^8%|u{Z2&9PN0Alq;2d#(TjbSlcrL79O=`Yujl<6uCoK<7_Wm- z4zNy%5HE{wY7(Cc;AdObc&8<42Z(dZS4peJYU7PFenN}?wY8~EC@qj!E9YZa z;5IE`cJ7!6Z{Ra_-%M5Ign`B}`-x)1foZB;58d+7M0wOR*Y^@teOu!Pj0KGZJJ@uu zu4kOP86yU$eyg&mrDKL!JHS8P0_QKJ+*K61tx~?Br+M2ns=#0G z+1E^09nM~Q-^A}A;oYl=pM02#Quf}!wt zdd7STw$#wofA9^wgk+Z<2z=pR;S}0oEjKgLx`#^$@3uc2|m; zsU}+(jeWA5WFBW4l-ES9QP|>; z0udP}(+1_8?nvkPE;c9{f@TIyA4{Y@3FX&6TnnIgGt5JLJiOjP~*`~%^AJQoOY<8X~~#Qm6Ati4>O;Ne$a(djVwl+F1eZN3K{1^U*W+rx(_89~$0axM zn<%h{Q7+s8YivohM%us3N^|7{CcWiU4C$=YcFpi1S$JyfCam|T6>7I1&)EgG(0WwjK z@_Ha4GSr_)t%#Nt+3j;1F9OT9ssbhy85jW}^#}Zo9%GMj;MK(8;T+I% z26ZILf_`P8O;Cv%y@ob17D?YkN!mn-x=d0tQ8F-5k|{|1MHOiD9IavGGTc2WE2Q43 z;B-o;3xtjbcQCIBrLl{YPNsMWt?D8S^evaBfiO|3Zj#n8WzvBfsUj0B3S3H60(NJe z<)_Jy&=mVqJ=i6)G$eJKT>XgL$%t}tfvyPGm1rROK5M&myS2l*)4I#L$J%Lq#Okr` zvvyg#tp}}7S`S;F&U5BX$}7&BnpcuHBd;`XcAhJ5Zk{`DeqKdhWnNX@k`Z8&nPV5={2uw07RZOB?mC<$3UTHjYsJh&6-imOPn`!NOZG|gCZn@V>kWWl zzRHLbN9zq10?}0z3_)H^y9t-i0d#BO`sx5)8J?pe{N}~zH$$!1MEKwL3lpeNqSr-r z$@D$RJlbB9Q3CV88Za{iv;yCCKt2S5LDSwR!EHgpJ02&vNicj=ox0F*6*!1ZFHxNe#Ay!w5{Cv#Y#Vj%R>hx+ZH4t+&XR@Of{+bbs^%* zaYKek#xG#D-0;Mn3-NBhml@p7d^cqQvsIyQXZ&h}c+e4;aA^j>dgr-L3IEbiVEL!H z>k{3~p}?>v)=7i9Qz8GpMbw0=!9WCOOo>uuk2}M|6A%dr1X|0=N*IZA%5`P{CnNvW zaKBE4cGE8{;>v^mkVu5qvb|0jYcEK;#=~P7!}8&DB3~$)(oDm$pdn8$9!FiX>Gt2NmFPmQ4+&+NCyC9wu<&4Kf6tmlLqiqzYCPU)Y9(~3xVG~L&vJUUkXsta zy#J9iw%ZwsM0-JSmQ)dtKQc*19l2IS9)9J9;>zWsRhuVLm2(4{ca7~Fcs1&#Lm`wa zWgKT8Ebv!-3#0y%3oD9THMHz+bvyJ4hbgW|C_6vom* zB%HjpLq&>%rDeyHU??MOm0qq;Opfm8cv}=f$~sM%&kdefP@{zeNZ8gAaw&HhiG+dQ z8bV|PiRw_6{`ilTFP{R6?AWn;M|@?-*sd1EsYLd4GtWK?5~jEJtv8w~6`wrTqUcLx zGk_uM_HCLTzri}S(t7OM7DY{B`#3TBOU1n6^u_7&s`%P3rF0b#z;?Y)v}VdEjmdRX zJNc{igTJ9H%Z1sQXU4BCkh_vL`?-_8hK$v!pQ{VR591mm%9AT~{Wc&g9Gc`!y`hi% zV#)`rbIyCCVuPwe*kxyiKm;Rhl~*JmRLE1>wB8Dn5`qby2}M(5cv$sHWqYA++CpTq z`9xG@`=qK$zruMX3JI1yALb9K{NPDl#!yJmgiq3hJ8yZyAjl%vuL({+AnV}exci(G zJ=m{W9|a?SmA}S!^PN!}I$>6jSmf)&D1rx(bB?3^@vd15a?X!^t{c;~?|P z+6mt~78E-=%Hav#`ND$vVM2nveZ&VJtaGY*F}(enrv^2F4_>9(!n^HZhOot#G_^9% z%Ln2&Ccl>(E+dyU1h&3K`bjo#vqu)vS2X1@gGl zSk3wP`(Ut$MW{!|Gh(MvL5ZDqnQIyCqm_2#l2DgSQKCUNT&KNN32I|J&81L+^E@lK8L=;%66|uq=^1gKT_tv@+*({D zC~60145x<IZ_$fqHeah6$#Bm!(ol zf{+WW*cZS>HTfx1QU~ohd+EfLE4zCJT%(AGwK%zyy>z%N%gmkqL(Hs1F}6iimGG=N%@m@Or1IHSU_+`w~Oda0Bf{yfdvbtTLBvn5MLKj9a) zeAKXz4~mz|#_Cy1UpytAR#2mM(XJL1 zb+c9tk6f?ddm}PR{4y^@nv4CKt2x7kNFtvEfbWuua&UlXPp)u%{59GuZf z8#47ld{+@Vt)R%u=}Po8Z9}XUkG`x>ugd7Za zSvHGf`QN5<({+lns~tXUsGF@TeUw{hkRaWqZ`++s3aBe~cTDc@7aA6l{u z^5Rx6FCT3S*i<+D*~hC!A*1s(@e*!|)9^#)J^F{Uq^E;)-)7oXO}hMi!aMIqtyDa% zIz36EJD*7haw%%AxJE8ZEN%-wP<3BX?3A=pIi+EC*rRl=Oi6&lbH?G>;ndqQX-Uxz z`554TyNl1%E{h$$BD*BhFfB4V^iWj-3R=FCQ@==!;UZ2@BY&&U?xnVtjH8~a8cqh; z{5DSgH_FOI{Ebq@R*5Ip8_uuFewRwt|2m}oxgUxXneloyzmI45!^6{pR7>vWjPbye z3}8bI_14wfc+O7oev_-b{E^`@-$kctG}JgK)IE{aFnXLSbH(UkSF3K*Uxm}PX!2A*?m^lhji_J$k-GsgE^yX1@Wn;ErhMEruSqp# zWN~iBd4r*T7-uji4(qx%XLyL=kZJ0tH>=W-h=R6*>6xwLe3S3ql-_h*YjoCeQ!%H% z6s|pXh&MdGQr8hgS0G;H5Zk#m=MpW^U$K5TS7#YBfro7zJwT7rWF+>}h6@qm3eNB` z&GzTyG}DIrvL1O}XG$b9QPwL~LxfgxK5+E<5`fzINc=w?BfeEaroIU%HS=e`33oEpuL z1KpKvngw07_eY5+TG<+D0l^z zal0E#nn_SeN6uwEtl>w*!X+bN5CwB(Yn4l7gWF`5rLyq*WY;gwl}*-flT8$5J40_i zy;SDjCR?*q7Wts2KP!3`H~A2^4Voq!(Z1z7SI-!h>#UbO7W48BS;S6R&z6O<;wNIx z-X#lzecBHWVF~|pi|py}1AX`U99XI~=x>(Ur3w3SBDmh%$;`*3NUXk+q05f)s+g&dGK> zSQ$T6f4|JeG!;*oC>mD8JpAm!_^ItNw4|MB>JUqYIpv$;OZZWUMxHU1A+6p1$gy%-tagXRZ>?Mj64>IA|czh36QXnT%wttW?Cw*$w>`kYihI&xO_ zqTSi5p6C)s%5kQAMfSw|8>1e`Rwee6taH@d5+9&OAjKou66IdD(64HDmili|VfKZY zXQ&zP*ZVz?<(GJ#q_nW>qH2Tx4rabWeR(u~R!LR+r!VSrQ%t#gN>xqyQ;T<} z`#&4LKD78?X>D?bZ2eR9hf=0QEZ&^~u^+vLb@|^`jQCBlE~*ad)w|QlX?4nV`9`(v zSlODzyO&bWtJigOUKr6!T6;lWeP9=Tl(q^p23fEUDGnxGxC5L9VIlLFKKp(-B+TCI zy_pdChovp7mQm5a-oE_@b@Xb#zxBY|eZx{FLJ`Id$&=u$Jb5CVOL2m%&nBk@XEBS@ zrUWE3M~Ek&eMo+SNiS|DK$;oDrRlc}R?ldEees5es_B8>^rnDkWPNUyZyoTX8TbR| zmH5P6>2zQB@|z0mKL8_-(TcGNhoB@xXe;K2uTQbidhR~FUm_<$-&wQ5R_dwX?iXme znz9CYaf+aiEi9iz+K5sg>_qos3;G}1Da(U_jWgL`@ZH(bM>Phz*?@t$ct&fdEYr}P z6u`Np8bKj;CE61U6e18lp5NF58D{Vk_pDeAORVBdw?b$`W#oXj*FJSRa7(>Z^GOCQ zgjm|bZS28W+gXjE-~7mmS{1S(ld?MR)9iz*a0>d7xu;+dT{H#YoT-_q+4A)H{2=tC zLg=w*Fj&2_^eiM98I~7~iB}T`Rvv1QxidDwhBl2`%eM>0pcD4s!;{|l{@z-CMh6|D22&@tT0Si+`+{1mbQ0r8%Vl9p!I7D`Z zWajZ^$=FcAA*_=e7*;1)#Tyg%OQwGwJXN#hMyZ17MWtLN_|0{))4g!p-2t*AC(pl|Ms1 zUdNK7GsX8kglrVWq%_ix5x0k0Ls6~x{s;=k?Up;ffWNo+cRl|I{U@6K@djdzTMFY{ zZp#^rn=p?3D=v0_8u^B6?POG}2%s$d4}|gX|AY76KcG2`_-{p=OXni~^Zum4M@PWB zwsj}Z1uI|}dL|h%lsrvQ;dq-tiGO}1hVmybya_~0evBYLej<={W^!6d-LQ}t@@4_K zsUiar%bkeMN94akSRlLK7RYP4B#pd!4;k5m*79hAf0B+=qNh*DLJlH1xg@6< zs4S90vYG+>k}xBkq_fXYL~7DnwpczUm-Z7fBzlN#V84ar)=NudsVXRynpGhECT1@= zR3^FCK->V~%qc0lKHmZ|l6>JH=YZ-B%U|CokY7Fl_HxC{U+0puK#(J<52<6S56+Qb$Z^ z8hV0?Yhiw!&5mG4oK?nGB&T*zeqO4tf<1=a$9%7S@*QE^DLVzKm-~Ty_L0C0Bgm&u zWz=`earB=FGVE<2pHzU~TWbk<2N@MWy;M#v1M|bf>}8-AG#EvgmN&u zNhziBUDg>+-XH&p9vwJ+{{s_a>3^OThs&hUQtq};f6d!XnD#}FUo@qm_(?!E@9jC( zbKusi-u|0&K7ae!jb~ZB0N6azj;fzmgt$&sw$~XnJ^s=2%o--zWmJq5&wfSSvwo{7 zXmNPgPlU5bI~44uz2_UDja%C*i7Ds{A1zF{{@oQyd^ByS5O=Ho1`#G6nZNaLXoI-d z_*mh99Aw#E_hRzsP1YF%Lc+w~Xn#}KkTG4i$@&h}CPOihyj}^(j|V~2SO#ewo)0*0 zkZq$x6US&8pwswas(oWUt)ZQ?kq)IhDGq7z5}-n97cHYZZ0H}LJE)WU($+p{vk`g=f&!dkU_>1%x+riZLP&F@P6kFcpu#abg^B;xHI4 zCSZvUV$t8oU31A?vQD|zf%gjsWSz26GCGvPlMkwc?xJfbb{}-ZE@;^=CgTSkDPpoX z3Y@`^G91;m7;z++#7z84!5DLuQ@LcgA1Swt~0 z54zy#a%$7VbCbl_iZBI-jO3A}!;rzlaSA2jXtBi$C_IYgWi6A4yi+cYbH%;cjj0U+`Oc&FOml4Po6RJr}mnamoE`=!P}sAL_S8TZJALKK7VD`qe|knL1a2Xxa3P0wc9f7d zlqKgh%1F0ShsVxrqwV=bQ!;}=$%^(slMupmA!!~sm^a%4_t{9t98UGF0!r&8D&&hC z)B_HxqLh~1C8I*AS%acbT1qiSN}%c~mpGfFeo)-&IRp}vg={2a6iNv9hA88dh`gdx z!7=YD_>N*`AByZCT($o~^`m%R8QXCRdMc(W*BwB)9EOs}qjbhsOr<$724aDeOi_;M zC_Qpi8q|aW(S*|dl0EFfqp1G1n3D>t{l#x>QQ1i0P0i*6&x1MtM%hx*14EI%E$9 z^$#hYR?L+~0i7le!A5$_P8LJ(OB9?wVvIY2N`Vx}TpXL37)!FmX%GUYs)a`bLL8dX zp|loaS~!)iGmw#qYEIoL)44P%^#D#0D~A)I0;d5^Z)9O8rFS|RpX|Q`J9V7KIk~i( zehF#rfH_a@gL`*8My=VCgLL;*;J$_N4i6BHFcwZdnRs$E?$WD<>VChNabQkk#j){=Q|Cm} zxPR$}R1gRppntmde8AD?3vEoVQfM1S36-#DjhO^( zNgLHt&p;IQ2$*KH9QZNs`@2;`9*JMw_S?hf{HRqyBML7q+sj1<-FjpGjf4x@H7}#B zO)-+1MWB(B#c+U(fNsx@uW&E+T{3BdUebbT-#yXuq5aq&d0>Xsu%1*FHdG2@;`wf4w{eV4u?+7F;~gRlfh-rx z6>#&pecWr@l^8ZAHfBsrH-9B&qi&2YKIV*Zqu}b+Z&c_ib-t6Qjr+kf11 z`{CO+Z;v!J5A;(puTSk<`(WWy_w{!6pa1B^o6p=japOu<>hzTlyl|!Y)Wv&0Jht|$ z7eZ=B6mH-9<|C)}diVeQ!ob?jPcKF^+Vs|hSuC2el>2KE#4IX=K-}?T6=dbVo{?&J$7_ykEkA3*Ur583_ek@{E zR=vtONwqtmTG1$dsAp)~%_l!!^Xj@&k9$vk))dKQ?76b()TY4?TYujd+?cW`VRg3ua2vhuS^<~(~!74enE+| zxLn~fv_;472;1yEzV(YUEvJrXjnym1B|Bz39K9zfZ`+v?>rSa!Ed>$ zI(b*1CC-{$XiQfK{@dU3{MPmQ$-yUk54F6x^;GDpag#FKWR*T^`2=X3ooZe*$Gqd) z&f`CCIK5xJYDC`lkH388)=Po&6O;X&@wS=6+BG}AfBMn~FI{@!*K^Oky8qIXJ>iR& z=F8^n{`q)BY@Sgv*_apO&bCr^-I8&`YZ6oi{+kDW?LT+v#aADF>-muStb$pl&4X($ zoV<2?@bPEVE||kV@cpK9-`rUL<|j{md%gAY?Te4B{q)Bp-+BVd3K%QDAk(^CW2YTv zS>dEy_nc{2jnih$Pq8VMrxuX>;WnwIWADw47mt2_;%5JgpRT)f@u?vlq4OH;xyxc! z3z7o*o}8sq<~0flBmFFy9alD-TGxB;>&;j8zjWo}6U`UiIs4K3U0-{ix$*4pPyc#u z%PTKN%v%*xn_D&=COmLs>)=oNMh)kiD*wJP{6F@UESP6GA^|vuv(>j!WjByiDSk3} z2Y}kin60iygPXz6w0#r0N_IrTMe3m?d`YkfjUlr_Rjpwv%RJlMQ3VUD%5CK}j)luR zl$`8ugs~|~gY9G?c*LaHp+LVFp`4HIo~j7OQd3j3WPWuE!>Pi6E>^mT&rdSZ$!$q< zF)v0a>ZOeljB{D}iiPDXeU(C?B(*enn|hK+m8O^Gl#*E>Z zTjH)N=QP75C84u+zuy`95~$tOrhZ&d`9S5>xrITw6_%zAf2+gL}~u2Ca?V>|R! z5v--I-dv9wGy8`tW*v(KLy0f4{Q2H(%_>LqYpd80aJIv-?965{=YB( E4>Q?kEdT%j literal 0 HcmV?d00001 diff --git a/data/geruest/821004200.dwg b/data/geruest/821004200.dwg index 148b0a35f00d08ff1b5bae8330bd28bbd046faf2..d82726c3ae28b855efe48e4687c59085dd22b5aa 100644 GIT binary patch delta 7230 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 Dj3In+ delta 3191 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 diff --git a/data/json/geruest.json b/data/json/geruest.json index cbbb946..d19f920 100644 --- a/data/json/geruest.json +++ b/data/json/geruest.json @@ -6,13 +6,10 @@ "dummy": "Rohrabhaenger_dummy.dwg", "attribute": { "LAENGE": { - "typ": "number", "min": 0, - "max": 6000, - "einheit": "mm" + "max": 6000 }, "TEILEFARBE": { - "typ": "auswahl", "optionen": [ "RAL7035", "RAL7030" @@ -225,13 +222,10 @@ "dummy": "821006051.dwg", "attribute": { "LAENGE": { - "typ": "number", "min": 0, - "max": 6000, - "einheit": "mm" + "max": 6000 }, "TEILEFARBE": { - "typ": "auswahl", "optionen": [ "RAL7035", "RAL7030" @@ -240,18 +234,6 @@ "frei_option": "Individuell" } }, - "einbauwinkel": { - "Platte": [ - 0, - 90 - ], - "Arm": [ - 0, - 90, - 180, - 270 - ] - }, "geometrie_varianten": [ { "name": "R 2\"", diff --git a/data/json/geruest_traeger.json b/data/json/geruest_traeger.json index 42c5385..19f0da3 100644 --- a/data/json/geruest_traeger.json +++ b/data/json/geruest_traeger.json @@ -5,7 +5,7 @@ "teileart": "Geruest Traeger", "querstrebe": { "dummy": "dummy_Strebe_30x4.dwg", - "abstand_max": 500, + "abstand": 500, "rand_min": 100, "rand_max": 250 }, diff --git a/lang/de_DE.json b/lang/de_DE.json index 055680d..721c443 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -1040,6 +1040,7 @@ "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." + "gr-tr-beschr-keine": "\nKeine Rohr-Geruest-Traeger in der Zeichnung.", + "gr-tr-strebe-rand": "Traeger %1 mm: Der Randabstand der Querstreben betraegt %2 mm und liegt ausserhalb des erlaubten Bereichs %3 bis %4 mm (Strebenabstand %5 mm, %6 Streben). Der Traeger wird trotzdem eingefuegt." } } diff --git a/lang/en_GB.json b/lang/en_GB.json index 2714170..7a94884 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -1040,6 +1040,7 @@ "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." + "gr-tr-beschr-keine": "\nNo pipe scaffold beams in the drawing.", + "gr-tr-strebe-rand": "Beam %1 mm: the edge distance of the cross braces is %2 mm, outside the allowed range %3 to %4 mm (brace spacing %5 mm, %6 braces). The beam is inserted anyway." } } diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 7c8c66e..cbabbae 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -1048,36 +1048,57 @@ (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)) + ;; Querstreben: Abstand IMMER genau 500, beide Raender gleich; gewaehlt + ;; wird die Strebenzahl mit dem Rand am naechsten an 100..250 + ;; (Gleichstand -> weniger Streben). Plan = (rand positionen). + ;; Im Bereich: + (tu-eqf "geruest-traeger-plan/1000" '(250.0 (250.0 750.0)) + (geruest-traeger-streben-plan 1000.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/1200" '(100.0 (100.0 600.0 1100.0)) + (geruest-traeger-streben-plan 1200.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/1300" '(150.0 (150.0 650.0 1150.0)) + (geruest-traeger-streben-plan 1300.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/1500" '(250.0 (250.0 750.0 1250.0)) + (geruest-traeger-streben-plan 1500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/400" '(200.0 (200.0)) + (geruest-traeger-streben-plan 400.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/500" '(250.0 (250.0)) + (geruest-traeger-streben-plan 500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/200" '(100.0 (100.0)) + (geruest-traeger-streben-plan 200.0 500.0 100.0 250.0)) + ;; Ausserhalb (netto mod 500 in (0, 200) oder netto < 200) - der Plan + ;; kommt trotzdem, der Abstand bleibt 500: + ;; 1100: Rand 300 (k=1) oder 50 (k=2), beide 50 daneben -> weniger Streben + (tu-eqf "geruest-traeger-plan/1100" '(300.0 (300.0 800.0)) + (geruest-traeger-streben-plan 1100.0 500.0 100.0 250.0)) + ;; 1050: Rand 275 (25 daneben) schlaegt 25 (75 daneben) + (tu-eqf "geruest-traeger-plan/1050" '(275.0 (275.0 775.0)) + (geruest-traeger-streben-plan 1050.0 500.0 100.0 250.0)) + ;; 1150: Rand 75 (25 daneben) schlaegt 325 (75 daneben) + (tu-eqf "geruest-traeger-plan/1150" '(75.0 (75.0 575.0 1075.0)) + (geruest-traeger-streben-plan 1150.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/501" '(250.5 (250.5)) + (geruest-traeger-streben-plan 501.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-plan/kurz-150" '(75.0 (75.0)) + (geruest-traeger-streben-plan 150.0 500.0 100.0 250.0)) + (tu-nil "geruest-traeger-plan/null" + (geruest-traeger-streben-plan 0.0 500.0 100.0 250.0)) + ;; Abstand zwischen zwei Streben ist in jedem Fall exakt 500 + (foreach _l '(700.0 1100.0 1150.0 2345.0 5999.0) + (setq _rec (geruest-traeger-streben _l 500.0 100.0 250.0)) + (tu-true (strcat "geruest-traeger-streben/abstand-" (rtos _l 2 0)) + (vl-every '(lambda (a b) (equal (- b a) 500.0 1e-6)) + _rec (cdr _rec)))) + ;; Rand-Abweichung + (tu-eqf "geruest-rand-abweichung/innen" 0.0 (geruest-rand-abweichung 150.0 100.0 250.0)) + (tu-eqf "geruest-rand-abweichung/unten" 25.0 (geruest-rand-abweichung 75.0 100.0 250.0)) + (tu-eqf "geruest-rand-abweichung/oben" 50.0 (geruest-rand-abweichung 300.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) + ;; Ergebnis (dummy laenge querlinie abstand 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}}"))) @@ -1086,7 +1107,7 @@ (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}}"))) + (ssg-json-parse "{\"querstrebe\": {\"abstand\": 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))