diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index 4846534..41acffb 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -1995,6 +1995,59 @@ (setq s (substr s (+ pos 1 (strlen sep))))) (reverse (cons s out))) +;; --- SEGS: Segment-Essenz einer Modus-3-Kette (selbst-enthalten) --- +;; Ein SEGS-Wert ist die AUS DEN PFAD-OBJEKTEN ABGELEITETE Zahlenliste, die der +;; Modus-3-Bau ohnehin allein verwendet (gf-analysiere-kette + Start/Endpunkte +;; aus gf-sortiere-objekte). Sie ersetzt im Journal die frueheren OBJS-Handles: +;; dadurch ist die Kette im XDATA vollstaendig beschrieben und beim Doppelklick- +;; Replay unabhaengig davon, ob die urspruenglichen LINE/ARC noch existieren +;; (robuster als der OBJS-Weg von Modus 2). Siehe doc/variofoerderer.md. +;; +;; Ein Segment ist eine 9er-Liste, die die "Bau-Essenz" und die fuer die +;; AS-Trimmung/das Soll-Ende noetigen absoluten Punkte zusammenfuehrt: +;; (typ hz laenge bwinkel bseite spx spy epx epy) +;; typ "Linie"/"Bogen" (delimiter-frei, daher unquotiert serialisiert) +;; hz Eintritts-Horizontalwinkel (Grad) +;; laenge 2D-Laenge / Sehnenmass (mm) +;; bwinkel Bogenwinkel (0 bei Linie) +;; bseite "links"/"rechts" +;; spx spy Startpunkt (XY, mm) des Segments \ aus gf-sortiere-objekte, +;; epx epy Endpunkt (XY, mm) des Segments / fuer Trimmung + Soll-Ende +;; +;; Serialisierung: Segmente per ";" getrennt, Felder je Segment per "|". Beide +;; Trenner sind kollisionsfrei - die einzigen String-Felder (typ/bseite) sind +;; feste Schluesselwoerter ohne ";" oder "|", alle uebrigen Felder sind Zahlen +;; (rtos-Mode-2, wie PT/REAL). Ein leeres SEGS ("") ist eine leere Liste. +(defun vfl-seg->string (seg) + (vfl-strjoin + (list (nth 0 seg) ; typ (String) + (rtos (float (nth 1 seg)) 2 6) ; hz + (rtos (float (nth 2 seg)) 2 6) ; laenge + (rtos (float (nth 3 seg)) 2 6) ; bwinkel + (nth 4 seg) ; bseite (String) + (rtos (float (nth 5 seg)) 2 6) ; spx + (rtos (float (nth 6 seg)) 2 6) ; spy + (rtos (float (nth 7 seg)) 2 6) ; epx + (rtos (float (nth 8 seg)) 2 6)) ; epy + "|")) +(defun vfl-string->seg (s / f) + (setq f (vfl-strsplit s "|")) + (list (nth 0 f) ; typ + (atof (nth 1 f)) ; hz + (atof (nth 2 f)) ; laenge + (fix (atof (nth 3 f))) ; bwinkel (Ganzzahl, wie gf-analysiere-kette) + (nth 4 f) ; bseite + (atof (nth 5 f)) ; spx + (atof (nth 6 f)) ; spy + (atof (nth 7 f)) ; epx + (atof (nth 8 f)))) ; epy +(defun vfl-segs->string (segs) + (vfl-strjoin (mapcar 'vfl-seg->string segs) ";")) +(defun vfl-string->segs (s) + (if (= (strlen s) 0) + '() + (mapcar 'vfl-string->seg (vfl-strsplit s ";")))) + ;; --- Serialisierung (tagged, selbstbeschreibend) --- ;; Zeilenformat "kind:payload", Eintraege per "\n" verkettet. getstring/getreal ;; liefern nie ein Newline, daher ist "\n" ein sicherer Record-Trenner. @@ -2002,6 +2055,8 @@ ;; Entity-Handles (Hex-Strings, kollidieren nie mit dem Komma-Trenner). ;; Handles bleiben - anders als Enames - ueber Sitzungen/Undo hinweg stabil ;; und sind damit die richtige Persistenzform fuer ssget-Ergebnisse. +;; "SEGS" (Segment-Essenz, Modus 3): Payload = ";"-getrennte Segmente, Felder +;; per "|" (siehe vfl-segs->string oben). (defun vfl-entry->string (e / k v) (setq k (car e) v (cdr e)) (cond @@ -2013,6 +2068,7 @@ ((= k "STR") (strcat "STR:" v)) ((= k "STEP") (strcat "STEP:" v)) ((= k "OBJS") (strcat "OBJS:" (vfl-strjoin v ","))) + ((= k "SEGS") (strcat "SEGS:" (vfl-segs->string v))) (t "NIL:"))) (defun vfl-string->entry (ln / p kind pay c) (setq p (vl-string-search ":" ln)) @@ -2030,6 +2086,7 @@ ((= kind "STR") (cons "STR" pay)) ((= kind "STEP") (cons "STEP" pay)) ((= kind "OBJS") (cons "OBJS" (if (> (strlen pay) 0) (vfl-strsplit pay ",") nil))) + ((= kind "SEGS") (cons "SEGS" (vfl-string->segs pay))) (t (cons "NIL" nil)))))) ;; forward-journal ist bereits in Bau-Reihenfolge (nicht die umgekehrte ;; *vfl-journal*-Rohform) - gemeinsame Serialisierung fuer Ketten- UND @@ -2068,6 +2125,21 @@ ((= k "OBJS") (strcat "[" (vfl-strjoin (mapcar (function (lambda (h) (strcat "\"" (vfl-json-escape h) "\""))) v) ",") "]")) + ((= k "SEGS") (strcat "[" + (vfl-strjoin + (mapcar + (function (lambda (seg) + (strcat "[\"" (vfl-json-escape (nth 0 seg)) "\"," + (vfl-json-num (float (nth 1 seg))) "," + (vfl-json-num (float (nth 2 seg))) "," + (vfl-json-num (float (nth 3 seg))) ",\"" + (vfl-json-escape (nth 4 seg)) "\"," + (vfl-json-num (float (nth 5 seg))) "," + (vfl-json-num (float (nth 6 seg))) "," + (vfl-json-num (float (nth 7 seg))) "," + (vfl-json-num (float (nth 8 seg))) "]"))) + v) ",") + "]")) (t "null")) "}")) ;; forward-journal (Bau-Reihenfolge) als kompakter JSON-Array-String. @@ -2115,7 +2187,7 @@ ;; Rueckgabe: (marker . journal) oder nil (kein/unbekanntes Journal). (defun vfl-journal-xdata-lesen (ent / xd) (setq xd (vfs-xdata-lesen ent)) - (if (and xd (member (car xd) '("linienzug" "linienzug2"))) + (if (and xd (member (car xd) '("linienzug" "linienzug2" "linienzug3"))) (cons (car xd) (vfl-string->journal (apply 'strcat (cdr xd)))) nil)) @@ -8184,22 +8256,24 @@ ;; und der Override sicher zurueckgesetzt wird. Rueckgabe: T bei Erfolg, sonst nil. ;; Bekannte Grenze (nur Modus 2): das Replay von vfl-in-selection braucht die ;; Original-Pfadobjekte; wurden sie geloescht, schlaegt der Konverter fuer diesen -;; Block sauber fehl (sichtbar am nicht erhoehten Batch-Zaehler). +;; Block sauber fehl (sichtbar am nicht erhoehten Batch-Zaehler). Modus 3 +;; ("linienzug3") hat diese Grenze NICHT: es replayt die Segment-Essenz (SEGS) +;; aus dem Journal, nicht die Objekt-Handles - unabhaengig von den Original-LINE/ARC. (defun vfl-konvertiere-ent (ent ziel-dim / bname xd marker journal ok) (setq bname (cdr (assoc 2 (entget ent)))) (setq xd (vfl-journal-xdata-lesen ent)) ; (marker . journal) oder nil (setq marker (car xd) journal (cdr xd)) (if (and (wcmatch bname "VF_*") - (member marker '("linienzug" "linienzug2")) + (member marker '("linienzug" "linienzug2" "linienzug3")) journal) (progn (setq *ssg-ils-dim* ziel-dim) ; Zieldim erzwingen (entdel ent) ; alten Block weg (vfl-journal-replay-start journal) ; volle Queue, KEIN Truncate (setq ok (vl-catch-all-apply - (if (= marker "linienzug2") - 'vf-linienzug-modus2 - 'vf-linienzug-modus))) + (cond ((= marker "linienzug2") 'vf-linienzug-modus2) + ((= marker "linienzug3") 'vf-linienzug-modus3) + (t 'vf-linienzug-modus)))) (setq *ssg-ils-dim* nil) ; Override zuruecksetzen ;; Fehlgeschlagene Konvertierung NICHT stumm verschlucken: der alte Block ;; ist per entdel schon weg, die Teil-Geometrie liegt frei in der @@ -8225,8 +8299,10 @@ ;; Doppelklick kein *ssg-ils-dim*-Override aktiv ist). Wird VOR dem entdel ;; gelesen; der Override greift beim anschliessenden Replay/Weiterbau. (setq dim (ssg-dim-xdata-lesen ent)) - (if (= marker "linienzug2") - (vfl-edit-ent2 ent journal) + (cond + ((= marker "linienzug2") (vfl-edit-ent2 ent journal)) + ((= marker "linienzug3") (vfl-edit-ent3 ent journal)) + (t (progn (setq glieder (vfl-journal-glieder journal)) (setq n (length glieder)) @@ -8282,15 +8358,20 @@ ;; Handler selbst (dort ebenfalls (setq *ssg-ils-dim* nil)), denn ;; die naechste Zeile wird dann nicht mehr erreicht. (vf-linienzug-modus) - (setq *ssg-ils-dim* nil))))) + (setq *ssg-ils-dim* nil)))))) (princ)) -;; Modus-2-Block per Doppelklick zuruecksetzen: voller Reset - die komplette -;; gespeicherte Eingabe (inkl. der gewaehlten Pfad-Objekte, siehe -;; vfl-in-selection) wird 1:1 stumm abgespielt, identischer Neuaufbau. Kein +;; Modus-2/3-Block per Doppelklick zuruecksetzen: voller Reset - die komplette +;; gespeicherte Eingabe wird 1:1 stumm abgespielt, identischer Neuaufbau. Kein ;; Sektions-Dialog; der Nutzer kann den neu entstandenen Block danach ganz ;; normal weiterbearbeiten oder loeschen. -(defun vfl-edit-ent2 (ent journal / dim) +;; +;; bau-fn = die Modus-Bau-Funktion, die das Journal abspielt: 'vf-linienzug-modus2 +;; fuer Marker "linienzug2" (Pfad-Objekte per OBJS-Handles) bzw. +;; '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) ;; Dimension des Bestandsblocks VOR dem entdel merken und beim Neubau als ;; Override erzwingen (dimensionstreuer Doppelklick-Neubau, sonst globale ;; DXFM_DIM). Danach Override zuruecksetzen. @@ -8301,12 +8382,17 @@ (vfl-journal-replay-start journal) ;; 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 von - ;; vf-linienzug-modus2 die Teil-Geometrie wickeln. Den Dim-Override raeumt im - ;; Abbruchfall der Handler ab. - (vf-linienzug-modus2) + ;; ist per entdel schon weg, bei einem Abbruch muss der *error*-Handler der + ;; Modus-Funktion die Teil-Geometrie wickeln. Den Dim-Override raeumt im + ;; Abbruchfall der Handler ab. bau-fn ist ein Funktions-SYMBOL (quoted beim + ;; Aufruf) - ueber apply aufrufen, nicht als (bau-fn) (das riefe die Funktion + ;; namens "bau-fn" auf, nicht den durchgereichten Wert). + (apply bau-fn nil) (setq *ssg-ils-dim* nil) (princ)) +;; Rueckwaertskompatibler Wrapper (Modus 2). vfl-edit-ent3 fuer Modus 3. +(defun vfl-edit-ent2 (ent journal) (vfl-edit-ent-voll ent journal 'vf-linienzug-modus2)) +(defun vfl-edit-ent3 (ent journal) (vfl-edit-ent-voll ent journal 'vf-linienzug-modus3)) ;; ============================================================ ;; RECOVERY: Kette aus der Sektions-XDATA der Zeichnung neu aufbauen @@ -8702,14 +8788,80 @@ (foreach x liste (setq d (abs (- x w))) (if (< d bd) (setq bd d best x))) best) +;; --- Segment-Essenz einer selektierten Pfad-Kette (Modus 3) -------------- +;; Fuehrt die 5-Feld-Bau-Segmente aus gf-analysiere-kette mit den absoluten +;; Start-/Endpunkten aus gf-sortiere-objekte (kette: (ent sp ep umk)) zu der +;; selbstbeschreibenden 9-Feld-Form zusammen, die das SEGS-Journal traegt: +;; (typ hz laenge bwinkel bseite spx spy epx epy) +;; Beide Listen sind gleich lang und in derselben Reihenfolge (segmente wurde +;; genau aus kette abgeleitet). spx/spy/epx/epy sind die planaren XY der +;; Segment-Enden - sie ersetzen den spaeteren direkten kette-Zugriff (AS- +;; Trimmung am ersten Segment, Soll-Ende-Report), sodass der Bau OHNE die +;; Original-Entities auskommt und beim Replay aus dem Journal gespeist wird. +(defun vfl-kette->segmente9 (kette segmente / out i seg item sp ep) + (setq out '() i 0) + (foreach seg segmente + (setq item (nth i kette) sp (cadr item) ep (caddr item)) + (setq out (cons (list (nth 0 seg) (nth 1 seg) (nth 2 seg) (nth 3 seg) (nth 4 seg) + (car sp) (cadr sp) (car ep) (cadr ep)) + out)) + (setq i (1+ i))) + (reverse out)) + +;; Objektauswahl + Analyse (Live) ODER Segment-Essenz aus dem Journal (Replay). +;; Gegenstueck zu vfl-in-selection, aber persistiert NICHT die Entity-Handles, +;; sondern die abgeleitete 9-Feld-Segmentliste (Journal-Art "SEGS"). Damit ist +;; der Modus-3-Block im XDATA vollstaendig beschrieben und der Doppelklick-Neubau +;; unabhaengig davon, ob die urspruenglichen LINE/ARC noch existieren. +;; Rueckgabe: die 9-Feld-Segmentliste oder nil (Abbruch/leer). filter/startpunkt +;; wie bei der Live-Auswahl; ecke-tol wird nur im Live-Zweig fuer die +;; Eck-Vorpruefung gebraucht (Replay-Daten sind bereits geprueft eingefroren). +(defun vfl-in-segmente (filter startpunkt ecke-tol / popped ss k obj-liste kette segmente ecke-bad segs9) + (setq popped (if *vfl-replay-queue* (vfl-replay-pop) nil)) + (if popped + ;; --- Replay: fertige Segment-Essenz direkt aus dem Journal --- + (progn + (setq segs9 (car popped)) + (if (null segs9) + (if (vfl-headless-p) (vfl-headless-abbruch "SEGS-leer"))) + segs9) + ;; --- Live: Objekte waehlen, sortieren, analysieren, dann journalisieren --- + (progn + (if (vfl-headless-p) (vfl-headless-abbruch "SEGS")) + (setq ss (ssget filter)) + (if (null ss) + (progn (vfl-journal-record "NIL" nil) nil) + (progn + ;; Auswahlsatz -> VLA-Objektliste (wie im frueheren vfl-in-selection). + (setq obj-liste '() k 0) + (while (< k (sslength ss)) + (setq obj-liste (cons (vlax-ename->vla-object (ssname ss k)) obj-liste)) + (setq k (1+ k))) + (setq obj-liste (reverse obj-liste)) + (setq kette (gf-sortiere-objekte obj-liste startpunkt)) + (if (null kette) + (progn (princ (ssg-text "vfl-m3-fehler-nicht-sortierbar")) + (vfl-journal-record "NIL" nil) nil) + (progn + (setq segmente (gf-analysiere-kette kette)) + (setq ecke-bad (vfl2-pruefe-eckwinkel kette ecke-tol)) + (if ecke-bad + (progn + (vfl-meldung (ssg-textf "vfl-vwnb-alert-eckwinkel" (list (rtos ecke-bad 2 1)))) + (vfl-journal-record "NIL" nil) nil) + (progn + (setq segs9 (vfl-kette->segmente9 kette segmente)) + (vfl-journal-record "SEGS" segs9) + segs9))))))))) + (defun vf-linienzug-modus3 ( / ss obj-liste startpunkt start-hoehe as-seite - es-seite kette segmente n i seg seg-ent typ hz laenge + es-seite kette segmente segmente9 n i seg seg-ent typ hz laenge bwinkel bseite frame antwort winkel deltaL anzahl-gf anzahl-vf vfl-nummer lastEnt hoehe-bis ecke-bad first-line last-line letzt-winkel aus ein soll-ende ist-ende run-typ richtn best-w kvariante L_VF ent-fp rad3 just-open - dbg-an old-error) + dbg-an old-error dbg-old-error vfl-ins) (princ "\n\n=========================================") (princ (ssg-text "vfl-vwnb-titel")) (princ "\n=========================================") @@ -8721,34 +8873,40 @@ ;; automatisch ueber vfl-journal-record mitgeloggt (siehe dort) - hier nur ;; noch Session-Start/-Ende + Zwischenergebnisse, die NICHT direkt aus einer ;; Nutzereingabe stammen (Analyse-Ergebnis, Segment-Header, Bau-Ergebnis). - ;; *vfl-journal*/*vflw-pending* werden dabei nebenbei mitbefuellt, aber von - ;; Modus 3 nirgends gelesen/persistiert (kein Replay/XDATA wie bei Modus 1/2 - ;; - das ist hier bewusst NICHT mit umgebaut, nur das Logging). - ;; Der *error*-Handler schliesst die Datei bei Abbruch sauber. + ;; *vfl-journal* wird - wie bei Modus 1/2 - als XDATA (Marker "linienzug3") + ;; persistiert und ist damit per Doppelklick editierbar (vfl-edit-ent3, voller + ;; Reset-Replay). Statt der Objekt-Handles traegt es die Segment-ESSENZ (SEGS, + ;; siehe vfl-in-segmente): der Block ist selbst-enthalten und der Neubau + ;; unabhaengig von den urspruenglichen LINE/ARC. + ;; FRUEHER *error*-Handler: schliesst nur die dbg-Datei sauber, falls waehrend + ;; der Pfad-/Praeambel-Phase (noch keine Geometrie) abgebrochen wird. Der + ;; eigentliche Abbruch-WICKLER (Teil-Geometrie zu einem VF_n-Block sichern) + ;; wird erst nach dem Setup scharf geschaltet (siehe unten), sobald + ;; vfl-nummer/lastEnt feststehen - analog Modus 2. (setq dbg-an (dbg-schalter-open "vfl-modus3" "vfl_modus3.dbg" "DXFM_LOG")) (if dbg-an (progn (dbgf "vf-linienzug-modus3") (dbgmsg "=== SESSION Modus 3 (Vorwaerts-Nachbau) START ===") (dbgflush))) - (setq old-error *error*) + (setq dbg-old-error *error*) (setq *error* (function (lambda (msg) - (setq *error* old-error) + (setq *error* dbg-old-error) (if dbg-an (progn (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) (dbgreturn nil) (dbgclose))) - (if old-error (old-error msg) (princ))))) + (if dbg-old-error (dbg-old-error msg) (princ))))) (vfl-gf-abhaengigkeit-sicherstellen "vfl-m3-alert-gf-modul") - ;; --- 1. Pfad-Objekte waehlen (LINE/ARC) --- - (princ (ssg-text "vfl-m3-pfad-waehlen")) - (setq ss (vfl-in-selection '((0 . "LINE,ARC")))) - (if (null ss) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit))) - (setq obj-liste (mapcar 'vlax-ename->vla-object ss)) + ;; --- 1./3. Pfad-Objekte + Segment-Essenz: siehe Schritt 3 unten. --- + ;; Auswahl UND Ableitung laufen zusammen in vfl-in-segmente, weil die + ;; Sortierung den Startpunkt braucht (Schritt 2) - die Objektauswahl steht + ;; daher NACH der Startpunkt-/AS-Praeambel, nicht mehr davor. Journalisiert + ;; wird nur noch die abgeleitete Segment-Essenz (SEGS), nicht die Handles. ;; --- 2. Startpunkt (hoehere Seite) + Hoehe + AS-Seite --- ;; Wizard: Startpunkt+Hoehe als EIN Gruppen-Dialog (analog Modus 2). Die @@ -8785,24 +8943,25 @@ (setq as-seite (if (= (vfl-in-string (ssg-text "prompt-wahl-1-2")) "2") "rechts" "links")) (vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante - ;; --- 3. Sortieren + Analysieren --- - (setq kette (gf-sortiere-objekte obj-liste startpunkt)) - (if (null kette) - (progn (princ (ssg-text "vfl-m3-fehler-nicht-sortierbar")) (exit))) - (setq segmente (gf-analysiere-kette kette)) + ;; --- 3. Pfad waehlen + Sortieren + Analysieren + Eck-Vorpruefung --- + ;; vfl-in-segmente kapselt Live (ssget -> gf-sortiere-objekte -> + ;; gf-analysiere-kette -> Eck-Vorpruefung -> 9-Feld-Essenz) UND Replay + ;; (fertige Essenz aus dem SEGS-Journal). Persistiert die Segment-Essenz + ;; statt der Objekt-Handles - damit ist der Block selbst-enthalten und der + ;; Doppelklick-Neubau (vfl-edit-ent3) braucht die Original-LINE/ARC nicht. + (princ (ssg-text "vfl-m3-pfad-waehlen")) + (setq segmente9 (vfl-in-segmente '((0 . "LINE,ARC")) startpunkt *vfk-eckwinkel-toleranz*)) + (if (null segmente9) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit))) + ;; segmente = 5-Feld-Bausicht (typ hz laenge bwinkel bseite), abgeleitet aus + ;; der 9-Feld-Essenz - der bestehende Bau unten arbeitet unveraendert damit. + (setq segmente (mapcar (function (lambda (s) (list (nth 0 s) (nth 1 s) (nth 2 s) (nth 3 s) (nth 4 s)))) + segmente9)) (if dbg-an (progn (dbgmsg (strcat "ANALYSE: " (itoa (length segmente)) " Segment(e) erkannt")) (dbg 'segmente) (dbgflush))) - ;; --- 4. Eck-Winkel-Vorpruefung (vor dem Bau) --- - (setq ecke-bad (vfl2-pruefe-eckwinkel kette *vfk-eckwinkel-toleranz*)) - (if ecke-bad - (progn - (vfl-meldung (ssg-textf "vfl-vwnb-alert-eckwinkel" (list (rtos ecke-bad 2 1)))) - (exit))) - ;; --- 5. Setup --- (setq aus (if aus-dx aus-dx *vfk-as-es-fallback-dx*) ein (if ein-dx ein-dx *vfk-as-es-fallback-dx*)) (setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) @@ -8812,7 +8971,29 @@ (setq vfl-nummer (vf-next-number)) (setq lastEnt (vf-lastent-ohne-attribute)) (vfl-acc-reset) - (setq anzahl-gf 0 anzahl-vf 0 frame nil n (length segmente)) + (setq anzahl-gf 0 anzahl-vf 0 frame nil) + ;; --- Abbruch-Sicherung ab hier scharf (analog Modus 1/2) --- + ;; Ab Schritt 6 kann jederzeit Geometrie entstehen. Der Wickler sichert die + ;; bis zum Abbruch gebaute Teil-Geometrie als VF_n-Block mit dem vollen + ;; Journal (Marker "linienzug3") - sie bleibt stehen und ist per Doppelklick + ;; fortsetz-/editierbar. Loest den fruehen dbg-Hook ab (Ruecksprungziel bleibt + ;; dbg-old-error) und uebernimmt das dbgclose. *ssg-ils-dim* zuruecksetzen: + ;; Begruendung siehe gleichlautende Stelle in vf-linienzug-modus. + (setq old-error dbg-old-error) + (setq *error* + (function (lambda (msg) + (setq *error* old-error) + (setq *ssg-ils-dim* nil) + (vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf + startpunkt frame as-seite es-seite "linienzug3") + (if dbg-an + (progn + (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) + (dbgreturn nil) + (dbgclose))) + (if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end)) + (princ)))) + (setq n (length segmente)) ;; erste/letzte "Linie" fuer die Trimmung bestimmen (setq first-line -1 last-line -1 i 0) (while (< i n) @@ -8831,13 +9012,19 @@ (setq i 0) (while (< i n) (setq seg (nth i segmente) typ (car seg) hz (cadr seg)) - (setq seg-ent (car (nth i kette))) ; echte LINE/ARC-Entity dieses Segments + ;; seg-ent: KEINE Live-Entity mehr - Modus 3 baut jetzt aus der Segment- + ;; Essenz (SEGS), nicht aus durchgereichten LINE/ARC. Damit entfaellt das + ;; kosmetische Pfad-Segment-Highlight (vfl-seg-highlight ist nil-sicher). + ;; Der Wegfall ist bewusst: die Essenz macht den Bau/Replay unabhaengig von + ;; den Original-Objekten, ein Highlight haette beim Doppelklick-Neubau ohnehin + ;; keine Entity mehr zum Hervorheben. + (setq seg-ent nil) ;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die ;; unveraenderten vfl-in-*-Aufrufe unten konsumieren die Antworten aus - ;; *vflw-pending*). Segment vorher in der Zeichnung hervorheben. Bei einem - ;; Bogen ist Vario-Kurve nur im offenen VF-Lauf erlaubt - deshalb die - ;; Variante-Antwort nur mitschicken, wenn run-typ="VF" (sonst konsumiert - ;; der Aufrufer sie nicht und sie wuerde ins naechste Segment lecken). + ;; *vflw-pending*). Bei einem Bogen ist Vario-Kurve nur im offenen VF-Lauf + ;; erlaubt - deshalb die Variante-Antwort nur mitschicken, wenn run-typ="VF" + ;; (sonst konsumiert der Aufrufer sie nicht und sie wuerde ins naechste + ;; Segment lecken). (if (vfl-wizard-aktiv) (progn (vfl-seg-highlight seg-ent T) @@ -8950,12 +9137,21 @@ (setq frame (vfl-insert-es-element "GF" frame (car (frame->hz-winkel frame)) (if letzt-winkel letzt-winkel *vfk-gefaelle-winkel*) (caddr (car frame)) es-seite))) - ;; --- 8. Block + Ist-Ziel-Report --- + ;; --- 8. Block + Journal-XDATA + Ist-Ziel-Report --- (setq hoehe-bis (caddr (car frame))) - (vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf (caddr startpunkt) hoehe-bis - (vfl-planar-dist startpunkt (car frame)) as-seite es-seite startpunkt lastEnt) + (setq vfl-ins + (vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf (caddr startpunkt) hoehe-bis + (vfl-planar-dist startpunkt (car frame)) as-seite es-seite startpunkt lastEnt)) + ;; Eingabe-Journal am fertigen Block persistieren (Marker "linienzug3") -> + ;; spaeter per Doppelklick editierbar (vfl-edit-ent3: voller Reset-Replay wie + ;; Modus 2, aus der Segment-Essenz statt aus Objekt-Handles). + (if vfl-ins (vfl-journal-xdata-schreiben vfl-ins "linienzug3")) - (setq soll-ende (caddr (last kette))) ; Endpunkt des letzten gezeichneten Segments + ;; Endpunkt des letzten gezeichneten Segments - aus der Segment-Essenz + ;; (epx/epy des letzten Segments), nicht mehr aus den Original-Entities. + ;; Damit stimmt der Soll-Ende-Report auch beim Doppelklick-Neubau, wenn die + ;; urspruenglichen LINE/ARC laengst weg sind. + (setq soll-ende (list (nth 7 (last segmente9)) (nth 8 (last segmente9)))) (setq ist-ende (car frame)) ; ES-KS_AUS (princ "\n\n=========================================") (princ (ssg-text "vfl-vwnb-eingefuegt")) diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 8ca036c..1e8b8d8 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -855,6 +855,56 @@ ) +;; ------------------------------------------------------------ +;; TESTS: vf_linienzug.lsp - SEGS-Journal-Serialisierung (Modus 3) +;; ------------------------------------------------------------ +;; Roundtrip der Segment-Essenz: die Zahlenliste, die Modus 3 aus den Pfad- +;; Objekten ableitet, muss verlustfrei durch String<->Liste und durch die +;; Journal-Eintrags-Serialisierung laufen - das ist die Grundlage dafuer, dass +;; ein Modus-3-Block per Doppelklick 1:1 aus dem XDATA-Journal nachgebaut wird. +(defun tu-tests-vfl-segs ( / _seg _segs _s _e) + (princ "\n[vf_linienzug/SEGS]") + (if (null (atoms-family 1 '("VFL-SEGS->STRING"))) + (tu-record nil "vfl-segs/modul" "vf_linienzug.lsp nicht geladen (VFL-SEGS->STRING fehlt)") + (progn + ;; Einzelsegment (Bogen, alle Felder gesetzt) Roundtrip + (setq _seg '("Bogen" 102.5 707.106781 90 "rechts" 1000.0 2000.0 1500.5 2500.25)) + (tu-eqf "vfl-seg/roundtrip-bogen" + _seg (vfl-string->seg (vfl-seg->string _seg))) + + ;; Linie (bwinkel 0, bseite default) Roundtrip + (setq _seg '("Linie" 30.0 4200.0 0 "links" 0.0 0.0 3637.31 2100.0)) + (tu-eqf "vfl-seg/roundtrip-linie" + _seg (vfl-string->seg (vfl-seg->string _seg))) + + ;; Ganze Segmentliste (Linie + Bogen + Linie) Roundtrip + (setq _segs (list '("Linie" 0.0 3000.0 0 "links" 0.0 0.0 3000.0 0.0) + '("Bogen" 0.0 707.106781 90 "rechts" 3000.0 0.0 3500.0 -500.0) + '("Linie" 270.0 2500.0 0 "links" 3500.0 -500.0 3500.0 -3000.0))) + (tu-eqf "vfl-segs/roundtrip-liste" + _segs (vfl-string->segs (vfl-segs->string _segs))) + + ;; Leere Liste -> "" -> leere Liste + (tu-eq "vfl-segs/leer-string" "" (vfl-segs->string '())) + (tu-nil "vfl-segs/leer-parse" (vfl-string->segs "")) + + ;; Einbettung als Journal-Eintrag: (SEGS . liste) durch + ;; vfl-entry->string / vfl-string->entry + (setq _e (cons "SEGS" _segs)) + (setq _s (vfl-entry->string _e)) + (tu-true "vfl-segs/entry-praefix" (= "SEGS:" (substr _s 1 5))) + (tu-eqf "vfl-segs/entry-roundtrip" + _e (vfl-string->entry _s)) + + ;; Delimiter-Sicherheit: die serialisierte Form enthaelt genau die + ;; erwarteten Trenner und KEIN Newline (sonst zerrisse der Record-Trenner) + (tu-nil "vfl-segs/kein-newline" + (vl-string-search "\n" (vfl-segs->string _segs))) + ) + ) +) + + ;; ------------------------------------------------------------ ;; BEFEHL: TEST_UNIT ;; ------------------------------------------------------------ @@ -877,7 +927,7 @@ (foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore" "tu-tests-export" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" - "tu-tests-sepscan") + "tu-tests-sepscan" "tu-tests-vfl-segs") (tu-run-block fn)) (princ "\n\n----------------------------------------------------------------") diff --git a/tests/test_vf_headless_statisch.py b/tests/test_vf_headless_statisch.py index bec30ba..29ba88a 100644 --- a/tests/test_vf_headless_statisch.py +++ b/tests/test_vf_headless_statisch.py @@ -173,6 +173,7 @@ EINGABE_ERLAUBT = { "vfl-in-int": {"getint": 1}, "vfl-in-real": {"getreal": 1}, "vfl-in-selection": {"ssget": 1}, + "vfl-in-segmente": {"ssget": 1}, # Modus-3-Wrapper: Live-Auswahl -> Segment-Essenz (SEGS), Replay aus Journal "vfl-winkel-live": {"getint": 1}, # livefn fuer vfl-in-value # --- Wizard-Schicht (DCL); bei ssg-gui-aus greift sie nicht --- "vflw-zahl": {"getreal": 1}, # Fallback ohne Dialog