From 6b8f0f9ec397b0d3103cefe7fc6fe98e36c4302c Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Fri, 16 Oct 2026 11:41:51 +0200 Subject: [PATCH] =?UTF-8?q?[ADD]=20Kreiseldrehrichtungsanzeige=20hinzugef?= =?UTF-8?q?=C3=BCgt;=20[CHANGE]=20Insert=20des=20Kreisel=20mit=20Ausrichtu?= =?UTF-8?q?ng=20wird=20jetzt=20=C3=BCber=20die=20vom=20Benutzer=20gezeichn?= =?UTF-8?q?ete=20Eingabelinie=20erreichnet;=20[FIX]=20Fehlende=20config=20?= =?UTF-8?q?Datei=20f=C3=BCr=20die=20Positionierung=20der=20St=C3=BCckliste?= =?UTF-8?q?=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 2 +- Lisp/KreiselInsert.lsp | 219 ++++++++++++++++++++++---------- Lisp/README.md | 2 +- cfg/stueckliste.json | 29 +++++ data/ils/Drehrichtungspfeil.dwg | Bin 0 -> 16134 bytes lang/de_DE.json | 3 +- lang/en_GB.json | 3 +- tests/test_unit.lsp | 21 +++ 8 files changed, 206 insertions(+), 73 deletions(-) create mode 100644 cfg/stueckliste.json create mode 100644 data/ils/Drehrichtungspfeil.dwg diff --git a/CLAUDE.md b/CLAUDE.md index 0624f58..64a1533 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -105,7 +105,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `ssg_blocktools.lsp` | SSG_BLOCK_COUNT, SSG_BLOCK_DELETE, SSG_ATTR_ERSETZEN | Block-Werkzeuge fuer das aktuelle Layout (Ursprung ACADSST `TOOLS.LSP`/`ssg.lsp`): Einfuegungen zaehlen (Pick, wcmatch-Muster oder Statistik aller Bloecke), alle Einfuegungen loeschen (Rueckfrage, gesperrte Layer bleiben), Attributwerte zeichnungsweit/in Auswahl suchen+ersetzen (Vorschau mit Anzahl, Teilstring oder Gesamtwert). Wirksamer Blockname (dynamische Bloecke). Reine Kerne `ssg-bt-name-passt-p`, `ssg-bt-zaehle-namen`, `ssg-bt-wert-neu` in `tests/test_unit.lsp` getestet | | `ssg_texttools.lsp` | SSG_TEXT_ERSETZEN, SSG_TEXT_PARALLEL, SSG_TEXT_HOEHE, SSG_TEXT_STIL | Text-Werkzeuge (Ursprung ACADSST `CHGTEXT`/`PARTEXT`/`ALTHOEHE`/`ALTSTIL`): Suchen/Ersetzen in TEXT, MTEXT und Block-Attributen (nutzt `ssg-bt-*`, daher NACH `ssg_blocktools` laden), Text parallel zu Linie/Polylinie/Bogen (lesbar ausgerichtet, Abstand), Hoehe/Stil aendern. Arbeitet in der XY-Ebene des Welt-KS. Reine Kerne `ssg-tt-lesbarer-winkel`, `ssg-tt-versetzt` getestet | | `ssg_stueckliste.lsp` | SSG_FAEHNCHEN_STUECKLISTE | Positions-Faehnchen + Stuecklistentabelle, komplett AUS DER ZEICHNUNG - es wird keine Datei eingelesen. Der Benutzer waehlt die Elemente aus (`ssg-sl-erkannte-elemente`: `ssget`-Auswahl, Enter ohne Auswahl = alle erkannten im aktuellen Layout; der reine Filter `ssg-sl-elemente-aus-ss` laesst nicht erkannte Objekte still wegfallen, man darf also grosszuegig ein Fenster aufziehen). `ssg-sl-klasse` klassifiziert Blocknamen (`KREISEL_*`/`ECKRAD_*` -> "ILS Kreisel/Eckrad", `VF_*`/`GF_*` -> "ILS Streckengruppe", `GR_*` -> "Rohrgeruest", `AP60*`/`AP110*`/`AP_110*`/`APG110*` -> je eigene Omniflo-Geraden-Gruppe; wirksamer Blockname ueber `ssg-bt-effname`, also auch dynamische Bloecke). Jedes einzelne Element bekommt ein Faehnchen (`ssg-sl-faehnchen`, Rechteck/Kreis/Automation-Rahmen + Text) an der unteren linken Ecke seiner Bounding-Box (aus `csv:get-bbox` - liefert Zentrum+Abmessungen, NICHT Eckpunkte, die Ecke wird im Code daraus berechnet); die Tabelle (`ssg-sl-tabelle-zeichnen`) gruppiert GROB je Elementklasse (keine Sivas-Artikelaufloesung wie `export_sivas.py`) zu einer Zeile mit Anzahl + Laengensumme aus dem `LAENGE`-Attribut (`ssg-sl-gruppieren`/reiner Kern `ssg-sl-gruppieren-werte`), mehrere gleichartige Elemente tragen daher dieselbe Positionsnummer. **Bewusst KEIN Datei-Import wie bei Walz/Bihler**: dort vergab ein externes System die Positionsnummern und lieferte sie als Datei zurueck, die das CAD nur abzeichnete - bei Walz (`MENU/POSITION.LSP`) eine `.pos`/`.zei` aus einem Clipper-Warenwirtschaftssystem mit Koordinaten-Zuordnung, bei Bihler ITB (`Walz_und_Bihler/SBCADBib/ITB-Module_ori.lsp`, `c:bea_pos`) eine `.pos` mit Zuordnung ueber den Entity-Handle. Diese Umwege sind hier nicht nachgebaut (Entscheidung 2026-10): Nummern und Mengen entstehen in dieser Funktion, die Stuecklisten-AUSGABE liefern EXPORTSIVAS/EXPORTCSV aus denselben Block-Attributen. Der alte `c:stueckl` (Laengen-Summierung ueber hartcodierte Walz-Profillayer wie `L-2`=ST, `A-7`=AP110) hat ebenfalls kein Aequivalent im dxfmakros-Datenmodell (Block-Attribute statt Linien-Layer). Braucht `csv:get-bbox`/`csv:get-attrib` aus `export.lsp` und `ssg-bt-effname` aus `ssg_blocktools.lsp` - muss daher NACH beiden geladen werden (siehe `ssg-ensure`-Reihenfolge in `SSG_LIB.mnl`/`ssg_load.lsp`). Reine Kerne `ssg-sl-klasse`, `ssg-sl-gruppieren-werte` in `tests/test_unit.lsp` getestet | -| `KreiselInsert.lsp` | KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad, EckradEdit, KREISEL_WEICHEN | ILS Kreisel und Eckrad komplett in AutoLISP. **Beruehrpunkte/Weichen** (`kreisel-bp-*`, Befehl KREISEL_WEICHEN, Menue Kreisel-Module -> Weichenpunkte ermitteln): ermittelt aus den Blockparametern (Einfuegepunkt, Drehung, ABSTAND - nicht aus der Geometrie, Modell wie `draw-module`) die Beruehrpunkte aller `KREISEL_*`/`ECKRAD_*` GLEICHER Hoehe (`toleranz_hoehe`): Rad/Rad (`Kreis/Kreis`) und Rad/Laufprofil (`Kreis/Tangente`), nur auf der freien Radseite (aeusserer Halbkreis inkl. Flanken; Eckrad ganz frei), Toleranz `toleranz_beruehrung` (component_defaults.json, Sektion kreisel). Kapsel-Mindestabstand < 2r - tol = KOLLISION (Ueberschneidung), Kreisel Seite an Seite (Beruehrung nur an Flanke/Profil) = KOLLISION `Tangente/Tangente` - beide werden gemeldet, aber nie zur Weiche. Ausgabe: Konsolenliste + Kreis-Marker (Layer `kreisel_weiche`/`kreisel_kollision` aus layer.cfg, XDATA `SSG_KR_WEICHE`, jeder Lauf ersetzt die alten). Der Export schreibt dieselben Punkte als `kreisel_weichen.json` neben `export_raw.json` (`csv:kreisel-weichen-schreiben`, export.lsp); `export_csv.py` uebernimmt sie (`export_neighbors.load_lisp_kreisel_weichen`/`lisp_touch_switches`, Rueckfall auf `compute_kreisel_touch_switches` nur ohne Datei), `export_sivas.py` setzt daraus Merkmal 4403 "Anzahl Weichen ILS". Reine Kerne `kreisel-bp-element`/`-paar`/`-berechnen` in `tests/test_unit.lsp` (`tu-tests-kreisel-bp`, u.a. mit den 11 Einfuegungen aus `tests/testdata/schnittspunkte_kreisel.dxf` -> 10 Beruehrungen) getestet. Eckrad: eigener Dialog `eckrad_edit.dcl` (`eckrad-dialog`, reiner Kern `eckrad-dialog-werte` getestet) beim Einfuegen (ILS_Eckrad, ohne GUI Konsolen-Rueckfall) und per Doppelklick (EckradEdit); kein Abstand/Typ, Drehung als freier Winkel statt fester Kreisel-Ausrichtung. `ils-eckrad-insert` behaelt eine vorhandene NUMMER (Edit -> gleicher Blockname `ECKRAD_n`). Kreiselname: `draw-module` setzt `Bezeichnung` nur noch bei leerem Wert auf die Vorgabe `KreiselN` (eigener Name aus KreiselEdit bleibt erhalten); Beschriftung `kreisel-label-text` = `KREISEL n: _` (ohne Namen bzw. bei Vorgabename wie bisher `KREISEL n: _`); da die Beschriftung als TEXT in der wiederverwendeten Blockdefinition `KREISEL_n_TYP_ABSTAND` steckt, setzt `kreisel-label-aktualisieren` sie dort per `entmod` neu (wirkt auf alle Referenzen der Definition) | +| `KreiselInsert.lsp` | KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad, EckradEdit, KREISEL_WEICHEN | ILS Kreisel und Eckrad komplett in AutoLISP. **Drehrichtungspfeil**: `draw-module` fuegt `data/ils/Drehrichtungspfeil.dwg` auf AN8-Mitte (Block-KS 90 Grad, da die DWG fuer Kreisel Nord->Sued gezeichnet ist; cfg `kreisel.pfeil_winkel`) und SP8-Mitte (+180) mit in den KREISEL_n-Block ein, GUZ = X-Skalierung -1 (reiner Kern `kreisel-pfeil-params`, getestet); Eckrad: ein Pfeil auf der Radmitte mit dem AN8-Winkel (`eckrad-pfeil-params`); bei wiederverwendeter Blockdefinition (KREISEL_n bzw. ECKRAD_n) zieht `kreisel-pfeil-aktualisieren` die Spiegelung per `entmod` nach. **Beruehrpunkte/Weichen** (`kreisel-bp-*`, Befehl KREISEL_WEICHEN, Menue Kreisel-Module -> Weichenpunkte ermitteln): ermittelt aus den Blockparametern (Einfuegepunkt, Drehung, ABSTAND - nicht aus der Geometrie, Modell wie `draw-module`) die Beruehrpunkte aller `KREISEL_*`/`ECKRAD_*` GLEICHER Hoehe (`toleranz_hoehe`): Rad/Rad (`Kreis/Kreis`) und Rad/Laufprofil (`Kreis/Tangente`), nur auf der freien Radseite (aeusserer Halbkreis inkl. Flanken; Eckrad ganz frei), Toleranz `toleranz_beruehrung` (component_defaults.json, Sektion kreisel). Kapsel-Mindestabstand < 2r - tol = KOLLISION (Ueberschneidung), Kreisel Seite an Seite (Beruehrung nur an Flanke/Profil) = KOLLISION `Tangente/Tangente` - beide werden gemeldet, aber nie zur Weiche. Ausgabe: Konsolenliste + Kreis-Marker (Layer `kreisel_weiche`/`kreisel_kollision` aus layer.cfg, XDATA `SSG_KR_WEICHE`, jeder Lauf ersetzt die alten). Der Export schreibt dieselben Punkte als `kreisel_weichen.json` neben `export_raw.json` (`csv:kreisel-weichen-schreiben`, export.lsp); `export_csv.py` uebernimmt sie (`export_neighbors.load_lisp_kreisel_weichen`/`lisp_touch_switches`, Rueckfall auf `compute_kreisel_touch_switches` nur ohne Datei), `export_sivas.py` setzt daraus Merkmal 4403 "Anzahl Weichen ILS". Reine Kerne `kreisel-bp-element`/`-paar`/`-berechnen` in `tests/test_unit.lsp` (`tu-tests-kreisel-bp`, u.a. mit den 11 Einfuegungen aus `tests/testdata/schnittspunkte_kreisel.dxf` -> 10 Beruehrungen) getestet. Eckrad: eigener Dialog `eckrad_edit.dcl` (`eckrad-dialog`, reiner Kern `eckrad-dialog-werte` getestet) beim Einfuegen (ILS_Eckrad, ohne GUI Konsolen-Rueckfall) und per Doppelklick (EckradEdit); kein Abstand/Typ, Drehung als freier Winkel statt fester Kreisel-Ausrichtung. `ils-eckrad-insert` behaelt eine vorhandene NUMMER (Edit -> gleicher Blockname `ECKRAD_n`). Kreiselname: `draw-module` setzt `Bezeichnung` nur noch bei leerem Wert auf die Vorgabe `KreiselN` (eigener Name aus KreiselEdit bleibt erhalten); Beschriftung `kreisel-label-text` = `KREISEL n: _` (ohne Namen bzw. bei Vorgabename wie bisher `KREISEL n: _`); da die Beschriftung als TEXT in der wiederverwendeten Blockdefinition `KREISEL_n_TYP_ABSTAND` steckt, setzt `kreisel-label-aktualisieren` sie dort per `entmod` neu (wirkt auf alle Referenzen der Definition) | | `VarioFoerderer.lsp` (Wrapper fuer `vf_core.lsp`/`vf_standard.lsp`/`vf_etage.lsp`/`vf_linienzug.lsp`) | FOERDERANLAGE, VARIOFOERDERER_EDIT, VARIOFOERDERER_NACHRUESTEN, Vario_Kette_Merge, VF_SEKTION_RESTORE, VF_WIZARD_AN, VF_WIZARD_AUS | Vario-Foerderanlagen Generator (Typen Standard/Etage/Linienzug); Standard- UND Etage-Typ: Werteingabe per DCL-Dialog, inkl. der vorgelagerten Eingabemodus-Wahl 3D-Linie/Werte manuell (`vfc_eingabemodus`, `vfc-dialog-eingabemodus-waehlen`) - beide Modi laufen seitdem in denselben Basis-/Winkel-Dialog, der Konsolen-Pfad ist nur noch Rueckfall bei abgeschalteter GUI/anderer Sprache. Der Dialog-Ablauf ist zentral in vf_core (`vf-dialog-ablauf`/`vf-dialog-berechnen-einfuegen`, parametrisiert ueber Typ-Registry `*vf-typ-registry*` + Flag `horizontal-p`); `vfs-standard-dialog-ablauf`/`vfe-etage-dialog-ablauf` sind nur noch duenne Wrapper darauf. Beide nutzen dieselben Dialoge `vfs-dialog-eingabe-basis`/`vfs-dialog-winkel-verteilung` und den gemeinsamen L_GF/L_VF-Solve `vf-winkel-solve` (vf_core; berechne-alle-winkel/berechne-winkel-etage teilen ihn); Doppelklick auf VF_n-Block ruft VARIOFOERDERER_EDIT, das per SSG_VF_EDIT-XDATA-Marker ("standard"/"etage"/"linienzug") in den passenden Neuaufbau-Zweig dispatcht (`vfe-edit-ent` fuer Etage, `vfl-edit-ent` fuer Linienzug); Altbestand-Bloecke ohne Marker werden abgewiesen. Linienzug (Modus 1, `vf-linienzug-modus`) zeichnet jede interaktive Eingabe in einem Journal auf (`vfl-in-point`/`-string`/`-real`/`-int`) und schreibt es als XDATA auf den fertigen `VF_n`-Block; ein Abbruch (ESC) loescht dadurch nichts mehr, sondern wickelt die bis dahin gebaute Teil-Geometrie ebenso zu einem Block (`vfl-modus-abbruch-sichern`) - sie bleibt stehen und ist sofort per Doppelklick weiter editierbar/fortsetzbar. Der komplette Linienzug-Hauptstrang (Menue-Fragen von AS-Element bis Kettenende, inkl. GF-Bogen/Vario-Kurve/VF-Einheit-Fortsetzung) laeuft per Default ueber DCL-Dialoge statt Konsole (`*vfl-wizard-mode*`, Befehle VF_WIZARD_AN/VF_WIZARD_AUS): `vfl-in-string`/`-int`/`-real` zeigen bei aktivem Wizard-Modus generische Dialoge aus `dcl/vf_linienzug_wizard.dcl` (Zahl-Eingabe bzw. Mehrfachauswahl aus `vfl-menu`/`vfl-menu-int`) statt `getstring`/`getint`/`getreal` aufzurufen; die ORIGINALEN Konsolen-Fragen (princ + get*, de/en ueber ssg-text) bleiben unveraendert im Code (Fallback bei Fehlern via `vl-catch-all-apply`, siehe `doc/variofoerderer.md`). `vfl-edit-ent` liest das Journal, zeigt die Sektionen ueber einen DCL-Dialog (`vfl-dlg-position`, `dcl/vfl_edit.dcl`) an, kuerzt bei Bedarf auf eine gewaehlte Sektion (stummer Replay der behaltenen Eingaben, umgeht den Wizard-Zweig komplett) und baut danach interaktiv weiter. Linienzug Modus 2 (Pfad+Zielhoehe-Solver, `vf-linienzug-modus2`) nutzt dieselben Wrapper PLUS die neue Objektauswahl-Wrapper-Funktion `vfl-in-selection` (Pfad-Objekte als stabile Entity-Handles journalisiert, neue Journal-Art `"OBJS"`) und schreibt sein Journal unter dem eigenen XDATA-Marker `"linienzug2"`; Doppelklick baut hier NICHT abschnittsweise, sondern per vollem Reset 1:1 aus dem Journal neu auf (`vfl-edit-ent2`, dispatcht aus `vfl-edit-ent` per Marker) - Modus 3 (Vorwaerts-Nachbau) bleibt bewusst reine Konsole (im Code selbst als Legacy/"wird umgebaut" markiert). `Vario_Kette_Merge` (in vf_linienzug.lsp) fuehrt mehrere einzeln gebaute VF_n-Bloecke ab einem gewaehlten Start-Baustein ueber die reale KS_AUS->KS_EIN-Nachbarschaft zu einem Gesamt-Block zusammen (aggregierte Attribute, Luecken-Erkennung). Die interaktiven Editier-Pfade (`vfl-edit-ent` Sektions-Zweig, `vfl-edit-ent2`) rufen den Neuaufbau BEWUSST direkt auf, NICHT ueber `vl-catch-all-apply`: der alte Block ist beim Neuaufbau schon per `entdel` weg, und ein Catch faengt den Abbruch VOR `*error*` ab - der Abbruch-Wickler `vfl-modus-abbruch-sichern` kaeme nie zum Zug und die ganze Kette bliebe als lose Einzelteile ohne Block liegen (Fehlerbild "VF-Block aufgebrochen"). Nur der nicht-interaktive Batch-Konverter `vfl-konvertiere-ent` behaelt den Catch (die Batch-Schleife muss weiterlaufen) und meldet den betroffenen Block. Die Abbruch-Handler setzen zusaetzlich `*ssg-ils-dim*` zurueck, weil der Aufrufer, der das sonst tut, nach einem Abbruch nicht mehr erreicht wird. Recovery-Pfad fuer bereits aufgebrochene Ketten: `VF_SEKTION_RESTORE` (vf_linienzug.lsp) setzt das Ketten-Journal aus der XDATA der losen Objekte zusammen - `SSG_VF_EDIT_SEG` traegt pro Entity den Journal-Abschnitt DER ITERATION, in der es entstand (indiziert mit dem ersten Glied-Index darin; eine VF-Einheit mit eingebetteten Vario-Kurven erzeugt mehrere Glieder in einer Iteration), `SSG_VF_EDIT_PRE` auf den Entities des ersten Gliedes die Praeambel (Startpunkt/Starthoehe/AS). Die `.dbg`-Dateien sind reine AUSGABE und werden von keinem Befehl gelesen (dbgopen legt sie bei jedem Lauf neu an) - Wiederherstellungs-Daten gehoeren in die XDATA. `vf_konstanten.lsp` (von vf_core.lsp vor den drei Submodulen geladen) sammelt alle Laengen-/Winkel-/Toleranz-Magic-Numbers aus dem VarioFoerderer-Cluster als dokumentierte `*vfk-*`-Konstanten. Die Migration ist abgeschlossen: vf_core/vf_standard/vf_etage/vf_linienzug referenzieren durchgaengig die zentralen Konstanten statt roher Literale (Winkel, Stations-/Separator-Laengen, Mindestlaengen, AS/ES-Fallback-Versaetze, Text-/Label-Masse) - Ausnahmen sind in `vf_konstanten.lsp` selbst dokumentiert (Werte mit fuehrender Config-Anbindung via `ssg-cfg-or`, itoa-/Integer-Kontexte, Blocknamen-Strings, die 2000.0-mm/m-Umrechnung). `*vfk-gf-bogen-winkel*` (30/60/90, GF-Bogen + Vario-Kurve) und `*vfk-as-es-winkel*` ("30"/"90", AS/ES; bewusst STRING, da 2-wertig und im Journal als STR gefuehrt) sind Beispiele; vf_linienzug hat fuer beide einen Fallback, falls vf_konstanten nicht geladen wurde. **Nicht-interaktiver Betrieb**: `*vfl-headless*` (+ `vfl-headless-p`/`-abbruch`/`-notausgang`, Diagnose `*vfl-headless-fehler*`, optionaler Antwort-Hook `*vfl-headless-antwort-fn*`) verbietet jede Live-Eingabe - eine erschoepfte Replay-Queue ist damit ein harter Abbruch MIT Fundstelle (Art der Eingabe, Glied- und Eingabe-Nummer) statt eines stillen Rueckfalls auf `getpoint`/`getreal`; eingebaut in `vfl-in-value`/`-value-p`/`-selection`/`-abstand`. Die Bau-Pfade melden abgewiesene Sektionen ueber `vfl-meldung` (sammelt nach `*vfl-meldungen*` + `dbgmsg`, `alert` nur bei erlaubter GUI, sonst `princ`) statt per blockierendem `alert`; reine Interaktiv-Alerts (fehlendes DCL, "nicht editierbar", "kein Journal") bleiben `alert`. Genutzt von `tests/test_hm_recformat.lsp` (Replay der echten Aufzeichnungen) und von `vf_spec.lsp`; Fahrplan in `doc/todo/plan-vf-interactive.md`. Details/Entscheidungsbaum: `doc/variofoerderer.md` | | `vf_spec.lsp` (von vf_core.lsp NACH vf_linienzug.lsp geladen) | VF_SPEC_BAU | Linienzug-Ketten aus **Eingabedaten** bauen, ohne Dialog und ohne Konsolenfrage (Stufe 1 aus `doc/todo/plan-vf-interactive.md`). Eine **Spec** beschreibt eine Kette in Domaenenwerten (`"links"`, `"winkel"`, `"aussen"`, ja/nein) statt in Menue-Codes; `vsp-journal` uebersetzt sie in ein Eingabe-Journal, `vsp-bau-aus-spec` spielt es per `vfl-journal-replay-start` durch den UNVERAENDERTEN `vf-linienzug-modus` (Replay-Kern `vsp-bau-journal` - nimmt auch ein unveraendert uebernommenes, aufgezeichnetes Journal, z.B. aus `dxf_anlage_export.py`). Damit traegt der fertige Block dasselbe `SSG_VF_EDIT`-Journal wie eine handgebaute Kette und bleibt per Doppelklick editier- und 2D/3D-konvertierbar - kein Journal-Konsument muss etwas von Specs wissen. Wichtige Regeln, die der Uebersetzer kennt: der Menue-Code haengt am Frame (erste Sektion 4 Optionen ohne GF-Bogen, danach 5), `hz` steht nur beim ERSTEN Segment im Journal, und `erst-hor` unterscheidet den horizontalen Erstkoerper einer VF-Einheit (Separator-/Endpunktfragen vorab) vom gewinkelten (keine Frage). Fehlerverhalten: fail fast - `vsp-pruefen` sammelt ALLE Spec-Fehler, gebaut wird dann nichts (nichts wird geraten). Ergebnis-Record je Kette mit `status` (`executed`/`warnung`/`desync`/`abbruch`/`spec-fehler`), Block/Handle/Einfuegepunkt/Attributen, `prompts` (muss 0 sein), `glieder_soll` vs. `glieder_ist` (Desync-Detektor, der auch bei aufgehender Queue greift) und den gesammelten `meldungen`. Headless-Rahmen `vsp-headless-an`/`-aus` (Save/Restore von `*vfl-headless*`, `*vfl-wizard-mode*`, `*ssg-gui-aus*` + zaehlende Eingabe-Stubs). Spec-Datei flach als JSON (`ssg-load-json` liest zeilenweise), erzeugt von `lib/vf_spec_export.py`. Gegenstueck/Referenz in Python: derselbe Emitter, gegen die 5 echten HundM-Ketten rundlaufgeprueft | | `Gefaellestrecke.lsp` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN); Modus 1 per DCL-Dialog (Einfuegehoehe, Element-Winkel 30/90); Doppelklick auf GF_n-Block ruft GEFAELLESTRECKE_EDIT (Dialog vorbelegt, Neuaufbau; die Einfuegehoehe kommt aus der aktuellen Z des Einfuegepunkts statt aus XDATA/HOEHE_VON_mm, damit ein in Z verschobener Block per OK direkt auf der neuen Hoehe neu entsteht - gleiches gilt fuer die 2D/3D-Umschaltung `gf-konvertiere-ent`; der GF-Linienzug-Rebuild nutzt weiter die absoluten Segmentpunkte aus XDATA) | diff --git a/Lisp/KreiselInsert.lsp b/Lisp/KreiselInsert.lsp index f2b369f..94780ab 100644 --- a/Lisp/KreiselInsert.lsp +++ b/Lisp/KreiselInsert.lsp @@ -314,6 +314,77 @@ ) +;; --- Drehrichtungspfeil (data/ils/Drehrichtungspfeil.dwg) --- +;; Die DWG ist fuer einen Kreisel Nord -> Sued (AN oben, SP unten, also +;; Einfuegewinkel 270) und Drehrichtung im Uhrzeigersinn gezeichnet. +;; Im Block liegt der Kreisel entlang +X (Winkel 0); die Drehung des +;; Kreisels kommt ueber den Einfuegewinkel des KREISEL_n-Blocks automatisch +;; dazu. Der Pfeil wird daher im Block um 0 - 270 = 90 Grad gedreht. +(setq *kreisel-pfeil-block* "Drehrichtungspfeil") +(setq *kreisel-pfeil-winkel* (ssg-cfg-or "kreisel" "pfeil_winkel" 90.0)) + +;; X-Skalierung des Pfeils: UZS -> 1, GUZ -> -1 (Spiegelung an der eigenen +;; Y-Achse, die Pfeilspitze zeigt dann in die Gegenrichtung). +(defun kreisel-pfeil-xskal (drehrichtung) + (if (equal drehrichtung "GUZ") -1.0 1.0) +) + +;; Reiner Kern: Einfuegeparameter beider Pfeile im Block-KS des Kreisels. +;; Rueckgabe: ((punkt winkel xskal) (punkt winkel xskal)) - AN8, dann SP8 +;; (SP8 um 180 Grad gedreht). +(defun kreisel-pfeil-params (radius abstand drehrichtung / xs) + (setq xs (kreisel-pfeil-xskal drehrichtung)) + (list (list (list radius 0.0 0.0) *kreisel-pfeil-winkel* xs) + (list (list (+ radius abstand) 0.0 0.0) + (rem (+ *kreisel-pfeil-winkel* 180.0) 360.0) xs)) +) + +;; Reiner Kern fuers Eckrad: ein Pfeil auf der Radmitte (Block-Ursprung), +;; ausgerichtet wie der AN8-Pfeil eines Kreisels gleicher Drehung. +(defun eckrad-pfeil-params (drehrichtung) + (list (list (list 0.0 0.0 0.0) *kreisel-pfeil-winkel* + (kreisel-pfeil-xskal drehrichtung))) +) + +;; Einen Pfeil einfuegen. Ab dem zweiten Mal per Blockname, damit INSERT +;; nicht nach dem Neudefinieren der bestehenden Definition fragt. +(defun kreisel-pfeil-einfuegen (pt winkel xs) + (command "_.INSERT" + (if (tblsearch "BLOCK" *kreisel-pfeil-block*) + *kreisel-pfeil-block* + (strcat *block-path* *kreisel-pfeil-block* ".dwg")) + pt xs 1.0 winkel) +) + +;; Alle Pfeile einer Parameterliste ((punkt winkel xskal) ...) einfuegen +;; (beim Aufbau der Blockdefinition). Fehlt die DWG, wird ohne Pfeil gebaut. +(defun kreisel-pfeile-einfuegen (params) + (if (findfile (strcat *block-path* *kreisel-pfeil-block* ".dwg")) + (foreach p params + (kreisel-pfeil-einfuegen (car p) (cadr p) (caddr p)) + ) + (princ (ssg-textf "kreisel-warn-pfeil-fehlt" (list *kreisel-pfeil-block*))) + ) +) + +;; Spiegelung der Pfeile in einer BESTEHENDEN Blockdefinition KREISEL_n_... +;; nachziehen (Edit/Redraw mit geaenderter DREHRICHTUNG - der Blockname +;; enthaelt die Drehrichtung nicht, die Definition wird wiederverwendet). +;; Wirkt wie kreisel-label-aktualisieren auf alle Referenzen der Definition. +(defun kreisel-pfeil-aktualisieren (bname drehrichtung / e ed xs) + (setq xs (kreisel-pfeil-xskal drehrichtung)) + (setq e (tblobjname "BLOCK" bname)) + (while (setq e (entnext e)) + (setq ed (entget e)) + (if (and (= (cdr (assoc 0 ed)) "INSERT") + (= (strcase (cdr (assoc 2 ed))) (strcase *kreisel-pfeil-block*)) + (not (equal (cdr (assoc 41 ed)) xs 1e-9))) + (entmod (subst (cons 41 xs) (assoc 41 ed) ed)) + ) + ) +) + + ;; Attribut-Funktionen: ssg-attrib-read, ssg-attrib-merge, ;; ssg-attrib-defaults, ssg-attrib-make-defs aus ssg_core.lsp @@ -439,6 +510,11 @@ ;; SP8 Kreis (Spannstation) bei (radius+abstand, 0) - Dim-Suffix im Dateinamen (command "_.INSERT" (strcat *block-path* "SP8_" *ki-dxfm-dim* ".dwg") (list (+ radius abstand) 0.0 0.0) 1 1 0) + ;; Drehrichtungspfeile auf AN8- und SP8-Mitte (SP8 um 180 Grad gedreht, + ;; GUZ gespiegelt). Fehlt die DWG, wird der Kreisel ohne Pfeil gebaut. + (kreisel-pfeile-einfuegen + (kreisel-pfeil-params radius abstand (cdr (assoc "DREHRICHTUNG" attribs)))) + ;; Tangenten-Layer: bevorzugt aus cfg/layer.cfg (Baugruppe ;; "kreisel_tangente"), sonst der bisherige Hartwert S_LP. (if (null (ssg-layer-anlegen "kreisel_tangente" T)) @@ -506,6 +582,8 @@ (kreisel-label-aktualisieren bname (kreisel-label-text kreisel-nummer (cdr (assoc "Bezeichnung" attribs)) (if is-pin "PIN" "STANDARD") abstand)) + ;; ebenso die Spiegelung der Drehrichtungspfeile + (kreisel-pfeil-aktualisieren bname (cdr (assoc "DREHRICHTUNG" attribs))) ) ) @@ -561,87 +639,89 @@ ;; KreiselInsert: Manuell Position + Parameter ;; -------------------------------------------- +;; Reiner Kern: Abstand und Rotation aus Basispunkt und gezeigtem Punkt der +;; Abstands-Abfrage (Gummiband-Linie, 2D in der XY-Ebene). +;; Ohne Punkt (Enter) -> Vorgabe-Abstand, Rotation 0. +;; Rueckgabe: (abstand rotation-in-grad), Rotation in [0, 360) +(defun kreisel-insert-richtung (pt p2 vorgabe / a) + (if (and p2 (> (distance (list (car pt) (cadr pt)) (list (car p2) (cadr p2))) 1e-6)) + (progn + (setq a (* (/ 180.0 pi) (angle (list (car pt) (cadr pt)) (list (car p2) (cadr p2))))) + (list (distance (list (car pt) (cadr pt)) (list (car p2) (cadr p2))) + (if (>= a 359.9999999) 0.0 a)) + ) + (list vorgabe 0.0) + ) +) + ;; -------------------------------------------- ;; KreiselInsert: Manuell Position + Parameter +;; Abfrage: 1. Basispunkt (AN-Seite), 2. Abstand als Gummiband-Linie - die +;; Richtung der Linie ist zugleich die Rotation (Wert tippen = direkte +;; Distanzeingabe in Zeigerichtung), 3. Hoehe (Vorgabe aus angeklicktem +;; Block bzw. Z des Basispunkts), 4. Kreiselart. ;; -------------------------------------------- -(defun c:KreiselInsert ( / pt abstand typ rotation rotStr hoehe +(defun c:KreiselInsert ( / pt p2 erg abstand typ rotation hoehe hoehe-vorgabe selectedEnt blockAttribs oldOsmode) (ssg-start (ssg-text "kreisel-start-insert") '(("OSMODE") ("CECOLOR"))) - + ;; Temporär Osnap für Blockerkennung setzen (setq oldOsmode (getvar "OSMODE")) (setvar "OSMODE" (logior oldOsmode 512)) ;; 512 = INS (Block-Einfügepunkt) - - ;; 1. Einfuegepunkt + + ;; 1. Basispunkt (AN-Seite) (setq pt (getpoint (ssg-text "kreisel-prompt-basispunkt"))) - (if (null pt) - (progn - (setvar "OSMODE" oldOsmode) - (ssg-end) - (princ (ssg-text "kreisel-abbruch-befehl")) - (princ) - (return) - ) - ) - + ;; Osnap zurücksetzen auf den ursprünglichen Wert (setvar "OSMODE" oldOsmode) - - ;; 2. Prüfen ob ein Block getroffen wurde - (setq selectedEnt (nentselp pt)) - - ;; --- HÖHE BESTIMMEN --- - (cond - ;; Fall 1: Auf einem Kreisel-Block geklickt - ((and selectedEnt - (setq selectedEnt (car selectedEnt)) - (setq blockAttribs (ssg-attrib-read selectedEnt)) - (assoc "HOEHE" blockAttribs)) - (setq hoehe (atof (cdr (assoc "HOEHE" blockAttribs)))) - (princ (ssg-textf "kreisel-hoehe-von-bestehendem" (list (rtos hoehe 2 0)))) - ) - ;; Fall 2: Punkt hat Z-Koordinate > 0 - ((and (> (length pt) 2) (> (caddr pt) 0)) - (setq hoehe (caddr pt)) - (princ (ssg-textf "kreisel-hoehe-aus-3dpunkt" (list (rtos hoehe 2 0)))) - ) - ;; Fall 3: Sonst separat abfragen - (t - (setq hoehe (getreal (ssg-textf "kreisel-prompt-hoehe-z" (list (rtos *kreisel-default-hoehe* 2 0))))) - (if (null hoehe) (setq hoehe *kreisel-default-hoehe*)) + + (if (null pt) + (princ (ssg-text "kreisel-abbruch-befehl")) + (progn + ;; Hoehen-Vorgabe: angeklickter Block mit HOEHE, sonst Z des Punkts, + ;; sonst Default (abgefragt wird erst nach dem Abstand) + (setq selectedEnt (nentselp pt)) + (setq hoehe-vorgabe + (cond + ((and selectedEnt + (setq selectedEnt (car selectedEnt)) + (setq blockAttribs (ssg-attrib-read selectedEnt)) + (assoc "HOEHE" blockAttribs)) + (atof (cdr (assoc "HOEHE" blockAttribs)))) + ((and (> (length pt) 2) (> (caddr pt) 0)) + (caddr pt)) + (t *kreisel-default-hoehe*) + ) + ) + + ;; 2. Abstand (Tangentenlaenge) - Linie gibt auch den Winkel vor + (setq p2 (getpoint pt (ssg-textf "kreisel-prompt-abstand" + (list (rtos *kreisel-default-laenge* 2 0))))) + (setq erg (kreisel-insert-richtung pt p2 *kreisel-default-laenge*) + abstand (car erg) + rotation (cadr erg)) + (princ (ssg-textf "kreisel-abstand-ausgabe" (list (rtos abstand 2 0)))) + + ;; 3. Hoehe (Z-Koordinate) + (setq hoehe (getreal (ssg-textf "kreisel-prompt-hoehe-z" (list (rtos hoehe-vorgabe 2 0))))) + (if (null hoehe) (setq hoehe hoehe-vorgabe)) + + ;; 4. Kreiselart + (setq typ (kreisel-ask-typ)) + + (princ (ssg-textf "kreisel-insert-zusammenfassung" + (list (rtos rotation 2 1) (rtos hoehe 2 0) typ))) + + ;; Modul einfuegen + (draw-module (list (car pt) (cadr pt) hoehe) abstand rotation + (list (cons "KREISELART" typ) + (cons "HOEHE" (rtos hoehe 2 0)))) + + (princ (ssg-text "kreisel-insert-fertig")) ) ) - - ;; 3. Abstand - (initget 6) - (setq abstand (getdist (list (car pt) (cadr pt)) - (ssg-textf "kreisel-prompt-abstand" (list (rtos *kreisel-default-laenge* 2 0))))) - (if (null abstand) (setq abstand *kreisel-default-laenge*)) - (princ (ssg-textf "kreisel-abstand-ausgabe" (list (rtos abstand 2 0)))) - - ;; 4. Rotation - (setq rotStr (getstring (ssg-text "kreisel-prompt-rotation"))) - (if (= rotStr "") - (setq rotation 0.0) - (setq rotation (atof rotStr)) - ) - - ;; 5. Kreiselart - (setq typ (kreisel-ask-typ)) - - (princ (ssg-textf "kreisel-insert-zusammenfassung" - (list (rtos rotation 2 1) (rtos hoehe 2 0) typ))) - - ;; 6. Modul einfuegen - (draw-module (list (car pt) (cadr pt) hoehe) abstand rotation - (list (cons "KREISELART" typ) - (cons "HOEHE" (rtos hoehe 2 0)))) - - ;; KEIN weiteres (setvar "OSMODE" ...) mehr nötig! - ;; ssg-end stellt nur CLAYER und CMDECHO wieder her, nicht OSMODE - - (princ (ssg-text "kreisel-insert-fertig")) (ssg-end) + (princ) ) @@ -1429,6 +1509,9 @@ (dbgp (strcat "INSERT Block: " *block-path* "AN8_" *ki-dxfm-dim* ".dwg")) (command "_.INSERT" (strcat *block-path* "AN8_" *ki-dxfm-dim* ".dwg") (list 0.0 0.0 0.0) 1 1 0) + ;; Drehrichtungspfeil auf die Radmitte (GUZ gespiegelt) + (kreisel-pfeile-einfuegen (eckrad-pfeil-params (cdr (assoc "DREHRICHTUNG" attribs)))) + ;; Unsichtbare Attribut-Definitionen erzeugen (dbgp (strcat "Erzeuge " (itoa (length *eckrad-attrib-defs*)) " Attribut-Definitionen")) (ssg-attrib-make-defs *eckrad-attrib-defs* 50.0) @@ -1461,6 +1544,8 @@ (progn (princ (ssg-textf "kreisel-bestehende-blockdef" (list bname))) (dbgp (strcat "Verwende bestehende Blockdefinition: " bname)) + ;; EckradEdit behaelt ECKRAD_n - Spiegelung des Pfeils nachziehen + (kreisel-pfeil-aktualisieren bname (cdr (assoc "DREHRICHTUNG" attribs))) ) ) diff --git a/Lisp/README.md b/Lisp/README.md index 14846ce..68091b0 100644 --- a/Lisp/README.md +++ b/Lisp/README.md @@ -675,7 +675,7 @@ AutoLISP-Implementierung fuer ILS Kreisel und Eckrad. Enthaelt alle Befehle fuer | Befehl | Beschreibung | | --- | --- | -| `c:KreiselInsert` | Kreisel-Block an Punkt einfuegen (mit Dialog fuer Parameter) | +| `c:KreiselInsert` | Kreisel-Block einfuegen: Basispunkt (AN-Seite), Abstand als Gummiband-Linie (deren Richtung = Rotation), Hoehe, Kreiselart | | `c:KreiselConnect` | Zwei Punkte waehlen, Kreisel dazwischen einfuegen | | `c:KreiselRedraw` | Bestehenden Kreisel mit neuen Parametern neu zeichnen | | `c:KreiselQuick` | Schnelleinfuegung mit Standardwerten | diff --git a/cfg/stueckliste.json b/cfg/stueckliste.json new file mode 100644 index 0000000..3c0cba8 --- /dev/null +++ b/cfg/stueckliste.json @@ -0,0 +1,29 @@ +{ + "_kommentar": "Konfiguration fuer SSG_FAEHNCHEN_STUECKLISTE (Lisp/ssg_stueckliste.lsp). Vorgaben fuer den Startdialog, Darstellung von Fuehrung und Tabelle (Walz-Layout, siehe tests/testdata/positionierung_nach_walz.dxf) und Regeln je Bauteiltyp.", + "startnummer": 100, + "alle_positionen_in_fahne": false, + "pos_layer": { + "modus": "suffix", + "suffix": "-POS", + "fest": "POS" + }, + "tabellen_layer": "D-1-POS", + "fuehrung": { + "textstil": "Pos", + "font": "arial.ttf", + "texthoehe": 75, + "landung": [105, 159], + "landing_gap": 15, + "rahmen": true + }, + "tabelle": { + "textstil": "Standard", + "texthoehe": 100, + "spalten": [300, 500, 500, 900, 4866], + "zeilenhoehe": 136 + }, + "regeln": [ + { "block": "GR_Traeger_*", "marker": "mitte" }, + { "block": "GR_Fahrstrecke_*", "marker": "mitte" } + ] +} diff --git a/data/ils/Drehrichtungspfeil.dwg b/data/ils/Drehrichtungspfeil.dwg new file mode 100644 index 0000000000000000000000000000000000000000..8d8c7f866159f604059132e03a1a3cbe78f2a1cd GIT binary patch literal 16134 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 literal 0 HcmV?d00001 diff --git a/lang/de_DE.json b/lang/de_DE.json index 91e78a9..2ed8a48 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -284,7 +284,6 @@ "kreisel-hoehe-aus-3dpunkt": "\n-> Hoehe aus 3D-Punkt uebernommen: %1", "kreisel-hoehe-aus-basispunkt": "\n-> Höhe aus Basispunkt übernommen: %1", "kreisel-hoehe-aus-startpunkt": "\n-> Hoehe aus Startpunkt uebernommen: %1", - "kreisel-hoehe-von-bestehendem": "\n-> Höhe von bestehendem Kreisel übernommen: %1", "kreisel-insert-zusammenfassung": "\n-> Rotation: %1 Höhe: %2 Kreiselart: %3", "kreisel-labelsetup-fertig": "\nNeue Einstellungen: Höhe=%1, Farbe=%2, Y-Versatz=%3, Schriftart=Arial", "kreisel-neue-beschriftungsposition": "\nNeue Beschriftungsposition: X=%1 Y=%2", @@ -471,7 +470,6 @@ "kreisel-prompt-basispunkt": "\nBasispunkt (AN-Seite): ", "kreisel-prompt-block-auswaehlen": "\nKreisel-Block auswaehlen: ", "kreisel-prompt-endpunkt": "\nEndpunkt (SP-Seite aussen): ", - "kreisel-prompt-rotation": "\nRotation in Grad <0>: ", "kreisel-prompt-startpunkt": "\nStartpunkt (AN-Seite aussen): ", "kreisel-quick-fertig": "\nSchnell Einfuegen fertig.", "kreisel-start-connect": "Kreisel Smart Connect", @@ -490,6 +488,7 @@ "kreisel-typ-opt-pin": "\n[2] PIN", "kreisel-typ-opt-standard": "\n[1] STANDARD (ohne PIN)", "kreisel-warn-blockpfad": "\n[KreiselInsert] WARNUNG: Block-Pfad nicht ermittelbar!", + "kreisel-warn-pfeil-fehlt": "\n[KreiselInsert] WARNUNG: %1.dwg nicht gefunden - Kreisel ohne Drehrichtungspfeil.", "kreisel-warn-core-fehlt": "\n[KreiselInsert] WARNUNG: ssg_core.lsp nicht gefunden - Standardwerte.", "omni-abbruch": "\nAbgebrochen.", "omni-abbruch-bogen": "\n[BOGEN] Abgebrochen.", diff --git a/lang/en_GB.json b/lang/en_GB.json index 8520834..238f66b 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -284,7 +284,6 @@ "kreisel-hoehe-aus-3dpunkt": "\n-> Height taken from 3D point: %1", "kreisel-hoehe-aus-basispunkt": "\n-> Height taken from base point: %1", "kreisel-hoehe-aus-startpunkt": "\n-> Height taken from start point: %1", - "kreisel-hoehe-von-bestehendem": "\n-> Height taken from existing Kreisel: %1", "kreisel-insert-zusammenfassung": "\n-> Rotation: %1 Height: %2 Kreisel type: %3", "kreisel-labelsetup-fertig": "\nNew settings: Height=%1, Colour=%2, Y offset=%3, Font=Arial", "kreisel-neue-beschriftungsposition": "\nNew label position: X=%1 Y=%2", @@ -471,7 +470,6 @@ "kreisel-prompt-basispunkt": "\nBase point (AN side): ", "kreisel-prompt-block-auswaehlen": "\nSelect Kreisel block: ", "kreisel-prompt-endpunkt": "\nEnd point (SP side, outer): ", - "kreisel-prompt-rotation": "\nRotation in degrees <0>: ", "kreisel-prompt-startpunkt": "\nStart point (AN side, outer): ", "kreisel-quick-fertig": "\nQuick insert done.", "kreisel-start-connect": "Kreisel Smart Connect", @@ -490,6 +488,7 @@ "kreisel-typ-opt-pin": "\n[2] PIN", "kreisel-typ-opt-standard": "\n[1] STANDARD (without PIN)", "kreisel-warn-blockpfad": "\n[KreiselInsert] WARNING: block path not determinable!", + "kreisel-warn-pfeil-fehlt": "\n[KreiselInsert] WARNING: %1.dwg not found - carousel without direction arrow.", "kreisel-warn-core-fehlt": "\n[KreiselInsert] WARNING: ssg_core.lsp not found - using defaults.", "omni-abbruch": "\nCancelled.", "omni-abbruch-bogen": "\n[ARC] Cancelled.", diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 5ed48a4..78a235e 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -803,6 +803,27 @@ (defun tu-tests-kreisel ( / _e) (princ "\n[KreiselInsert]") + ;; Drehrichtungspfeil: AN8-Mitte 90 Grad, SP8-Mitte 270 Grad, GUZ gespiegelt + (tu-eqf "kreisel-pfeil-params/uzs" + '(((400.0 0.0 0.0) 90.0 1.0) ((2700.0 0.0 0.0) 270.0 1.0)) + (kreisel-pfeil-params 400.0 2300.0 "UZS")) + (tu-eqf "kreisel-pfeil-params/guz" + '(((400.0 0.0 0.0) 90.0 -1.0) ((2700.0 0.0 0.0) 270.0 -1.0)) + (kreisel-pfeil-params 400.0 2300.0 "GUZ")) + (tu-eqf "kreisel-pfeil-xskal/nil-ist-uzs" 1.0 (kreisel-pfeil-xskal nil)) + (tu-eqf "eckrad-pfeil-params/guz" + '(((0.0 0.0 0.0) 90.0 -1.0)) (eckrad-pfeil-params "GUZ")) + + ;; KreiselInsert: Abstand + Rotation aus der Gummiband-Linie + (tu-eqf "kreisel-insert-richtung/ost" + '(2300.0 0.0) (kreisel-insert-richtung '(100.0 100.0 0.0) '(2400.0 100.0 0.0) 2300.0)) + (tu-eqf "kreisel-insert-richtung/sued" + '(1500.0 270.0) (kreisel-insert-richtung '(0.0 0.0 0.0) '(0.0 -1500.0 0.0) 2300.0)) + (tu-eqf "kreisel-insert-richtung/z-ignoriert" + '(1000.0 90.0) (kreisel-insert-richtung '(0.0 0.0 2000.0) '(0.0 1000.0 0.0) 2300.0)) + (tu-eqf "kreisel-insert-richtung/enter-vorgabe" + '(2300.0 0.0) (kreisel-insert-richtung '(0.0 0.0 0.0) nil 2300.0)) + ;; Projektion eines Punktes auf eine Gerade (tu-eqf "kreisel-project-point/lot" '(0.0 0.0) (kreisel-project-point '(0.0 5.0) '(0.0 0.0) '(10.0 0.0)))