diff --git a/Lisp/vf_konstanten.lsp b/Lisp/vf_konstanten.lsp index c40dcd7..7f4ed78 100644 --- a/Lisp/vf_konstanten.lsp +++ b/Lisp/vf_konstanten.lsp @@ -303,6 +303,13 @@ ;; Ueberschreiten, kein Abbruch. Aktiv referenziert. (if (null *vfk-vf-laenge-max*) (setq *vfk-vf-laenge-max* 25000.0)) +;; Hoechstzahl Vario-Kurven INNERHALB einer VF-Einheit (zwischen Umlenk- und +;; Motorstation) - Nutzer-Regel 2026-10-22. Zwei Kurven bedeuten hoechstens +;; drei VF-Geraden im selben Lauf; danach muss die Einheit mit einer +;; Motorstation schliessen und eine neue mit Umlenkstation beginnen. +;; Geprueft in vfl-m2-lauf-grenzen (vf_linienzug.lsp, Modus 2). +(if (null *vfk-vf-kurven-max*) (setq *vfk-vf-kurven-max* 2)) + (setq *vfk-konstanten-geladen* t) (princ) (princ) diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index 81368eb..f60cdb2 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -425,6 +425,10 @@ ;; Fallback, falls vf_konstanten (aus irgendeinem Grund) nicht geladen wurde. (if (null *vfk-gf-bogen-winkel*) (setq *vfk-gf-bogen-winkel* '(30 60 90))) (if (null *vfk-as-es-winkel*) (setq *vfk-as-es-winkel* '("30" "90"))) +;; Grenzen einer VF-Einheit (Modus 2) - Fallback wie oben, falls +;; vf_konstanten.lsp nicht geladen wurde. +(if (null *vfk-vf-laenge-max*) (setq *vfk-vf-laenge-max* 25000.0)) +(if (null *vfk-vf-kurven-max*) (setq *vfk-vf-kurven-max* 2)) (setq *vfl-glied-schema* (list (cons "AS" @@ -712,13 +716,36 @@ (setq i (1+ i))) ergebnis) +;; Segmentkopf fuer die GENERISCHEN Wizard-Dialoge. +;; +;; Die Segment-Gruppendialoge (vflw_seg_*) tragen "Segment i/n: ..." schon in +;; ihrer Kopfzeile. Sobald eine Frage aber auf einen generischen Dialog +;; zurueckfaellt - weil der Gruppendialog uebersprungen wurde oder weil es +;; eine Anschlussfrage ist (Separator vor/nach, Vario-Variante) -, stand dort +;; nur die Frage ohne Bezug: man sah nicht mehr, zu welchem Segment sie +;; gehoert, waehrend die Konsole es die ganze Zeit mitschreibt (Nutzer-Befund +;; 2026-10-22). *vflw-seg-kopf* wird von der Segment-Schleife gesetzt und +;; hier vorangestellt; ausserhalb der Schleife ist es nil und alles bleibt +;; wie bisher. +;; Trenner bewusst ": " und KEIN Zeilenumbruch: der Radio-Dialog baut seine +;; Kopfzeile als DCL-"label", und ein Umbruch im Label-String macht die +;; erzeugte DCL-Datei ungueltig. Die text-Tiles der anderen Dialoge sind +;; ohnehin einzeilig. *vflw-seg-kopf* traegt darum die KURZE Form +;; ("Segment 2/5"), nicht die volle Segmentbeschreibung - sonst wird die +;; Kopfzeile zu lang und schneidet ab. +(if (null *vflw-seg-kopf*) (setq *vflw-seg-kopf* nil)) +(defun vflw-kopf-text (prompt) + (if (and (boundp '*vflw-seg-kopf*) *vflw-seg-kopf*) + (strcat *vflw-seg-kopf* ": " (vflw-clean prompt)) + (vflw-clean prompt))) + (defun vflw-zahl-impl (prompt / dat dcl-pfad dlg-wert ergebnis vorgabe) (setq dcl-pfad (vflw-dcl-pfad)) (setq dat (load_dialog dcl-pfad)) (if (not (new_dialog "vflw_zahl" dat)) (progn (if (and dat (>= dat 0)) (unload_dialog dat)) nil) (progn - (set_tile "kopf" (vflw-clean prompt)) + (set_tile "kopf" (vflw-kopf-text prompt)) ;; Vorgabe ins Feld schreiben - "Ok Weiter" ohne Aenderung liefert damit ;; denselben Wert wie frueher das leere Feld (dort griff der Default ;; beim Aufrufer). @@ -749,7 +776,7 @@ (if (not (new_dialog "vflw_wahl" dat)) (progn (if (and dat (>= dat 0)) (unload_dialog dat)) "") (progn - (set_tile "kopf" (vflw-clean prompt)) + (set_tile "kopf" (vflw-kopf-text prompt)) (start_list "opts") (foreach opt optionen (add_list (vflw-clean opt))) (end_list) @@ -795,7 +822,7 @@ (setq zeilen (append (list "vflw_radio_dyn : dialog {" - (strcat " label = \"" (ssg-dcl-label-escape (vflw-clean prompt)) "\";")) + (strcat " label = \"" (ssg-dcl-label-escape (vflw-kopf-text prompt)) "\";")) (vfs-dcl-radio-gruppe-zeilen "vflwopt" "" sauber) (list " ok_cancel;" "}"))) (setq dcl-pfad (ssg-dcl-temp-schreiben "vflwradio" zeilen)) @@ -1313,7 +1340,12 @@ (set_tile "kopf" (vflw-clean (ssg-text "vfl-sep-gf-header"))) (set_tile "sepvor" "0") (set_tile "sepnach" "0") - (if es-folgt-sep (mode_tile "sepnach" 1)) + ;; Gesperrte Kachel ohne Begruendung verwirrt - daher die Hinweiszeile + ;; (Nutzer-Wunsch 2026-10-23): sie sagt, dass der Separator zwischen GF + ;; und ES automatisch gesetzt wird. + (if es-folgt-sep + (progn (mode_tile "sepnach" 1) + (set_tile "sephinweis" (vflw-clean (ssg-text "vfl-sep-gf-es-automatisch"))))) (action_tile "accept" "(setq gsepvor (get_tile \"sepvor\")) (setq gsepnach (get_tile \"sepnach\")) (done_dialog 1)") (action_tile "cancel" "(done_dialog 0)") @@ -1577,20 +1609,36 @@ (princ)) ;; --- Modus 2: Lauf-Anfang (NUR wenn der VF-Lauf der Pfadanfang ist) --- -;; "1"=ohne Umlenkstation (offener Anfang) / "2"=Umlenkstation direkt (kein -;; GF1/Separator) / "3"=Umlenkstation mit GF1+Separator (heutiges Standard- -;; verhalten). Plain ok_cancel, analog vflw-m2-lauf-abschluss-impl. -(defun vflw-m2-lauf-anfang-impl (kopf-txt / dat ergebnis gwahl) +;; Legt den selbstbeschreibenden WERT ab, nicht die Auswahl-Ziffer: +;; "mit-gf1" = Umlenkstation mit GF1+Separator (Regelfall, Option 1), +;; "direkt" = Umlenkstation direkt ohne GF1/Separator (Option 2), +;; "ohne" = ohne Umlenkstation, offener Anfang (Option 3). +;; Begruendung und Alt-Zuordnung: siehe vfl-m2-anfang-norm. +;; Plain ok_cancel, analog vflw-m2-lauf-abschluss-impl. +;; as-gesetzt: T, wenn der Nutzer vorher ein AS-Element gewaehlt hat. Dann +;; ist "Ohne Umlenkstation" gesperrt (Nutzer-Vorgabe 2026-10-23) - diese +;; Option setzt intern as-vorhanden wieder auf nil und wuerde das AS also +;; stillschweigend verwerfen; ausserdem braucht ein Lauf mit AS zwingend eine +;; Umlenkstation. Ohne AS bleibt sie waehlbar. +(defun vflw-m2-lauf-anfang-impl (kopf-txt as-gesetzt / dat ergebnis gwahl) (setq dat (load_dialog (vflw-dcl-pfad))) (if (not (new_dialog "vflw_m2_lauf_anfang" dat)) (if (and dat (>= dat 0)) (unload_dialog dat)) (progn (set_tile "kopf" (vflw-clean kopf-txt)) (set_tile "anfang_umlenk_gf1" "1") + (if as-gesetzt + (progn (mode_tile "anfang_offen" 1) + ;; Kurzform in Klammern (wie in der Konsolenliste unter der + ;; gesperrten Option) - der volle Satz passte nicht in die + ;; Zeile und wurde abgeschnitten. Als Messagebox-Text bleibt + ;; er in vfl-m2-anfang-offen-gesperrt stehen. + (set_tile "anfanghinweis" + (vflw-clean (ssg-text "vfl-m2-anfang-offen-gesperrt-zeile"))))) (action_tile "accept" - "(setq gwahl (cond ((= (get_tile \"anfang_offen\") \"1\") \"1\") - ((= (get_tile \"anfang_umlenk\") \"1\") \"2\") - (t \"3\"))) + "(setq gwahl (cond ((= (get_tile \"anfang_offen\") \"1\") \"ohne\") + ((= (get_tile \"anfang_umlenk\") \"1\") \"direkt\") + (t \"mit-gf1\"))) (done_dialog 1)") (action_tile "cancel" "(done_dialog 0)") (setq ergebnis (start_dialog)) @@ -1895,7 +1943,18 @@ ;; Original-Reihenfolge/-Journal im Loop: vfl-menu (Typ), dann bei GF ;; vfl-in-real (Neigung). Bei VF wird KEIN Neigungswert gefragt -> nur "2" ;; in die Pending-Queue. neigung-default: Vorbelegung Neigungsfeld (Grad). -(defun vflw-seg-linie-m2-impl (kopf-txt neigung-default / dat ergebnis gtyp gwert gsepvor gsepnach) +;; es-folgt-sep: T, wenn direkt hinter diesem Segment das ES-Element sitzt. +;; Dann ist "Separator NACH" gesperrt - vfl-insert-es-element bringt seinen +;; Separator immer selbst mit, ein zweiter stuende unmittelbar daneben +;; (Nutzer-Befund 2026-10-23, im Bau-Log zweimal +;; "Staustrecke_Separator_SP_300_mm" in Folge). Die Sperre gehoert GENAU +;; hierher: in Modus 2 sitzen die Separator-Kacheln in DIESEM Segment-Dialog, +;; nicht im separaten vflw_sep_gf von Modus 1. Ein zusaetzlicher Dialog waere +;; doppelt UND wuerde zwei ueberzaehlige Antworten in die Pending-Queue legen. +;; Die Antwort selbst bleibt erhalten (gesperrte Kachel = "Nein"), das +;; Journal-Format aendert sich also nicht. +(defun vflw-seg-linie-m2-impl (kopf-txt neigung-default es-folgt-sep / + dat ergebnis gtyp gwert gsepvor gsepnach) (setq dat (load_dialog (vflw-dcl-pfad))) (if (not (new_dialog "vflw_seg_linie_m2" dat)) (if (and dat (>= dat 0)) (unload_dialog dat)) @@ -1903,9 +1962,15 @@ (set_tile "kopf" (vflw-clean kopf-txt)) (set_tile "typ_gf" "1") (set_tile "wert" (rtos (float neigung-default) 2 1)) - ;; Neigungsfeld + Separator-Toggles nur bei GF aktiv. + (if es-folgt-sep + (progn (mode_tile "sep_nach" 1) + (set_tile "sephinweis" (vflw-clean (ssg-text "vfl-sep-gf-es-automatisch"))))) + ;; Neigungsfeld + Separator-Toggles nur bei GF aktiv - "NACH" bleibt + ;; dabei gesperrt, wenn das ES folgt (sonst machte der Wechsel + ;; GF/VF/GF die Sperre wieder zunichte). (action_tile "typ_gf" - "(mode_tile \"wert\" 0) (mode_tile \"sep_vor\" 0) (mode_tile \"sep_nach\" 0)") + (strcat "(mode_tile \"wert\" 0) (mode_tile \"sep_vor\" 0) (mode_tile \"sep_nach\" " + (if es-folgt-sep "1" "0") ")")) (action_tile "typ_vf" "(mode_tile \"wert\" 1) (mode_tile \"sep_vor\" 1) (mode_tile \"sep_nach\" 1)") (action_tile "accept" @@ -2299,8 +2364,17 @@ (if (and v (member kind '("STR" "INT"))) (progn (setq label nil) - (if (and *vflw-menu-optionen* - (setq idx (if (= kind "INT") v (atoi v))) + ;; idx MUSS vor dem Vergleich eine Zahl sein. Bei kind "INT" wird der + ;; Wert unveraendert durchgereicht - steht dort (z.B. nach einem + ;; Queue-Versatz oder aus einem Alt-Journal) ein String oder eine + ;; Liste, brach (>= idx 1) frueher mit "bad argument type" ab und riss + ;; den ganzen Bau mit (realer Absturz 2026-10-23 in der Winkelwahl). + ;; Eine reine ECHO-Funktion darf das nie: im Zweifel wird der Wert + ;; eben roh ausgegeben statt als Options-Text. + (setq idx (cond ((= kind "INT") (if (numberp v) v nil)) + ((= (type v) 'STR) (atoi v)) + (t nil))) + (if (and *vflw-menu-optionen* idx (numberp idx) (>= idx 1) (<= idx (length *vflw-menu-optionen*))) (setq label (vflw-clean (nth (1- idx) *vflw-menu-optionen*)))) (vfl-echo-wahl (if label label (if (= kind "STR") v (vl-princ-to-string v)))) @@ -2870,8 +2944,20 @@ (defun vfl-journal-replay-start (forward-journal) (setq *vfl-replay-queue* forward-journal *vfl-journal* '() *vfl-seg-glied-letzte* 0) + ;; Herkunft des Journals zuruecksetzen - der Aufrufer traegt sie direkt + ;; danach nach, wenn er einen Block kennt (vfl-replay-kette-setzen). + (setq *vfl-replay-kette* nil) (if (boundp '*vflw-pending*) (setq *vflw-pending* nil))) +;; Ketten-Fundstelle fuer Meldungen waehrend eines Replays: Blockname der +;; gerade abgespielten Kette, "-" wenn unbekannt (Live-Bau). Die Aufrufer von +;; vfl-journal-replay-start setzen *vfl-replay-kette* direkt danach - den Namen +;; muessen sie VOR dem entdel des Altblocks gelesen haben, aus der Zeichnung +;; ist er danach nicht mehr zu holen. Rein diagnostisch, kein Bau-Pfad liest ihn. +(defun vfl-replay-kette-text ( / k) + (setq k (if (boundp '*vfl-replay-kette*) *vfl-replay-kette* nil)) + (if (and k (= (type k) 'STR) (/= k "")) k "-")) + ;; --- Glied-Zerlegung fuer den Editier-Dialog --- ;; Labels der Glieder (STEP-Marker) in Bau-Reihenfolge. (defun vfl-journal-glieder (forward-journal / out) @@ -5173,6 +5259,91 @@ (vf-set-es-masse *vfl-es-winkel* es-seite) (if (vfl-frage-fahrtrichtung-p2 startpunkt) es-seite nil)) +;; Bau-Grenzen EINER VF-Einheit pruefen (Nutzer-Regel 2026-10-22). +;; +;; Eine VF-Einheit ist alles zwischen Umlenkstation und Motorstation. Sie darf +;; - hoechstens max-kurven Vario-Kurven enthalten (Vorgabe 2, also maximal +;; drei VF-Geraden), und +;; - max-laenge nicht ueberschreiten (Vorgabe *vfk-vf-laenge-max* = 25 m). +;; Gemessen wird die Pfadlaenge der Segmente des Laufs - konservativ, weil +;; GF1/GF2 darin noch mitzaehlen, obwohl sie ausserhalb von Umlenk..Motor +;; liegen. Lieber etwas zu frueh warnen als eine nicht baubare Einheit. +;; +;; Reiner Listen-Kern ohne Zeichnung: plan ist die Klassifizierungs-Liste +;; ("Linie"/"Bogen" ...), vf-start/vf-ende grenzen den Lauf ein. +;; +;; Rueckgabe: (laenge kurven teil-idx) +;; laenge - aufsummierte Pfadlaenge des Laufs +;; kurven - Zahl der Vario-Kurven darin +;; teil-idx - nil, wenn alles in den Grenzen liegt; sonst der Index des +;; BOGENS, an dem geteilt werden sollte (dort einen GF-Bogen +;; setzen, damit zwei Einheiten entstehen). Gibt es keinen +;; geeigneten Bogen, ist teil-idx -1 ("zu lang, nicht teilbar"). +(defun vfl-m2-lauf-grenzen (plan vf-start vf-ende max-laenge max-kurven / + i seg summe kurven teil-idx) + (setq i vf-start summe 0.0 kurven 0 teil-idx nil) + (while (<= i vf-ende) + (setq seg (nth i plan)) + (if (= (car seg) "Bogen") + (progn + ;; An DIESEM Bogen teilen, wenn die Einheit davor schon voll ist - + ;; entweder weil sie die erlaubten Kurven schon hat oder weil die + ;; Laenge bis hierher die Grenze reisst. + (if (and (null teil-idx) + (or (>= kurven max-kurven) (> summe max-laenge))) + (setq teil-idx i)) + (setq kurven (1+ kurven)))) + (setq summe (+ summe (caddr seg))) + (setq i (1+ i))) + ;; Grenze gerissen, aber kein Bogen zum Teilen gefunden (reine Gerade oder + ;; alle Boegen liegen zu frueh): nicht teilbar. + (if (and (null teil-idx) (or (> summe max-laenge) (> kurven max-kurven))) + (setq teil-idx -1)) + (list summe kurven teil-idx)) + +;; Formatmarke der Modus-2-Plan-Phase schreiben bzw. beim Replay erkennen. +;; Rueckgabe: T = neues Format (Bogentyp wird auch nach einer VF-Geraden +;; gefragt), nil = Alt-Journal ohne diese Antwort. +;; +;; Live wird die Marke IMMER geschrieben (ohne Frage an den Nutzer, der +;; livefn liefert sie einfach). Beim Replay wird sie NUR konsumiert, wenn sie +;; auch dasteht - sonst bliebe die Queue aus dem Takt. Dasselbe Muster wie +;; die Richtungswahl in Modus 1, nur dass hier gar keine Nutzerfrage dahinter +;; steckt: es geht allein um die Unterscheidbarkeit der Journal-Formate. +(defun vfl-m2-formatmarke ( / ) + (if (and *vfl-replay-queue* (not (equal (vfl-replay-peek-wert) "m2v2"))) + nil + (progn (vfl-in-value "STR" (function (lambda () "m2v2"))) t))) + +;; --- Modus 2: Lauf-Anfang, Antwortwerte ------------------------------------- +;; Die Antwort wird seit 2026-10-23 als selbstbeschreibender WERT journalisiert +;; ("mit-gf1"/"direkt"/"ohne"), nicht mehr als Auswahl-Ziffer. Anlass war die +;; Umstellung der Dialogreihenfolge (Regelfall zuerst): mit einer Ziffer haette +;; dieselbe "3" vor und nach der Umstellung Verschiedenes bedeutet, eine alte +;; Kette waere beim Doppelklick still mit einer ANDEREN Anfangsvariante neu +;; gebaut worden. Mit eigenem Wertevorrat ist jedes kuenftige Umsortieren +;; folgenlos (CLAUDE.md: "Werte selbstbeschreibend fuehren, nicht als +;; positionsabhaengige Indizes/Flags"). +;; Reihenfolge = Reihenfolge der Dialog-/Konsolenoptionen 1..3. +(setq *vfl-m2-anfang-werte* '("mit-gf1" "direkt" "ohne")) + +;; Alt -> Neu (nur fuer Bestandsketten): die Ziffern der ALTEN Optionsfolge +;; 1 = Ohne Umlenkstation, 2 = Umlenkstation direkt, 3 = Umlenkstation mit GF1. +;; Bewusst als Tabelle und nicht als cond-Kette: die Zuordnung ist aus dem +;; heutigen Code nicht mehr ableitbar, und ein Test kann sie direkt durchlaufen. +(setq *vfl-m2-anfang-alt* '(("1" . "ohne") ("2" . "direkt") ("3" . "mit-gf1"))) + +;; Journal-/Antwortwert -> kanonischer Wert, nil bei unbekanntem Wert. +;; nil heisst fuer den Aufrufer: ABBRECHEN. Ein Rueckfall auf eine geratene +;; Anfangsvariante waere genau der lautlose Fehlaufbau, den die Wertumstellung +;; verhindern soll - lieber ein harter Abbruch mit Fundstelle. +(defun vfl-m2-anfang-norm (wert / tr) + (cond + ((not (and wert (= (type wert) 'STR))) nil) + ((member wert *vfl-m2-anfang-werte*) wert) + ((setq tr (assoc wert *vfl-m2-anfang-alt*)) (cdr tr)) + (t nil))) + ;; Punkt um eine Strecke ENTGEGEN der Fahrtrichtung zurueckversetzen. ;; laenge-planar = horizontale (Grundriss-)Laenge, winkel = Gefaelle in Grad ;; (positiv = fallend). Vorwaerts gilt @@ -9822,6 +9993,7 @@ (setq *ssg-ils-dim* ziel-dim) ; Zieldim erzwingen (entdel ent) ; alten Block weg (vfl-journal-replay-start journal) ; volle Queue, KEIN Truncate + (setq *vfl-replay-kette* bname) ; Fundstelle fuer Replay-Meldungen (setq ok (vl-catch-all-apply (cond ((= marker "linienzug2") 'vf-linienzug-modus2) ((= marker "linienzug3") 'vf-linienzug-modus3) @@ -9937,15 +10109,19 @@ ;; 'vf-linienzug-modus3 fuer "linienzug3" (Segment-Essenz per SEGS - unabhaengig ;; von den Original-Objekten). Beide Modi haben eine flache Klassifizierungs- ;; Schleife ohne editierbare Einzelglieder, daher derselbe volle Reset. -(defun vfl-edit-ent-voll (ent journal bau-fn / dim) +(defun vfl-edit-ent-voll (ent journal bau-fn / dim bname) ;; Dimension des Bestandsblocks VOR dem entdel merken und beim Neubau als ;; Override erzwingen (dimensionstreuer Doppelklick-Neubau, sonst globale ;; DXFM_DIM). Danach Override zuruecksetzen. (setq dim (ssg-dim-xdata-lesen ent)) + ;; Blockname ebenfalls VOR dem entdel sichern - danach ist er weg, eine + ;; Meldung aus dem Replay koennte die Kette sonst nicht mehr benennen. + (setq bname (cdr (assoc 2 (entget ent)))) (entdel ent) (princ (ssg-text "vfl-edit2-reset-info")) (if dim (setq *ssg-ils-dim* dim)) (vfl-journal-replay-start journal) + (setq *vfl-replay-kette* bname) ;; DIREKTER Aufruf, KEIN vl-catch-all-apply - identische Begruendung wie im ;; Sektions-Zweig von vfl-edit-ent (siehe dortiger Kommentar): der alte Block ;; ist per entdel schon weg, bei einem Abbruch muss der *error*-Handler der @@ -11080,11 +11256,13 @@ hoehe-bis soll-ende ist-ende old-error vfl-ins vf-run-member carrier-idx kv-variante seg-hz letzt-koerper-hz vf-first-line vf-last-line climber-span mid-hor + m2-format-neu grenz-erg letzte-linie es-folgt-sep + uebergang-fp br-lvf this-lvf nach-kurve nach-vf sep-vor-gf sep-nach-gf n-climb n-hor target-climb winkel-list fdz fdx wslope lvf planar-len ang-diff best-diff w - climb-thresh longest-idx climbers nonclimber-len + longest-idx climbers nonclimber-len nonclimber-cnt nkurve kurve-chords run-span hor-koerper-len hor-pairs flat-p flat-zone-erste fill-len wahl3 rad3v feste-vf dH-adj dir-x dir-y fill-D end-hz @@ -11093,7 +11271,8 @@ gf-total br-gf-mode br-gf2-exp filler-A-len filler-a-done as-vorhanden es-vorhanden dbg-an dbg-old-error m2-lauf-antwort m2-offenes-ende es-frueh-abgelehnt - m2-front-antwort m2-offener-anfang m2-kein-gf1-vorne entry-fixed) + m2-front-roh m2-front-antwort m2-offener-anfang + m2-kein-gf1-vorne entry-fixed) (princ "\n\n=========================================") (princ (ssg-text "vfl-m3-titel")) (princ "\n=========================================") @@ -11194,7 +11373,31 @@ ;; --- 5. Klassifizierung (Phase A: nur speichern, nichts bauen) --- ;; Plan-Eintrag Gerade: ("Linie" hz laenge klass winkel) klass=GF/VF ;; Plan-Eintrag Bogen : ("Bogen" hz chord bwinkel bseite klass) + ;; + ;; FORMATMARKE der Plan-Phase. Seit 2026-10-22 wird der Typ eines Bogens + ;; AUCH nach einer VF-Geraden gefragt (vorher Vario-Kurve erzwungen, ohne + ;; Journal-Eintrag). Aeltere "linienzug2"-Ketten haben diese Antwort also + ;; nicht, und am WERT allein ist das nicht zu erkennen: Bogentyp und + ;; Vario-Variante benutzen beide "1"/"2". Darum eine eigene, selbst- + ;; beschreibende Marke ("m2v2") vor der Schleife - sie steht im Journal nur + ;; bei neu gebauten Ketten und wird beim Replay nur dann konsumiert, wenn + ;; sie wirklich da ist. Alles Alte laeuft unveraendert im Zwangs-Modus. + (setq m2-format-neu (vfl-m2-formatmarke)) (setq n (length segmente) i 0 plan '()) + ;; Index der LETZTEN Geraden im Pfad. Nur dort schliesst das ES-Element + ;; direkt an - und das bringt seinen Separator IMMER selbst mit + ;; (vfl-insert-es-element). Die Frage "Separator NACH dieser + ;; Gefaellestrecke" wird dort darum gesperrt; ohne das standen zwei + ;; Separatoren unmittelbar hintereinander vor dem ES (Nutzer-Befund + ;; 2026-10-23, im Bau-Log zweimal "Staustrecke_Separator_SP_300_mm" in + ;; Folge). Gesperrt wird im Segment-Dialog selbst (vflw-seg-linie-m2-impl, + ;; Parameter es-folgt-sep) - Modus 1 macht dasselbe in seinem eigenen + ;; Dialog vflw_sep_gf. + (setq letzte-linie -1) + (while (< i n) + (if (= (car (nth i segmente)) "Linie") (setq letzte-linie i)) + (setq i (1+ i))) + (setq i 0) (while (< i n) (setq seg (nth i segmente) typ (car seg) hz (cadr seg) laenge (caddr seg)) ;; seg-ent: KEINE Live-Entity mehr - Modus 2 baut jetzt aus der Segment- @@ -11204,21 +11407,33 @@ ;; Original-Objekten, ein Highlight haette beim Doppelklick-Neubau ohnehin ;; keine Entity mehr. (setq seg-ent nil) + ;; Segmentkopf fuer ALLE Dialoge dieses Segments setzen - auch fuer die, + ;; die auf einen generischen Dialog zurueckfallen (Vario-Variante, + ;; Separator vor/nach). Ohne ihn stand dort nur die Frage, ohne Bezug zum + ;; Segment (Nutzer-Befund 2026-10-22). Siehe vflw-kopf-text. + (setq *vflw-seg-kopf* (strcat "Segment " (itoa (1+ i)) "/" (itoa n))) + ;; Schliesst an DIESES Segment direkt das ES-Element an? Dann bringt es + ;; seinen Separator selbst mit und "Separator NACH" muss gesperrt sein + ;; (siehe letzte-linie oben). Wird sowohl dem Segment-Dialog als auch dem + ;; Konsolenweg unten uebergeben. + (setq es-folgt-sep (and es-vorhanden (= i letzte-linie))) ;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die ;; unveraenderten vfl-menu/vfl-in-*-Aufrufe unten konsumieren die Antworten ;; aus *vflw-pending*). Der nach-kurve-Fall stellt keine Frage (auto-VF) -> - ;; kein Dialog. Ebenso nach-vf bei einem Bogen: Vario-Kurve ist dann - ;; erzwungen (eine VF-Einheit kann nicht in einen GF-Bogen auslaufen). + ;; kein Dialog. Beim Bogen NACH einer VF-Geraden gibt es seit 2026-10-22 + ;; wieder eine Typ-Frage (siehe unten) - der Dialog kommt also auch dort; + ;; nur beim Nachbau eines Alt-Journals bleibt er aus, weil dessen Antwort + ;; an der Stelle fehlt. (if (and (vfl-wizard-aktiv) (not (and (= typ "Linie") nach-kurve)) - (not (and (= typ "Bogen") nach-vf))) + (not (and (= typ "Bogen") nach-vf (not m2-format-neu)))) (progn (vfl-seg-highlight seg-ent T) (if (= typ "Linie") (vflw-seg-linie-m2-impl (vfl-seg-kopf (1+ i) n (strcat "Gerade L=" (rtos laenge 2 0) " mm hz=" (rtos hz 2 1))) - *vfk-gefaelle-winkel*) + *vfk-gefaelle-winkel* es-folgt-sep) (vflw-seg-bogen-impl (vfl-seg-kopf (1+ i) n (strcat "Eck/Bogen " (itoa (nth 3 seg)) " Grad " (nth 4 seg))) @@ -11253,6 +11468,12 @@ ;; Planlaenge bleibt hier UNVERAENDERT (reale Pfadlaenge, fuer ;; die Anker-Vorschau noetig); die Trennung in GF-Koerper+ ;; Separator passiert erst beim Bauen (Phase B). + ;; Hinweis fuer den Konsolenweg; im Wizard sperrt der + ;; Segment-Dialog die Kachel "Separator NACH" selbst (siehe + ;; vflw-seg-linie-m2-impl, Parameter es-folgt-sep) - ein + ;; ZUSAETZLICHER Dialog waere hier falsch, er wuerde zwei + ;; ueberzaehlige Antworten in die Pending-Queue legen. + (if es-folgt-sep (princ (ssg-text "vfl-sep-gf-es-automatisch"))) (princ (ssg-text "vfl-sep-vor-gf-frage")) (setq sep-vor-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2") (list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2 @@ -11261,18 +11482,38 @@ (setq sep-nach-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2") (list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2 "vfl-sep-nach-gf-frage") "1")) + ;; Sicherheitsnetz fuer den reinen Konsolenpfad (dort gibt es + ;; kein Ausgrauen): Antwort verwerfen und melden - wie Modus 1. + (if (and sep-nach-gf es-folgt-sep) + (progn (vfl-meldung (ssg-text "vfl-sep-nach-durch-es-ueberschrieben")) + (setq sep-nach-gf nil))) (setq plan (cons (list "Linie" hz laenge "GF" winkel sep-vor-gf sep-nach-gf) plan)) (setq nach-vf nil)))))) ((= typ "Bogen") (setq bwinkel (nth 3 seg) bseite (nth 4 seg)) (princ (ssg-textf "vfl-m3-seg-bogen" (list (itoa (1+ i)) (itoa n) (itoa bwinkel) bseite))) - (if nach-vf - (setq antwort "2") ; nach VF-Gerade: Vario-Kurve erzwungen, keine Typ-Frage + ;; Nach einer VF-Geraden wurde der Bogen frueher ZWINGEND zur + ;; Vario-Kurve ("eine VF-Einheit kann nicht in einen GF-Bogen + ;; auslaufen"). Das stimmt nicht: die VF-Einheit endet mit der + ;; Motorstation, danach ist die Kette wieder auf der 3-Grad-Basis, wo + ;; ein GF-Bogen das normale Element ist - Modus 1 erlaubt genau das + ;; (Nutzer-Korrektur 2026-10-22). Gefragt wird daher immer; nach einer + ;; VF-Geraden ist die Vario-Kurve nur noch die VORAUSWAHL. + ;; Ein GF-Bogen beendet den Lauf; entstuende dadurch ein ZWEITER + ;; VF-Lauf, faengt das die bestehende "genau ein Lauf"-Pruefung ab. + ;; + ;; ALT-JOURNAL: dort fehlt die Antwort an dieser Stelle (die Frage gab + ;; es nicht). Erkannt wird das an der Formatmarke am Anfang der + ;; Plan-Phase (m2-format-neu) - am WERT der naechsten Antwort ginge es + ;; nicht, denn Typ und Variante benutzen beide "1"/"2". + (if (and nach-vf (not m2-format-neu)) + (setq antwort "2") ; Alt-Journal: Vario-Kurve war erzwungen (progn (princ (ssg-text "vfl-m3-typ-bogen")) (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") - (list "GF-Bogen" "Vario-Kurve") 1 + (list "GF-Bogen" "Vario-Kurve") + (if nach-vf 2 1) "vfl-m3-typ-bogen")))) (setq nach-vf nil) (if (= antwort "2") @@ -11288,6 +11529,9 @@ (setq plan (cons (list "Bogen" hz laenge bwinkel bseite "GF-Bogen" nil) plan)) (setq nach-kurve nil))))) (setq i (1+ i))) + ;; Segmentkopf wieder abschalten - ab hier sind die Fragen nicht mehr einem + ;; einzelnen Pfad-Segment zugeordnet (Lauf-Anfang/-Ende, Winkelwahl). + (setq *vflw-seg-kopf* nil) (setq plan (reverse plan)) ;; --- 6. VF-Lauf finden: VF-Geraden UND Vario-Kurven bilden EINEN Lauf --- @@ -11316,6 +11560,34 @@ ;; headless nur princ - blockiert also einen Batch-Lauf nicht). (vfl-meldung (ssg-textf "vfl-m3-vf-lauf-alert" (list (itoa vf-count)))) (exit))) + + ;; --- Bau-Grenzen der VF-Einheit (Nutzer-Regel 2026-10-22) --- + ;; Zwischen Umlenkstation und Motorstation: hoechstens 2 Vario-Kurven (also + ;; maximal drei VF-Geraden) und hoechstens *vfk-vf-laenge-max* (25 m). + ;; Bisher war die Laenge nur ein HINWEIS nach dem Solver - die Kette wurde + ;; trotzdem gebaut, obwohl sie so nicht herstellbar ist. Jetzt wird vor dem + ;; Bau abgebrochen, und zwar MIT der Stelle, an der zu teilen ist: den dort + ;; genannten Bogen als GF-Bogen klassifizieren, dann entstehen zwei + ;; Einheiten (die automatische Teilung kommt mit der Mehr-Lauf-Stufe M3b, + ;; siehe vfl-m3-vf-lauf-m3b). + (setq grenz-erg (vfl-m2-lauf-grenzen plan vf-start vf-ende + *vfk-vf-laenge-max* *vfk-vf-kurven-max*)) + (if (nth 2 grenz-erg) + (progn + (if (< (nth 2 grenz-erg) 0) + (vfl-meldung (ssg-textf "vfl-m2-einheit-grenze-unteilbar" + (list (rtos (/ (nth 0 grenz-erg) 1000.0) 2 1) + (itoa (nth 1 grenz-erg)) + (rtos (/ *vfk-vf-laenge-max* 1000.0) 2 1) + (itoa (fix *vfk-vf-kurven-max*))))) + (vfl-meldung (ssg-textf "vfl-m2-einheit-grenze" + (list (rtos (/ (nth 0 grenz-erg) 1000.0) 2 1) + (itoa (nth 1 grenz-erg)) + (rtos (/ *vfk-vf-laenge-max* 1000.0) 2 1) + (itoa (fix *vfk-vf-kurven-max*)) + (itoa (1+ (nth 2 grenz-erg))))))) + (exit))) + ;; Traeger-Stueck = laengste VF-Gerade im Lauf (traegt die ganze Hoehe). (setq carrier-idx -1 i vf-start) (while (<= i vf-ende) @@ -11387,22 +11659,44 @@ (setq m2-offener-anfang nil m2-kein-gf1-vorne nil) (if (= vf-start 0) (progn + ;; Reihenfolge UND Nummerierung wie im Dialog: Regelfall zuerst, + ;; Sonderfall zuletzt (Nutzer-Wunsch 2026-10-23). Die Ziffer ist reine + ;; Anzeige - journalisiert wird der WERT (vfl-menu-strwert), ein + ;; Umsortieren beruehrt bestehende Ketten darum nicht. (princ (ssg-text "vfl-m3-lauf-anfang-header")) - (princ (ssg-text "vfl-m3-lauf-anfang-opt-offen")) - (princ (ssg-text "vfl-m3-lauf-anfang-opt-umlenk")) (princ (ssg-text "vfl-m3-lauf-anfang-opt-umlenk-gf1")) + (princ (ssg-text "vfl-m3-lauf-anfang-opt-umlenk")) + (princ (ssg-text "vfl-m3-lauf-anfang-opt-offen")) + ;; Begruendung direkt unter der gesperrten Option. + (if as-vorhanden (princ (ssg-text "vfl-m2-anfang-offen-gesperrt-zeile"))) (if (vfl-wizard-aktiv) - (vl-catch-all-apply 'vflw-m2-lauf-anfang-impl (list (ssg-text "vfl-m3-lauf-anfang-header")))) - (setq m2-front-antwort (vfl-menu (ssg-text "vfl-prompt-wahl-1-3-def3") - (list (ssg-text "vfl-m3-lauf-anfang-opt-offen") - (ssg-text "vfl-m3-lauf-anfang-opt-umlenk") - (ssg-text "vfl-m3-lauf-anfang-opt-umlenk-gf1")) 3 - "vfl-m3-lauf-anfang-header")) + (vl-catch-all-apply 'vflw-m2-lauf-anfang-impl + (list (ssg-text "vfl-m3-lauf-anfang-header") as-vorhanden))) + (setq m2-front-roh + (vfl-menu-strwert (ssg-text "vfl-prompt-wahl-1-3-def1") + (list (ssg-text "vfl-m3-lauf-anfang-opt-umlenk-gf1") + (ssg-text "vfl-m3-lauf-anfang-opt-umlenk") + (ssg-text "vfl-m3-lauf-anfang-opt-offen")) + *vfl-m2-anfang-werte* 1 + "vfl-m3-lauf-anfang-header")) + (setq m2-front-antwort (vfl-m2-anfang-norm m2-front-roh)) (cond - ((= m2-front-antwort "1") + ;; Unbekannter Wert (weder Alt-Ziffer noch gueltiger Wert): abbrechen + ;; statt zu raten - mit Kette und Wert, damit die Fundstelle bleibt. + ((null m2-front-antwort) + (vfl-meldung (ssg-textf "vfl-m2-anfang-wert-unbekannt" + (list (vfl-replay-kette-text) + (vl-princ-to-string m2-front-roh)))) + (exit)) + ;; Sicherheitsnetz fuer den Konsolenweg (dort gibt es kein + ;; Ausgrauen): "Ohne Umlenkstation" mit gesetztem AS wuerde das + ;; AS-Element verwerfen - abweisen und auf den Regelfall gehen. + ((and (= m2-front-antwort "ohne") as-vorhanden) + (vfl-meldung (ssg-text "vfl-m2-anfang-offen-gesperrt"))) + ((= m2-front-antwort "ohne") (setq as-vorhanden nil m2-offener-anfang t) (princ (ssg-text "vfl-m3-lauf-anfang-offen-hinweis"))) - ((= m2-front-antwort "2") (setq m2-kein-gf1-vorne t))))) + ((= m2-front-antwort "direkt") (setq m2-kein-gf1-vorne t))))) ;; --- Lauf-Abschluss waehlen, NUR wenn der VF-Lauf das Pfadende ist (kein ;; Back-Lauf danach) - sonst bleibt die Steuerung komplett unveraendert @@ -11559,11 +11853,29 @@ ;; faelschlich angenommenen Separator+ES-Abstiegs. (if (or m2-offenes-ende (not es-vorhanden)) (setq z-junction (float end-hoehe))) (setq dH-bruecke (- z-front z-junction)) - ;; Modell: EIN Vario mit Horizontal-Mitte. Nur VF-Geraden, die LANG GENUG sind - ;; (Vertikalboegen brauchen viel Platz), tragen die Hoehe (Kletterer, gleicher - ;; Winkel). Zu kurze VF-Geraden werden horizontal (nur Anschluss/Motor). - ;; Mindestens die laengste VF-Gerade klettert immer. - (setq climb-thresh (if (boundp '*vfl-min-climber-laenge*) *vfl-min-climber-laenge* 3000.0)) + ;; Modell: EIN Vario mit Horizontal-Mitte. GENAU EINE VF-Gerade traegt die + ;; Hoehe - die LAENGSTE des Laufs; alle anderen werden flache Fueller. + ;; + ;; REGEL (Nutzer-Vorgabe 2026-10-22): verbindet eine Vario-Kurve zwei + ;; VF-Geraden, wird die KUERZERE flach gebaut und die LAENGERE klettert. + ;; Bei mehr als zwei Geraden im Lauf ist das genau "nur die laengste + ;; klettert". + ;; + ;; WARUM das keine Geschmacksfrage ist: der Solver unten budgetiert EIN + ;; Kletterstueck mit EINEM Paar Vertikalboegen (auf_6/ab_6). Der Bau setzt + ;; dieses Paar aber je Kletterer. Frueher klettete zusaetzlich jede + ;; VF-Gerade ab *vfl-min-climber-laenge* (3000 mm) - bei zwei Kletterern + ;; fehlten dadurch ein zweites Bogenpaar UND die Uebergaenge der zusaetzlich + ;; entstehenden flachen Zone im Laengenbudget. Realer Fehler 2026-10-22: + ;; zwei Kletterer (9700 + 4296 mm), das erste Segment wurde 1355.9 mm zu + ;; lang gebaut, die ganze Kette ueberschoss um 3185 mm - bei korrekter + ;; Hoehe (dZ nur -10.2 mm), also rein ein Laengen-, kein Hoehenproblem. + ;; Mit genau einem Kletterer passt das Modell wieder zum Bau; die + ;; verbleibende Laenge der flachen Zone faengt der letzte Fueller ab, der + ;; ohnehin auf den Zielpunkt rechnet (vfl-zsolve-es-fueller). + ;; + ;; *vfl-min-climber-laenge* wird dadurch fuer die Auswahl nicht mehr + ;; gebraucht (siehe doc/VarioFoerderer_Linienzug_Prinzipien.md, Abschnitt 14). (setq longest-idx -1 i vf-start) (while (<= i vf-ende) (setq seg (nth i plan)) @@ -11577,7 +11889,9 @@ (setq seg (nth i plan)) (cond ((and (= (car seg) "Linie") (= (nth 3 seg) "VF")) - (if (or (= i longest-idx) (>= (caddr seg) climb-thresh)) + ;; NUR die laengste VF-Gerade klettert (siehe Regel oben) - jede + ;; weitere wird flacher Fueller. + (if (= i longest-idx) (setq climbers (cons i climbers) climber-span (+ climber-span (caddr seg))) (setq nonclimber-len (+ nonclimber-len (caddr seg)) nonclimber-cnt (1+ nonclimber-cnt)))) ((= (car seg) "Bogen") @@ -11653,7 +11967,31 @@ ;; es-along-fix (reine Laenge, siehe oben) NUR aus dem Laengenbudget (span) ;; abziehen - NICHT aus feste-vf, sonst floss es faelschlich ueber sin(3°) ;; auch in die Hoehenbilanz ein (siehe Kommentar oben). - (setq wahl3 (vfl-zsolve-winkel-waehlen (- climber-span es-along-fix) dH-adj feste-vf)) + ;; --- Uebergangsboegen der flachen Zone ins LAENGENbudget --- + ;; vfl-zsolve-flach-ein (Vario_Bogen_auf_3) wird mit der Richtung des + ;; KLETTERERS eingebaut, liegt also physisch noch in dessen Pfad-Segment - + ;; im Solver-Budget kam er bisher gar nicht vor. Realer Befund 2026-10-23: + ;; Segment 1 (9700 mm) war mit Einlauf + auf_9 + Koerper + ab_9 exakt + ;; gefuellt, der auf_3 schob die Vario-Kurve danach um 199.9 mm hinter die + ;; gezeichnete Ecke; alles Folgende war mitverschoben. Spiegelbildlich gilt + ;; das fuer vfl-zsolve-flach-aus (Vario_Bogen_ab_3) beim EINTRITT in den + ;; Kletterer, wenn eine flache Zone vorausgeht. + ;; Nur die LAENGE wird korrigiert - die Hoehe dieser Boegen steckt bereits + ;; in *vfk-bruecke-korrektur* (siehe dH-adj oben). + (setq uebergang-fp 0.0) + (if (> n-climb 0) + (progn + (if (< (car climbers) vf-ende) ; flache Zone FOLGT -> flach-ein + (setq uebergang-fp + (+ uebergang-fp + (car (vfl-projiziere-neigung (car (get-bogen-mass bogen-auf 3)) + (caddr (get-bogen-mass bogen-auf 3)) + (fix *vfk-gefaelle-winkel*)))))) + (if (> (car climbers) vf-start) ; flache Zone GEHT VORAUS -> flach-aus + (setq uebergang-fp + (+ uebergang-fp (car (get-bogen-mass bogen-ab 3))))))) + (setq wahl3 (vfl-zsolve-winkel-waehlen + (- climber-span es-along-fix uebergang-fp) dH-adj feste-vf)) (if (null wahl3) (progn ;; Stufe 2: KEIN Tabellenwinkel passt (der/die Kletterer sind bei JEDER @@ -11750,7 +12088,18 @@ (vf-vfhoehe-schliesse pt) (vfl-acc-vf-seg "horizontal" 0 filler-A-len) (setq anzahl-vf (1+ anzahl-vf) filler-a-done t))))) - (if (= i vf-ende) + ;; Der Ziel-Solve des letzten Fuellers gilt NUR, wenn der VF-Lauf + ;; zugleich das Pfadende ist. Seine Annahme steht unten im Kommentar: + ;; der Schwanz verschiebt sich STARR entlang seg-hz. Folgt nach dem + ;; Lauf noch ein Back-Lauf (GF-Bogen und/oder GF-Gerade), stimmt das + ;; nicht mehr - die Kette knickt dort ab, und der Fueller wurde mit + ;; einem falschen Modell bemessen (realer Befund 2026-10-23: Lauf + ;; endete bei Segment 3, danach kamen GF-Bogen + GF; Endpunkt lag + ;; 111.6 mm NEBEN der ES-Achse, bei exakter Hoehe). In diesem Fall + ;; bekommt der Fueller schlicht die Restlaenge seines Segments; um + ;; Separator + ES kuemmert sich der Back-Lauf selbst (siehe dort die + ;; last-line-Reservierung). + (if (and (= i vf-ende) (= vf-ende (1- n))) ;; letzter Fueller, MIT ES-Element (unveraendert): Laenge so, dass der ;; ENDPUNKT auf der ES-KS_AUS-ACHSE liegt (Spiegel der AS-Regel). Der ;; Schwanz (Fueller + ab_3 + Motor + GF2 + Separator + ES) verschiebt diff --git a/dcl/vf_linienzug_wizard.dcl b/dcl/vf_linienzug_wizard.dcl index 9dfdd20..a9cd3db 100644 --- a/dcl/vf_linienzug_wizard.dcl +++ b/dcl/vf_linienzug_wizard.dcl @@ -547,9 +547,19 @@ vflw_sep_gf : dialog { key = "sepvor"; label = "Separator VOR dieser Gefaellestrecke"; } - : toggle { - key = "sepnach"; - label = "Separator NACH dieser Gefaellestrecke"; + // Steht am Kettenende ein ES-Element, ist "Separator NACH" gesperrt - + // der Hinweis dahinter sagt warum, in EINER Zeile mit dem Ankreuzfeld + // (gleiche Anordnung wie in vflw_seg_linie_m2). Sonst leer (set_tile ""). + : row { + : toggle { + key = "sepnach"; + label = "Separator NACH dieser Gefaellestrecke"; + } + : text { + key = "sephinweis"; + label = ""; + width = 62; + } } } @@ -1090,7 +1100,13 @@ vflw_seg_linie_m2 : dialog { : boxed_column { label = "Separator (nur bei GF):"; : toggle { key = "sep_vor"; label = "Separator VOR diesem GF-Stueck"; } - : toggle { key = "sep_nach"; label = "Separator NACH diesem GF-Stueck"; } + // Begruendung, wenn "NACH" gesperrt ist (ES folgt direkt), in EINER + // Zeile hinter dem Ankreuzfeld - sonst liest sie sich wie eine eigene + // Aussage statt wie die Erklaerung zu genau diesem Feld. Sonst leer. + : row { + : toggle { key = "sep_nach"; label = "Separator NACH diesem GF-Stueck"; } + : text { key = "sephinweis"; label = ""; width = 62; } + } } } @@ -1200,13 +1216,24 @@ vflw_m2_lauf_anfang : dialog { label = ""; width = 60; } + // Reihenfolge nach Haeufigkeit (Nutzer-Wunsch 2026-10-23): der + // Regelfall zuerst, der Sonderfall "offener Anfang" zuletzt. Die + // radio_button-KEYS bleiben dabei unveraendert; journalisiert wird der + // am Key haengende WERT ("mit-gf1"/"direkt"/"ohne", siehe + // vfl-m2-anfang-norm), nicht die Position - Umsortieren und Umnummerieren + // sind damit folgenlos fuer bestehende Ketten. : boxed_radio_column { label = "Lauf beginnen mit:"; key = "anfang"; - : radio_button { key = "anfang_offen"; label = "Ohne Umlenkstation (offener Anfang, spaeter fortsetzen)"; } - : radio_button { key = "anfang_umlenk"; label = "Umlenkstation direkt (kein GF1/Separator)"; } - : radio_button { key = "anfang_umlenk_gf1"; label = "Umlenkstation mit GF1 + Separator"; value = "1"; } + // Nummern wie in der Konsolenausgabe, damit Dialog und Protokoll + // dieselbe Option gleich benennen. + : radio_button { key = "anfang_umlenk_gf1"; label = "1 - Umlenkstation mit GF1 + Separator"; value = "1"; } + : radio_button { key = "anfang_umlenk"; label = "2 - Umlenkstation direkt (kein GF1/Separator)"; } + : radio_button { key = "anfang_offen"; label = "3 - Ohne Umlenkstation (offener Anfang, spaeter fortsetzen/verschmelzen)"; } } + // Begruendung, wenn "Ohne Umlenkstation" gesperrt ist (AS gesetzt). + // Breit genug fuer den vollen Satz - bei width = 60 wurde er abgeschnitten. + : text { key = "anfanghinweis"; label = ""; width = 76; } } ok_cancel; diff --git a/lang/de_DE.json b/lang/de_DE.json index 680c0e5..d742073 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -9,6 +9,7 @@ "vfl-as-auto-erkannt": "\n>>> Startpunkt liegt auf KS_AUS einer bestehenden %1-Kette - kein AS-Element noetig.", "vfl-familie-neutral": "GF/VF", "vfl-sep-nach-durch-es-ueberschrieben": "\n>>> Separator NACH ignoriert - das ES-Element bringt beim Kettenende bereits seinen eigenen Separator mit (sonst zwei Separatoren hintereinander).", + "vfl-sep-gf-es-automatisch": "(Ein Separator zwischen GF und ES wird automatisch gesetzt.)", "vfl-echo-position": "\n>>> Position: X=%1 Y=%2 Z=%3", "vfl-echo-wahl": "\n>>> Gewaehlt: %1", "vfl-aus-seite-header": "\n\nAUS-Element (AS_Element_*) - Seite waehlen:", @@ -250,13 +251,18 @@ "vfl-m3-kletter-zu-kurz-flach": "\n Kein Kletter-Winkel passt (Segment zu kurz) -> Lauf wird komplett flach gebaut, Hoehe wandert in GF1.", "vfl-m3-kletter-flach-gf": "\n Flache Zone: GF-Kompensation (GF1) = %1 mm", "vfl-m3-alert-vf-zu-lang": "VF-Lauf ist %1 m lang (Grenze %2 m) - bitte neu planen (z.B. Zwischen-Motorstation einfuegen).", + "vfl-m2-einheit-grenze": "Diese VF-Einheit ist so nicht baubar.\n\nGemessen: %1 m Laenge, %2 Vario-Kurven.\nErlaubt je Einheit (zwischen Umlenk- und Motorstation): hoechstens %3 m und %4 Vario-Kurven.\n\nBitte den Bogen in Segment %5 als GF-Bogen klassifizieren - dann endet die erste Einheit davor mit einer Motorstation und die zweite beginnt danach mit einer Umlenkstation.", + "vfl-m2-einheit-grenze-unteilbar": "Diese VF-Einheit ist so nicht baubar.\n\nGemessen: %1 m Laenge, %2 Vario-Kurven.\nErlaubt je Einheit (zwischen Umlenk- und Motorstation): hoechstens %3 m und %4 Vario-Kurven.\n\nEs gibt keinen Bogen, an dem sich der Lauf teilen liesse - bitte den Pfad kuerzer fassen oder eine Ecke einplanen, an der die Einheit enden kann.", "vfl-m3-warnung-fueller-eingang-zu-kurz": "\n WARNUNG: Fueller direkt nach der Umlenkstation zu kurz (%1 mm < Minimum %2 mm) - nur Minimalabstand gebaut, Zielposition nicht exakt erreicht.", "vfl-m3-warnung-fueller-ausgang-zu-kurz": "\n WARNUNG: Fueller direkt vor der Motorstation zu kurz (%1 mm < Minimum %2 mm) - nur Minimalabstand gebaut, Zielposition nicht exakt erreicht.", "vfl-m3-lauf-anfang-header": "\n\nDer VF-Lauf ist der Anfang des gewaehlten Pfads. Lauf beginnen mit:", - "vfl-m3-lauf-anfang-opt-offen": "\n 1 - Ohne Umlenkstation (offener Anfang, spaeter fortsetzen/verschmelzen)", + "vfl-m3-lauf-anfang-opt-offen": "\n 3 - Ohne Umlenkstation (offener Anfang, spaeter fortsetzen/verschmelzen)", "vfl-m3-lauf-anfang-opt-umlenk": "\n 2 - Umlenkstation direkt (kein GF1/Separator)", - "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 3 - Umlenkstation mit GF1 + Separator", + "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 1 - Umlenkstation mit GF1 + Separator", + "vfl-m2-anfang-wert-unbekannt": "Lauf-Anfang nicht lesbar: Kette %1 enthaelt den unbekannten Wert %2. Aufbau abgebrochen - eine geratene Anfangsvariante wuerde die Kette falsch neu bauen.", "vfl-m3-lauf-anfang-offen-hinweis": "\n Anfang bleibt bewusst offen - keine Umlenkstation/GF1/Separator gebaut, spaeter fortsetzen oder per Vario_Kette_Merge verbinden.", + "vfl-m2-anfang-offen-gesperrt-zeile": "\n (nicht waehlbar - mit AS-Element braucht der Lauf eine Umlenkstation)", + "vfl-m2-anfang-offen-gesperrt": "Mit AS-Element ist ein offener Anfang nicht moeglich - der Lauf braucht dann eine Umlenkstation.", "vfl-m3-lauf-abschluss-header": "\n\nDer VF-Lauf ist das Ende des gewaehlten Pfads. Lauf beenden mit:", "vfl-m3-lauf-abschluss-opt-offen": "\n 1 - Ohne Motorstation (offenes Ende, spaeter fortsetzen/verschmelzen)", "vfl-m3-lauf-abschluss-opt-motor": "\n 2 - Motorstation setzen (kein ES-Element)", diff --git a/lang/en_GB.json b/lang/en_GB.json index a2f75db..dfb6aae 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -9,6 +9,7 @@ "vfl-as-auto-erkannt": "\n>>> Start point lies on the KS_AUS of an existing %1 chain - no AS element needed.", "vfl-familie-neutral": "GF/VF", "vfl-sep-nach-durch-es-ueberschrieben": "\n>>> Separator AFTER ignored - the ES element already brings its own separator at the chain end (otherwise two separators in a row).", + "vfl-sep-gf-es-automatisch": "(A separator between GF and ES is inserted automatically.)", "vfl-echo-position": "\n>>> Position: X=%1 Y=%2 Z=%3", "vfl-echo-wahl": "\n>>> Selected: %1", "vfl-aus-seite-header": "\n\nOUT element (AS_Element_*) - select side:", @@ -250,13 +251,18 @@ "vfl-m3-kletter-zu-kurz-flach": "\n No climb angle fits (segment too short) -> run is built entirely flat, height is absorbed by GF1.", "vfl-m3-kletter-flach-gf": "\n Flat zone: GF compensation (GF1) = %1 mm", "vfl-m3-alert-vf-zu-lang": "VF run is %1 m long (limit %2 m) - please re-plan (e.g. insert an intermediate motor station).", + "vfl-m2-einheit-grenze": "This VF unit cannot be built like this.\n\nMeasured: %1 m length, %2 vario curves.\nAllowed per unit (between deflection and motor station): at most %3 m and %4 vario curves.\n\nPlease classify the bend in segment %5 as a GF bend - the first unit then ends before it with a motor station and the second one starts after it with a deflection station.", + "vfl-m2-einheit-grenze-unteilbar": "This VF unit cannot be built like this.\n\nMeasured: %1 m length, %2 vario curves.\nAllowed per unit (between deflection and motor station): at most %3 m and %4 vario curves.\n\nThere is no bend at which the run could be split - please shorten the path or plan a corner where the unit can end.", "vfl-m3-warnung-fueller-eingang-zu-kurz": "\n WARNING: filler right after the reversing station is too short (%1 mm < minimum %2 mm) - only the minimum clearance was built, target position not reached exactly.", "vfl-m3-warnung-fueller-ausgang-zu-kurz": "\n WARNING: filler right before the motor station is too short (%1 mm < minimum %2 mm) - only the minimum clearance was built, target position not reached exactly.", "vfl-m3-lauf-anfang-header": "\n\nThe VF run is the start of the selected path. Start the run with:", - "vfl-m3-lauf-anfang-opt-offen": "\n 1 - No reversing station (open start, continue/merge later)", + "vfl-m3-lauf-anfang-opt-offen": "\n 3 - No reversing station (open start, continue/merge later)", "vfl-m3-lauf-anfang-opt-umlenk": "\n 2 - Reversing station directly (no GF1/separator)", - "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 3 - Reversing station with GF1 + separator", + "vfl-m3-lauf-anfang-opt-umlenk-gf1": "\n 1 - Reversing station with GF1 + separator", + "vfl-m2-anfang-wert-unbekannt": "Cannot read run start: chain %1 contains the unknown value %2. Build aborted - guessing a start variant would rebuild the chain incorrectly.", "vfl-m3-lauf-anfang-offen-hinweis": "\n Start deliberately left open - no reversing station/GF1/separator built; continue later or join with Vario_Kette_Merge.", + "vfl-m2-anfang-offen-gesperrt-zeile": "\n (not selectable - with an AS element the run needs a reversing station)", + "vfl-m2-anfang-offen-gesperrt": "With an AS element an open start is not possible - the run then needs a reversing station.", "vfl-m3-lauf-abschluss-header": "\n\nThe VF run is the end of the selected path. End the run with:", "vfl-m3-lauf-abschluss-opt-offen": "\n 1 - No motor station (open end, continue/merge later)", "vfl-m3-lauf-abschluss-opt-motor": "\n 2 - Set motor station (no ES element)", diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 83283bf..48d2266 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -409,7 +409,7 @@ ;; vfl-par-gf-laenge - wie lang wird das GF-Stueck dazwischen? ;; Die Plan-Turn-Werte unten sind die Vorzeichen-Konvention aus ;; vf-element-plan-turn (KS_EIN -> KS_AUS in der XY-Ebene, links positiv). -(defun tu-tests-vfl-parallel ( / _b30 _b60 _b90 _kand _l) +(defun tu-tests-vfl-parallel ( / _b30 _b60 _b90 _kand _l _plan _plan3 _g _p _w) (princ "\n[vf_linienzug Parallel zum Kreisel]") ;; --- Bogenwahl: der Bogen muss den AS-Schwenk GENAU aufheben --- @@ -450,6 +450,28 @@ ;; AS schwenkt gar nicht aus der Achse -> keine Loesung (tu-nil "vfl-par-laenge/kein-versatz" (vfl-par-gf-laenge 2000.0 0.0 0.0 0.0)) + ;; --- Grenzen einer VF-Einheit (Modus 2) --- + ;; Zwischen Umlenk- und Motorstation: hoechstens 2 Vario-Kurven und 25 m. + ;; Plan-Eintraege: ("Linie" hz laenge ...) / ("Bogen" hz chord ...) - nur + ;; Typ und Laenge (3. Feld) zaehlen hier. + (setq _plan (list '("Linie" 0.0 9000.0 "VF") '("Bogen" 0.0 500.0) + '("Linie" 0.0 8000.0 "VF") '("Bogen" 0.0 500.0) + '("Linie" 0.0 6000.0 "VF"))) + ;; 24000 mm, 2 Kurven -> alles im Rahmen + (setq _g (vfl-m2-lauf-grenzen _plan 0 4 25000.0 2)) + (tu-eqf "vfl-m2-grenzen/laenge" 24000.0 (nth 0 _g)) + (tu-eq "vfl-m2-grenzen/kurven" 2 (nth 1 _g)) + (tu-nil "vfl-m2-grenzen/ok" (nth 2 _g)) + ;; Dritte Kurve -> an ihr (Index 5) teilen, obwohl die Laenge noch passt + (setq _plan3 (append _plan (list '("Bogen" 0.0 400.0) '("Linie" 0.0 300.0 "VF")))) + (tu-eq "vfl-m2-grenzen/dritte-kurve" 5 (nth 2 (vfl-m2-lauf-grenzen _plan3 0 6 25000.0 2))) + ;; Zu lang -> am ersten Bogen teilen, nach dem die Grenze reisst (Index 3: + ;; bis dahin sind 9000+500+8000 = 17500 drin, die Grenze liegt bei 15000). + (tu-eq "vfl-m2-grenzen/zu-lang" 3 (nth 2 (vfl-m2-lauf-grenzen _plan 0 4 15000.0 2))) + ;; Eine einzige lange Gerade: zu lang, aber kein Bogen zum Teilen -> -1 + (tu-eq "vfl-m2-grenzen/unteilbar" -1 + (nth 2 (vfl-m2-lauf-grenzen (list '("Linie" 0.0 30000.0 "VF")) 0 0 25000.0 2))) + ;; --- Vorgabewert aus einem Prompt ziehen (Dialog-Feld vorbelegen) --- ;; Die Konsolen-Prompts tragen ihre Vorgabe in Klammern; der Zahlen-Dialog ;; liest sie daraus. Gesucht ist die LETZTE Klammergruppe MIT Zahl - die @@ -470,6 +492,30 @@ (tu-nil "vflw-vorgabe/keine-zahl" (vflw-vorgabe-aus-prompt "Bitte Namen eingeben [frei]: ")) + ;; --- Modus-2-Lauf-Anfang: Journal-Wert normieren --- + ;; Die Antwort wird als selbstbeschreibender Wert gefuehrt, damit ein + ;; Umsortieren/Umnummerieren der Optionen bestehende Ketten nicht + ;; umdeutet. Alte Ketten tragen dort noch die Ziffern der ALTEN Folge - + ;; die Zuordnung dafuer steht als Tabelle in *vfl-m2-anfang-alt* und wird + ;; hier komplett durchlaufen, statt einzelne Faelle abzuschreiben. + (foreach _p *vfl-m2-anfang-alt* + (tu-eq (strcat "vfl-m2-anfang/alt-" (car _p)) + (cdr _p) (vfl-m2-anfang-norm (car _p)))) + ;; Jeder Tabellenwert muss auch ein gueltiger heutiger Wert sein - sonst + ;; laeuft eine Alt-Kette in den Unbekannt-Abbruch. + (foreach _p *vfl-m2-anfang-alt* + (tu-true (strcat "vfl-m2-anfang/alt-ziel-gueltig-" (car _p)) + (and (member (cdr _p) *vfl-m2-anfang-werte*) t))) + ;; Neue Werte gehen unveraendert durch. + (foreach _w *vfl-m2-anfang-werte* + (tu-eq (strcat "vfl-m2-anfang/neu-" _w) _w (vfl-m2-anfang-norm _w))) + ;; Unbekanntes wird NICHT geraten - nil heisst fuer den Aufrufer Abbruch. + (tu-nil "vfl-m2-anfang/unbekannte-ziffer" (vfl-m2-anfang-norm "4")) + (tu-nil "vfl-m2-anfang/leer" (vfl-m2-anfang-norm "")) + (tu-nil "vfl-m2-anfang/tippfehler" (vfl-m2-anfang-norm "mit_gf1")) + (tu-nil "vfl-m2-anfang/kein-string" (vfl-m2-anfang-norm 3)) + (tu-nil "vfl-m2-anfang/nil" (vfl-m2-anfang-norm nil)) + ;; --- Rueckwaerts bauen: Punkt entgegen der Fahrtrichtung zuruecksetzen --- ;; vfl-rueck-punkt ist der Rechenkern des Rueckwaerts-Baus: zu einem ;; BEKANNTEN Endpunkt den Startpunkt eines Bausteins finden. Vorwaerts gilt @@ -2452,41 +2498,6 @@ ) ) -(defun c:TEST_UNIT ( / out-dir report) - (tu-reset) - (princ "\n================================================================") - (princ "\n TEST_UNIT - LISP-Standardfunktionen") - (princ "\n================================================================") - - (foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore" - "tu-tests-etage" "tu-tests-vfl-es" "tu-tests-vfl-parallel" - "tu-tests-vfl-ziellinie" - "tu-tests-export" "tu-tests-hoehen" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-kreisel-bp" - "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" - "tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch" - "tu-tests-werkzeuge" "tu-tests-kf") - (tu-run-block fn)) - - (princ "\n\n----------------------------------------------------------------") - (princ (strcat "\n Gesamt: " (itoa (+ *tu-pass* *tu-fail*)) - " | PASS: " (itoa *tu-pass*) - " | FAIL: " (itoa *tu-fail*))) - (princ (if (= *tu-fail* 0) "\n ERGEBNIS: ALLE TESTS BESTANDEN" "\n ERGEBNIS: TESTS FEHLGESCHLAGEN")) - (princ "\n----------------------------------------------------------------") - - ;; Report-Datei schreiben (fuer run_tests.bat --lisp) - (setq out-dir (strcat (getenv "DXFMAKRO") "/tests/output")) - (vl-mkdir out-dir) - (setq report (strcat out-dir "/unit_results.txt")) - (if (tu-write-report report) - (princ (strcat "\n Report: " report)) - (princ "\n WARNUNG: Report konnte nicht geschrieben werden.") - ) - (princ) -) - -(princ "\ntest_unit.lsp geladen. Befehl: TEST_UNIT") -(princ) ;; ------------------------------------------------------------ ;; TESTS: KettenFoerderer.lsp + ssg_ks_insert.lsp (KS-Orientierung) ;; ------------------------------------------------------------ @@ -2739,3 +2750,38 @@ ) +(defun c:TEST_UNIT ( / out-dir report) + (tu-reset) + (princ "\n================================================================") + (princ "\n TEST_UNIT - LISP-Standardfunktionen") + (princ "\n================================================================") + + (foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore" + "tu-tests-etage" "tu-tests-vfl-es" "tu-tests-vfl-parallel" + "tu-tests-vfl-ziellinie" + "tu-tests-export" "tu-tests-hoehen" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-kreisel-bp" + "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" + "tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch" + "tu-tests-werkzeuge" "tu-tests-kf") + (tu-run-block fn)) + + (princ "\n\n----------------------------------------------------------------") + (princ (strcat "\n Gesamt: " (itoa (+ *tu-pass* *tu-fail*)) + " | PASS: " (itoa *tu-pass*) + " | FAIL: " (itoa *tu-fail*))) + (princ (if (= *tu-fail* 0) "\n ERGEBNIS: ALLE TESTS BESTANDEN" "\n ERGEBNIS: TESTS FEHLGESCHLAGEN")) + (princ "\n----------------------------------------------------------------") + + ;; Report-Datei schreiben (fuer run_tests.bat --lisp) + (setq out-dir (strcat (getenv "DXFMAKRO") "/tests/output")) + (vl-mkdir out-dir) + (setq report (strcat out-dir "/unit_results.txt")) + (if (tu-write-report report) + (princ (strcat "\n Report: " report)) + (princ "\n WARNUNG: Report konnte nicht geschrieben werden.") + ) + (princ) +) + +(princ "\ntest_unit.lsp geladen. Befehl: TEST_UNIT") +(princ)