Files
dxfmakros/doc/lisp_methods.md
T

329 lines
18 KiB
Markdown

# LISP-Routinen zum Bau einzelner Einheiten
Uebersicht ueber die LISP-Funktionen, die eine Einheit (Kreisel, Eckrad,
VarioFoerderer, Gefaellestrecke, Omniflo-Element, Geruest, Einzelblock)
erzeugen, mit Signatur, Parametern, Rueckgabe und Voraussetzungen.
Zu jedem Element steht der interaktive **Befehl** (was der Anwender aufruft)
und der **skriptfaehige Kern** (ohne `getpoint`/Dialog, aus Code oder JSON
aufrufbar). Das JSON-Format, mit dem die Testtreiber diese Kerne aufrufen,
steht in [json-input-lisp.md](json-input-lisp.md); alle Befehle in
[kommandos.md](kommandos.md).
Einheiten: mm und Grad. `hz` = Fahrtrichtung in der XY-Ebene, 0 = +X,
90 = +Y. Punkte sind Listen `(x y z)` in Weltkoordinaten.
---
## 0. Uebersicht
| Einheit | Befehl | Skriptfaehiger Kern | Ergebnis-Block | Modul |
|---|---|---|---|---|
| Kreisel | `KreiselInsert`, `KreiselConnect`, `KreiselQuick` | `kreisel-insert-script`, `kreisel-connect-script` | `KREISEL_n` | `KreiselInsert.lsp` |
| Eckrad | `ILS_Eckrad` | `ils-eckrad-insert` | `ECKRAD_n` | `KreiselInsert.lsp` |
| VF Standard | `StandardVarioFoerderer`, `FOERDERANLAGE` | `berechne-standard` + `variofoerderer-einfuegen` + `vf-block-erstellen` | `VF_n` | `vf_standard.lsp`, `vf_core.lsp` |
| VF Etage | `EtageVarioFoerderer` | `berechne-etage` + `etage-foerderanlage-einfuegen` + `vf-block-erstellen` | `VF_n` | `vf_etage.lsp`, `vf_core.lsp` |
| VF Linienzug | `LinienzugVarioFoerderer` | `vsp-bau-aus-spec` (Spec) bzw. `vfl-journal-replay-start` + `vf-linienzug-modus` | `VF_n` | `vf_linienzug.lsp`, `vf_spec.lsp` |
| Gefaellestrecke | `GEFAELLESTRECKE_EINFACH`/`_3DLINIE` | `gf-rebuild` bzw. `gefaellestrecke-einfuegen` | `GF_n` | `Gefaellestrecke.lsp` |
| Gefaellestrecke Linienzug | `GEFAELLESTRECKE_LINIENZUG` | - (nur interaktiv; Kern `gf-linienzug-einfuegen` braucht analysierte Segmente) | `GF_n` | `Gefaellestrecke.lsp` |
| Separator, Scanner | `ILS_SEPARATOR_INSERT`, `ILS_SCANNER_INSERT` | - (Schleife mit `getpoint`); Kern: `ssg-ils-block-laden` + `_.INSERT` | Einzelblock | `SSG_LIB_Commands.lsp`, `ssg_core.lsp` |
| BTMT, Eingang/Ausgang | `ILS_BTMT_*`, `ILS_EINGANG_INSERT`/`ILS_AUSGANG_INSERT` | wie Separator | Einzelblock | `SSG_LIB_Commands.lsp` |
| Omniflo Bogen/Weiche/TEF | `OMNI_APB_*`, `OMNI_W*_*`, `OMNI_TEF_*` ... | `_.INSERT` der `<sivasnr>.dxf` (Kern `omni:insert-dxf` fragt den Punkt) | Einzelblock | `OmniModulInsert.lsp` |
| Omniflo Gerade | `OMNI_AP60`/`AP110`/`APG110` | `omni:make-gerade` | Compound-Block | `OmniModulInsert.lsp` |
| TEF-Foerderer-Linie | `TEF_FOERDERER_LINIE` | `tefl-insert-element` (ein Element) | Einzelbloecke | `TEFInsert.lsp` |
| Geruest (Saeule, Rohrabhaenger, Wandbefestigung) | `GERUEST`, `GERUEST_SAEULEN` ... | `geruest-block-bauen` | `GR_<id>_<ts>` | `GeruestInsert.lsp` |
| Geruest-Traeger | `GERUEST_TRAEGER` | `geruest-traeger-block-bauen` | `GR_<id>_<ts>` | `GeruestInsert.lsp` |
---
## 1. Kreisel und Eckrad (`KreiselInsert.lsp`)
### `kreisel-insert-script (pt abstand rotation typ hoehe)`
Kreisel an einem freien Punkt. Ruft `draw-module` auf.
| Parameter | Bedeutung |
|---|---|
| `pt` | Einfuegepunkt: aeusserster Punkt der AN-Seite auf der Achse (einen Radius = 400 mm vor der Mitte des Antriebsrads) |
| `abstand` | Mittenabstand Antriebsrad - Spannrad |
| `rotation` | Drehung der Achse AN -> SP in Grad |
| `typ` | `"STANDARD"` oder `"PIN"` (nil = `"STANDARD"`) -> Attribut `KREISELART` |
| `hoehe` | Einbauhoehe; nil = Z von `pt`, sonst `*kreisel-default-hoehe*` |
Rueckgabe: Entity des `KREISEL_n`-Blocks oder nil. Setzt `ssg-start`/`ssg-end`
selbst.
### `kreisel-connect-script (ptStart ptEnd typ hoehe)`
Kreisel zwischen zwei aeussersten Punkten (AN-Seite, SP-Seite). Berechnet
`abstand` = Distanz - `*kreisel-durchmesser*` (800) und `rotation` aus den
Punkten. Rueckgabe wie oben.
### `draw-module (basePoint abstand rotation attribs)`
Gemeinsamer Bau-Kern beider Funktionen: setzt Antriebs- und Spannstation,
Tangenten, Beschriftung, fasst alles zu `KREISEL_n` zusammen, schreibt die
Attribute (`attribs` = Alist, wird mit `*kreisel-attrib-defs*` gemischt).
Direkt nur aufrufen, wenn zusaetzliche Attribute gesetzt werden muessen.
### `ils-eckrad-insert (pt rotation attribs)`
Eckrad als Compound-Block `ECKRAD_n`.
| Parameter | Bedeutung |
|---|---|
| `pt` | **Radmitte** `(x y hoehe)` |
| `rotation` | Drehung in Grad |
| `attribs` | Alist mit Ueberschreibungen, z.B. `(("GERUEST_EINZELMODUL" . "1") ("GERUEST_TYP" . "Schoenenberger Geruest"))`; nil = Defaults |
`c:ILS_Eckrad` berechnet die Radmitte aus Beruehrpunkt + Radius in Richtung
des zweiten Klicks. Rueckgabe: Entity des Blocks.
---
## 2. VarioFoerderer Standard und Etage (`vf_core.lsp`, `vf_standard.lsp`, `vf_etage.lsp`)
Ablauf in drei Schritten: **loesen** (Winkel und Teillaengen aus den
Gesamtmassen), **einfuegen** (Bausteine verketten), **Block bilden**
(`VF_n` mit Attributen). Der Dialog-Ablauf `vf-dialog-ablauf` /
`vf-dialog-berechnen-einfuegen` macht dasselbe, oeffnet aber Dialog 2 und ist
darum nicht skriptfaehig.
### Vorbereitung
| Funktion | Zweck |
|---|---|
| `init-bibliothek ()` | Bogen-, AS-/ES- und Stationsmasse aus den Bloecken lesen. Setzt `*lib-initialized*`. Wird von `berechne-standard` aufgerufen |
| `init-bibliothek-etage ()` | dasselbe fuer Etage, aufgerufen von `berechne-etage` |
| `vf-set-as-masse (winkel seite)` / `vf-set-es-masse (winkel seite)` | AS-/ES-Masse (`aus-dx`/`aus-dz`, `ein-dx`/`ein-dz`) fuer die Variante `"90"`/`"30"` + `"links"`/`"rechts"` setzen |
### Loesen
| Funktion | Rueckgabe |
|---|---|
| `berechne-standard (deltaL deltaH richtung seite)` | `(best-winkel L_GF L_VF ergebnis-liste)` |
| `berechne-alle-winkel (deltaL deltaH richtung feste-hz schon-flach)` | wie oben; `feste-hz` nil = 1600 mm feste Horizontale, `schon-flach` nil |
| `berechne-etage (deltaL deltaH richtung seite)` | wie oben, Etage-Geometrie (setzt die Gefaellebogen-Masse je `seite`) |
| `vf-winkel-solve (...)` | gemeinsamer L_GF/L_VF-Solve beider Typen (intern) |
- `richtung` = `"Auf"`/`"Ab"`, `deltaH` als Betrag.
- `ergebnis-liste` = je Kandidatenwinkel `(winkel L_GF L_VF gueltig)`.
Kandidaten aus `vario.bogen_winkel` (3 ... 51). Einen bestimmten Winkel
erzwingen: Eintrag mit diesem `winkel` aus der Liste nehmen und
`gueltig`, `L_GF` > 0, `L_VF` > 0 pruefen (so `mubea:build-vario-one`).
- `L_GF` wird danach auf Vorlauf `L_GF1` und Nachlauf `L_GF2` aufgeteilt
(gleichmaessig / vorne / hinten / manuell, `foerderer:split-gf`).
### Einfuegen
```
(variofoerderer-einfuegen deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz)
(etage-foerderanlage-einfuegen deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz)
```
Identische Signatur (Registry-Vertrag `*vf-typ-registry*`). `startpunkt` =
Lage von **KS_EIN des AS-Elements**, Z = Einbauhoehe am Anfang. `seite` =
`"links"`/`"rechts"`. Rueckgabe: Endpunkt (KS_AUS des ES-Elements).
`variofoerderer-einfuegen` ist aus Teilen zusammengesetzt (`vfs-as-teil`,
VF-Eingang/Koerper, `vfs-es-teil`), die auch der Linienzug nutzt.
### Block bilden
```
(setq lastEnt (ssg-lastent-ohne-attribute)) ; VOR dem Einfuegen merken
...einfuegen...
(vf-block-erstellen anlage-typ seite (vf-next-number) n-scanner
hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2
richtung best-winkel startpunkt lastEnt hz
geruest-einzelmodul geruest-typ)
(vf-edit-xdata-schreiben ent anlage-typ hz) ; editierbar per Doppelklick
```
- `anlage-typ` = `"standard"`/`"etage"`; `hoehe-bis` = +deltaH bei `"Auf"`,
-deltaH bei `"Ab"`; `geruest-einzelmodul` `"0"`/`"1"` (nil = `"1"`),
`geruest-typ` einer aus `*ssg-geruest-optionen*`.
- Sammelt alle Objekte seit `lastEnt` zum Block `VF_n`, setzt Layer, Attribute,
ID (`ssg-id-generate`) und Dimension. Rueckgabe: Entity des Blocks.
- `lastEnt` immer mit `ssg-lastent-ohne-attribute` holen, nicht mit
`(entlast)` (siehe `CLAUDE.md`, ssg_core).
- Blocknummer immer ueber `vf-next-number`: eine feste Nummer fuehrt bei der
zweiten Anlage zu einer Rueckfrage von `_.-BLOCK` und einem haengenden Befehl.
---
## 3. VF-Linienzug (`vf_linienzug.lsp`, `vf_spec.lsp`)
Ketten mit Vario-Kurven, GF-Boegen und mehreren VF-Einheiten. Der Bau-Ablauf
ist ein Frage-Antwort-Assistent; jede Eingabe wird journalisiert und als XDATA
am `VF_n`-Block gespeichert. Nicht-interaktiv baut man ihn, indem man ein
Journal **abspielt**.
| Funktion | Zweck |
|---|---|
| `vf-linienzug-modus ()` | Modus 1, frei zeichnend. Liest alle Eingaben ueber `vfl-in-point`/`-string`/`-real`/`-int` - aus der Replay-Queue, falls eine aktiv ist |
| `vf-linienzug-modus2 ()` | Modus 2, Pfad + Zielhoehe (Objektauswahl, eigenes Journal `"linienzug2"`) |
| `vfl-journal-replay-start (forward-journal)` | Journal als Antwort-Queue setzen; danach `vf-linienzug-modus` aufrufen |
| `vsp-journal (spec)` | Spec -> Journal (nil bei Spec-Fehlern, Fehler in `*vsp-fehler*`) |
| `vsp-pruefen (spec)` | alle Spec-Fehler sammeln |
| `vsp-bau-aus-spec (spec)` | eine Kette aus einer Spec bauen (headless, Replay, Ergebnis-Record mit `status`, Block, `prompts`, `glieder_soll`/`glieder_ist`) |
| `vsp-bau-liste (specs)` | mehrere Specs, je mit `vl-catch-all-apply` |
| `vsp-bau-datei (datei)` | Spec-Datei lesen (`vsp-json-laden` + `vsp-gruppieren`) und alle Ketten bauen; das ist `VF_SPEC_BAU` |
| `vsp-headless-an ()` / `vsp-headless-aus ()` | Headless-Rahmen: `*vfl-headless*`, Wizard aus, `*ssg-gui-aus*`, zaehlende Eingabe-Stubs |
Ein Abbruch im Headless-Modus nennt die Stelle (`*vfl-headless-fehler*`),
statt auf eine Live-Eingabe zu warten. Details zum Ablauf und zu den
Journal-Regeln: [variofoerderer.md](variofoerderer.md), Abschnitte 5-8b.
---
## 4. Gefaellestrecke (`Gefaellestrecke.lsp`)
### `gf-rebuild (deltaL winkel hz-winkel as-seite es-seite startpunkt as-winkel es-winkel geruest-einzelmodul geruest-typ)`
Der bequemste skriptfaehige Einstieg fuer Modus 1/2: setzt die AS-/ES-Masse,
berechnet die Staustrecke
`L_stau = (deltaL - aus-dx - ein-dx - separator_breite * cos(w)) / cos(w)`
und ruft `gefaellestrecke-einfuegen`. Ohne Zusammenfassung und Rueckfrage
(eigentlich fuer `GEFAELLESTRECKE_EDIT` gedacht).
| Parameter | Bedeutung |
|---|---|
| `deltaL` | horizontale Gesamtlaenge AS + Staustrecke + Separator + ES |
| `winkel` | Neigung der Staustrecke in Grad (Default im Dialog 3.0) |
| `hz-winkel` | Fahrtrichtung |
| `as-seite`, `es-seite` | `"links"`/`"rechts"` |
| `startpunkt` | **KS_EIN des AS-Elements**, Z = Einbauhoehe oben |
| `as-winkel`, `es-winkel` | `"90"`/`"30"` als STRING (nil = `"90"`) |
| `geruest-einzelmodul`, `geruest-typ` | wie beim VF |
Rueckgabe: `(ename deltaH deltaL)` oder nil (Staustrecke <= 0.1 mm -> `alert`).
### `gefaellestrecke-einfuegen (L_stau winkel startpunkt as-seite es-seite hz deltaL-total as-winkel es-winkel geruest-einzelmodul geruest-typ)`
Eigentlicher Bau: AS-Element, skalierte Staustrecke, Separator, ES-Element,
Block `GF_n` mit Attributen, XDATA `SSG_GF_EDIT` und ID. Erwartet die
fertige Staustreckenlaenge `L_stau` und passend gesetzte AS-/ES-Masse
(`vf-set-as-masse`/`vf-set-es-masse`). Rueckgabe wie `gf-rebuild`.
### Weitere
| Funktion | Zweck |
|---|---|
| `gf-modus12-abschluss (... dialog-bestaetigt ...)` | Abschluss Modus 1/2 mit Zusammenfassung; fragt ohne `dialog-bestaetigt` = T per Konsole nach |
| `gf-init-bibliothek ()` | AS-/ES-Masse fuer die GF lesen |
| `gf-linienzug-modus ()` | Modus 3, interaktiv (Auswahl vorhandener LINE/ARC) |
| `gf-linienzug-einfuegen (segmente grade startframe)` | Bau-Kern von Modus 3, erwartet die von `gf-analysiere-kette` aufbereiteten Segmente |
**Falle `*lib-initialized*`**: `gefaellestrecke-einfuegen` initialisiert die
GF-Masse nur, wenn `*lib-initialized*` nil ist - dasselbe Flag setzt
`init-bibliothek` des VarioFoerderers. Wer in einem Lauf erst VF und dann GF
baut, muss `gf-init-bibliothek` einmal selbst erzwingen (so
`mubea:ensure-gf-init` in `tests/test_mubea.lsp`), sonst rechnet die GF mit
den Vario-Massen und landet auf falscher Hoehe.
---
## 5. Einzelbloecke: Separator, Scanner, BTMT, Eingang/Ausgang
| Funktion | Zweck |
|---|---|
| `ils-insert-sensor (fname)` | Befehls-Kern Separator/Scanner: fuegt in einer Schleife ein, Punkt per `getpoint`, Drehung per `pause` - **nicht skriptfaehig** |
| `ils-insert-station (rohname)` | dasselbe fuer BTMT-Beladung, SC_Entladung, Eingang, Ausgang; setzt zusaetzlich den Layer aus dem `LAYER`-Attribut |
| `ssg-ils-block-laden (blockname)` | Blockdefinition `data/ils/<blockname>_<DIM>.dwg` einmal je Zeichnung laden; Rueckgabe: effektiver Blockname mit Suffix oder nil |
| `ssg-ils-block-datei (blockname)` | nur den Dateipfad aufloesen (3D-Fallback) |
| `ssg-block-layer-vorgabe` | `LAYER`-Attribut eines eingefuegten Blocks als Ziel-Layer anwenden |
| `ssg-id-generate (ent)` | eindeutige `ID` vergeben (nur wenn der Block ein ID-Attribut hat) |
Skriptfaehig bauen (Muster aus `hundm05:bau-sensor`):
```lisp
(setq eff (ssg-ils-block-laden "Scanner")) ; nicht per (and ...) verketten!
(command "_.INSERT" eff (list x y z) "" "" rotation)
(while (and (> (getvar "CMDACTIVE") 0) (< n 5)) (command "") (setq n (1+ n)))
(ssg-id-generate (entlast))
```
`ATTREQ`/`ATTDIA` vorher auf 0 setzen. Restliche Prompts gezaehlt mit Enter
beantworten, nicht ungebremst: eine Rueckfrage, die Enter ablehnt, haelt
`CMDACTIVE` sonst dauerhaft > 0.
---
## 6. Omniflo (`OmniModulInsert.lsp`)
| Funktion | Zweck |
|---|---|
| `omni:insert-dxf (sivasnr-str hoehe drehung)` | `%DXFM_OMNIFLO%/<sivasnr>.dxf` einfuegen, `HOEHE`/`DREHUNG` setzen, Layer. **Einfuegepunkt per Maus** (`pause`); Rueckgabe: Einfuegepunkt oder nil |
| `omni:insert-from-entry (dlg-result)` | Dialog-Ergebnis von Bogen/Weiche -> `omni:insert-dxf` |
| `omni:make-gerade (blockname pt pt2 srcAttribs textHeight textGap textEnt)` | **skriptfaehig**: Aluprofil-Gerade `"AP60"`/`"AP110"`/`"APG110"` von `pt` nach `pt2` als Compound-Block mit Text. `srcAttribs` per `omni:read-src-attribs`, `textEnt` nil. Kein `ssg-start` - Aufrufer setzt OSMODE/ATTREQ/ATTDIA = 0. Rueckgabe: Entity |
| `omni:insert-block (blockname laengemax)` | Befehls-Kern der Geraden (fragt `pt`/`pt2` per `getpoint`) |
| `omni:ladeschleifen-dialog (profiltyp init-hoehe)` | Dialog Ladeschleife, danach `omni:insert-dxf` |
Skriptfaehig fuer Boegen/Weichen: `_.INSERT` der DXF direkt mit Punkt
`(x y hoehe)` und Drehung, danach `ssg-attrib-set-on` mit `HOEHE`/`DREHUNG`
(`omni:test-insert` in `tests/test_omniflo.lsp`). Die festen TEF-/Zubehoer-
Befehle (`OMNI_TEF_*`, `OMNI_STOPPER` ...) rufen `omni:insert-dxf` mit fester
Sivas-Nummer. `OMNI_TV_*` und `OMNI_APBW_*` sind Platzhalter ohne Bau-Logik.
---
## 7. TEF-Foerderer-Linie (`TEFInsert.lsp`)
| Funktion | Zweck |
|---|---|
| `c:TEF_FOERDERER_LINIE` | Start-/Endpunkt abfragen, Linie zeichnen, 1000-mm-TEF-Geraden (`0_B10030.dxf`) aneinanderreihen; das letzte Stueck wird zurueckversetzt, damit der Foerderer genau am Linienende endet |
| `tefl-insert-element (start richtung-einheit s hz winkel)` | ein Element an Position `s` (mm ab `start`) entlang der Linie, Rz(hz)*Ry(winkel) |
| `tefl-ensure-block-loaded (dxf-pfad)` | Blockdefinition einmal laden |
---
## 8. Geruest (`GeruestInsert.lsp`)
| Funktion | Zweck |
|---|---|
| `geruest-einfuegen (bg wahl)` | Befehls-Kern Saeulen/Rohrabhaenger/Wandbefestigung: baut aus der Dialog-Auswahl `wahl` = `(variante-index oben unten laenge farbe)` die Werte (ARTINR, BEZEICHNUNG, TEILEART, LAENGE, TEILEFARBE), fragt dann den Punkt per `getpoint` |
| `geruest-block-bauen (bg variante werte pt teile-dwgs)` | **skriptfaehiger Kern**: Dummy-DWG + Teile-DWGs bei `pt`, Block `GR_<id>_<ts>`. `bg` = Baugruppe aus `geruest.json`, `werte` = Attribut-Alist, `teile-dwgs` aus `geruest-teile-dwgs` |
| `geruest-traeger-einfuegen (bg wahl)` | Befehls-Kern Traeger: Schleife Startpunkt -> Endpunkt oder Laenge |
| `geruest-traeger-block-bauen (bg layer netto strebe strebe-pfad muffe-pfad werte pt winkel)` | **skriptfaehiger Kern** Traeger: LINE (0,0,0)-(netto,0,0), Streben, Muffe, Block bei `pt` mit `winkel` (Radiant). Rueckgabe: `(blockname . entity)` |
| `geruest-traeger-laenge`, `-start`, `-streben`, `-pruefen` | reine Berechnung (Netto-Laenge nach Abzuegen, Startpunkt, Strebenverteilung, min/max) - in `tests/test_unit.lsp` getestet |
| `geruest-traeger-beschriften (blockEnt)` | Beschriftungstext aus dem fertigen Block |
| `geruest-block-abschliessen (bg attribs werte lastEnt pt rot)` | gemeinsamer Schluss aller Geruest-Bloecke (ATTDEFs, Block bilden, einfuegen, ID) |
Fuer Geruest gibt es noch keinen JSON-Treiber.
---
## 9. Einfuegeprimitiven (`ssg_ks_insert.lsp`, `vf_etage.lsp`)
Grundbausteine, auf denen VF, GF und Linienzug aufsetzen. Alle legen **KS_EIN**
des Blocks (nicht den Block-Ursprung) auf den Startpunkt und geben den
Weltpunkt von **KS_AUS** zurueck - damit lassen sich Bausteine verketten.
| Funktion | Zweck |
|---|---|
| `ensure-block-loaded (name)` | Block einmal ins Dokument laden |
| `extract-ks-from-block (block-obj)` | KS_EIN/KS_AUS eines eingefuegten Blocks (VLA-Objekt) lesen, Cache `*ks-cache*` je Blockname |
| `insert-block-by-ks (blockname einfuegepunkt hz)` | Block nur um Z gedreht (AS/ES-Elemente, Stationen) |
| `insert-inclined-scaled-block (blockname startpunkt laenge winkel hz)` | Block in X auf `laenge` skaliert und um `winkel` geneigt, Rz(hz)*Ry(winkel) (Staustrecken) |
| `insert-rotated-block-with-ks (blockname startpunkt winkel block-dx block-dz hz)` | Block gedreht, `block-dx`/`block-dz` nur als Fallback ohne KS_AUS (Boegen) |
| `insert-hz-scaled-block-by-ks (blockname startpunkt laenge hz-winkel vert-winkel)` | waagrechter, auf `laenge` skalierter Block (`Staustrecke_SP_1000_mm`, Massstab `laenge/1000`); Kandidat fuer die noch fehlende waagrechte Foerderstrecke. Bricht bei fehlender Blockdatei mit `(exit)` ab - vorher mit `ssg-ils-block-laden` pruefen |
| `ssg-rot-matrix-zy (chz shz cv sv)` | 4x4-Rotationsmatrix Rz(hz)*Ry(vert) aus Cosinus/Sinus beider Winkel (`ssg_core.lsp`) |
---
## 10. Gemeinsame Regeln fuer eigene Treiber
- Systemvariablen ueber `ssg-start`/`ssg-end` sichern; `OSMODE` 0,
`ATTREQ` 0, `ATTDIA` 0 setzen, sonst fragt `_.INSERT` nach Attributen.
- Module vorher laden: `(ssg-ensure "KreiselInsert")`, `"VarioFoerderer"`,
`"Gefaellestrecke"`, `"SSG_LIB_Commands"`.
- Die Dimension folgt `ssg-ils-dim-aktuell`; fuer einen festen Lauf
`*ssg-ils-dim*` (bzw. `*vsp-dim-override*` beim Spec-Bau) setzen und danach
zuruecksetzen.
- Rueckgaben nicht mit `and`/`or` verketten - die liefern in AutoLISP nur
T/nil (siehe `CLAUDE.md`, Konventionen).
- Interaktive GUI abschalten: `ssg-gui-aus` bzw. `vsp-headless-an`.
- Vorbilder: `tests/test_mubea.lsp` (Kreisel, VF Standard/Etage, GF,
Einzelbloecke), `tests/test_hundm05.lsp` (Spec-Ketten, Kreisel,
Einzelbloecke), `tests/test_omniflo_strecke.lsp` (Omniflo).