Files
dxfmakros/Lisp/DrawPlanquadrate.lsp

363 lines
15 KiB
Common Lisp

;; ============================================================
;; DrawPlanquadrate.lsp - Planquadrat-Raster in die Zeichnung zeichnen
;;
;; Zeichnet auf Hoehe Z=0 senkrechte Linien (konstantes X) und waagerechte
;; Linien (konstantes Y) im Abstand der aktiven Planquadrat-Konfiguration aus
;; cfg/export.cfg ([Planquadrate] section=... -> [Planquadrate_AN] o.ae.,
;; siehe export:cfg/export:load-cfg in export.lsp) - dieselbe Konfiguration,
;; nach der export_planquadrat.py (lib/) das Planquadrat eines Bauteils fuer
;; den CSV-Export berechnet. Am linken Rand steht je Zeile, am unteren Rand
;; je Spalte die zugehoerige Zaehlung (alphabetisch oder numerisch, je nach
;; x_description/y_description).
;;
;; Bereich: aus den EXPORTIERTEN Bloecken (dieselbe Blockmenge, die
;; csv:collect-export-blocks in export.lsp fuer EXPORTCSV sammelt - siehe
;; plq-export-bbox), NICHT die Zeichnungs-Extents (EXTMIN/EXTMAX). Ursprung
;; (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box
;; jedes Blocks (csv:get-bbox, dieselbe wie bbox_columns()/
;; planquadrat_column()/_bbox_min() in lib/export_csv.py bzw.
;; lib/export_planquadrat.py), NICHT aus dem reinen Einfuegepunkt: bei einem
;; gedrehten oder exzentrisch eingefuegten Block (z.B. ein um 90 Grad
;; gedrehter Kreisel, dessen Insertpunkt an einem Rand statt der Mitte liegt)
;; kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen - Ursprung
;; und Zellzuordnung MUESSEN auf derselben Referenz beruhen, sonst driften sie
;; auseinander (realer Bug: ein Kreisel landete dadurch in der CSV-Spalte
;; "F/3-G/13" statt der laut Zeichnung korrekten Spalte A). Reine
;; Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte, das
;; Planquadrat-Raster selbst) zaehlt zu EXTMIN/EXTMAX, gehoert aber zu keinem
;; Export-Block - das BricsCAD-Raster muss daher auf derselben Blockmenge wie
;; der CSV-Export rechnen, sonst landet ein Bauteil im CSV in einer anderen
;; Spalte/Zeile als im Raster in der Zeichnung.
;; Der Bereich wird IMMER auf ein ganzzahliges Vielfaches der Schrittweite ab
;; dem Ursprung aufgerundet - das Raster bildet also stets ein GESCHLOSSENES
;; Rechteck: jede senkrechte Linie endet exakt auf der letzten waagerechten
;; und umgekehrt (kein "Ueberstand" einzelner Linien ueber die letzte volle
;; Zeile/Spalte hinaus).
;; x_origin/y_origin aus [Planquadrate] legen die Koordinate von Index 1 fest
;; (wie im CSV-Export) und ueberschreiben den automatisch ermittelten
;; Ursprung. Fallback auf EXTMIN/EXTMAX, falls keine exportfaehigen Bloecke in
;; der Zeichnung stehen.
;;
;; Das fertige Raster (Linien + Beschriftungstexte) wird zu EINEM Block
;; (*plq-blockname*) zusammengefasst und bei (0,0,0) eingefuegt - dadurch
;; laesst sich das gesamte Planquadrat mit einem Klick auswaehlen/loeschen.
;; Bei jedem Aufruf wird ein evtl. vorhandener Planquadrat-Block zuerst
;; entfernt (DELETE_PLANQUADRATE-Logik) und danach neu gezeichnet -
;; wiederholte Aufrufe erzeugen also keine Dubletten.
;;
;; Befehle:
;; DRAW_PLANQUADRATE - Raster zeichnen/neu aufbauen
;; DELETE_PLANQUADRATE - Raster-Block entfernen (inkl. Blockdefinition)
;; PLANQUADRATE_TOGGLE - "Planquadrate an/aus": zeichnet, falls noch kein
;; Block in der Zeichnung ist, sonst loescht es ihn
;; (Menue: Konfiguration)
;; ============================================================
(vl-load-com)
;; Name der zusammenfassenden Blockdefinition. Fest (keine laufende Nummer):
;; es gibt je Zeichnung immer hoechstens ein Planquadrat-Raster.
(setq *plq-blockname* "ZZ_PLANQUADRATE")
;; --- Layer fuer das Planquadrat-Raster ---
;; cfg/layer.cfg [allgemein] Key "planquadrate"; ohne Eintrag Fallback-Name.
(defun plq-layer ( / lay)
(setq lay (ssg-layer-anlegen "planquadrate" T))
(if (null lay)
(progn
(setq lay "ZZ_Planquadrate")
(ssg-make-layer lay "8" T)
)
)
lay
)
;; Alle INSERTs des Planquadrat-Blocks in der Zeichnung loeschen und die
;; Blockdefinition entfernen (PURGE) - macht die Zeichnung frei fuer einen
;; sauberen Neuaufbau bzw. ist die komplette Logik von DELETE_PLANQUADRATE.
;; Rueckgabe: Anzahl geloeschter INSERTs.
(defun plq-block-entfernen ( / ss i n)
(setq n 0)
(if (tblsearch "BLOCK" *plq-blockname*)
(progn
(setq ss (ssget "X" (list (cons 0 "INSERT") (cons 2 *plq-blockname*))))
(if ss
(progn
(setq i 0)
(while (< i (sslength ss))
(entdel (ssname ss i))
(setq i (1+ i))
(setq n (1+ n))
)
)
)
(command "_-PURGE" "_B" *plq-blockname* "_N")
)
)
n
)
;; 1-basierten Index in eine Buchstaben-Bezeichnung wandeln (1=A, 26=Z,
;; 27=AA, ... wie Excel-Spalten) - Gegenstueck zu _index_to_alpha in
;; lib/export_planquadrat.py.
(defun plq-index-alpha (n / result rest)
(setq result "")
(while (> n 0)
(setq rest (rem (1- n) 26))
(setq result (strcat (chr (+ 65 rest)) result))
(setq n (/ (- n 1 rest) 26))
)
result
)
;; Label fuer einen 1-basierten Index, alpha-p = T -> Buchstaben, sonst Zahl.
(defun plq-label (n alpha-p)
(if alpha-p (plq-index-alpha n) (itoa n))
)
;; Inline-Kommentar (" # ...") von einem Config-Wert abschneiden. Der
;; gemeinsame INI-Parser (ssg-ini-get/ssg-load-ini, ssg_core.lsp) behandelt
;; nur ganze Zeilen ab "#" als Kommentar - die [Planquadrate_*]-Sektionen in
;; cfg/export.cfg nutzen aber INLINE-Kommentare hinter x_description/
;; y_description (z.B. "alphabetisch # Buchstaben, ..."), analog zu
;; lib/export_blockpatterns.py (configparser mit inline_comment_prefixes).
(defun plq-strip-comment (s / pos)
(setq pos (vl-string-search "#" s))
(if pos (vl-string-trim " \t" (substr s 1 pos)) (vl-string-trim " \t" s))
)
;; Ursprung + Ausdehnung aus den EXPORTIERTEN Bloecken ermitteln - dieselbe
;; Blockmenge, die csv:collect-export-blocks (export.lsp) fuer EXPORTCSV
;; sammelt. Der reine EXTMIN/EXTMAX der Zeichnung (alle Entities, auch
;; Hilfsgeometrie wie Kreisel-Umfahrungsboegen) weicht davon ab und fuehrte zu
;; einem im BricsCAD-Raster verschobenen Planquadrat gegenueber der CSV-Spalte
;; - siehe cfg/export.cfg [Planquadrate] und den Hinweis oben in der
;; Moduldoku. Sowohl Ursprung als auch obere Grenze kommen aus der
;; tatsaechlichen Bounding-Box (csv:get-bbox, dieselbe wie
;; bbox_columns()/planquadrat_column()/_bbox_min() in lib/export_csv.py bzw.
;; lib/export_planquadrat.py), NICHT aus dem reinen Einfuegepunkt: bei einem
;; gedrehten oder exzentrisch eingefuegten Block (z.B. ein um 90 Grad
;; gedrehter Kreisel, dessen Insertpunkt an einem Rand statt der Mitte liegt)
;; kann das BBox-Zentrum weit vom Einfuegepunkt abweichen - Ursprung und
;; Zellzuordnung MUESSEN auf derselben Referenz beruhen, sonst driften sie
;; auseinander (genau der Bug, der zur Einfuehrung von plq-export-bbox
;; fuehrte). Fallback auf den Einfuegepunkt (Gruppencode 10) nur, wenn
;; csv:get-bbox fuer einen Block scheitert (z.B. auf eingefrorenem Layer).
;; Rueckgabe: Liste (xmin ymin xmax ymax) oder nil, wenn keine Bloecke.
(defun plq-export-bbox ( / ss i ename ed pt bbox xmin ymin xmax ymax)
(setq ss (csv:collect-export-blocks))
(if ss
(progn
(setq i 0)
(while (setq ename (ssname ss i))
(setq ed (entget ename))
(setq pt (cdr (assoc 10 ed)))
(setq bbox (csv:get-bbox ename))
(if bbox
(progn
(setq xmin (if xmin (min xmin (- (nth 0 bbox) (/ (nth 3 bbox) 2.0)))
(- (nth 0 bbox) (/ (nth 3 bbox) 2.0))))
(setq ymin (if ymin (min ymin (- (nth 1 bbox) (/ (nth 4 bbox) 2.0)))
(- (nth 1 bbox) (/ (nth 4 bbox) 2.0))))
(setq xmax (if xmax (max xmax (+ (nth 0 bbox) (/ (nth 3 bbox) 2.0)))
(+ (nth 0 bbox) (/ (nth 3 bbox) 2.0))))
(setq ymax (if ymax (max ymax (+ (nth 1 bbox) (/ (nth 4 bbox) 2.0)))
(+ (nth 1 bbox) (/ (nth 4 bbox) 2.0))))
)
(if pt
(progn
(setq xmin (if xmin (min xmin (car pt)) (car pt)))
(setq ymin (if ymin (min ymin (cadr pt)) (cadr pt)))
(setq xmax (if xmax (max xmax (car pt)) (car pt)))
(setq ymax (if ymax (max ymax (cadr pt)) (cadr pt)))
)
)
)
(setq i (1+ i))
)
)
)
(if xmin (list xmin ymin xmax ymax))
)
;; Zeichnet eine Linie auf dem aktuellen Layer.
(defun plq-line (p1 p2)
(entmake (list
'(0 . "LINE")
(cons 10 p1)
(cons 11 p2)
))
)
;; Zeichnet einen zentrierten Text an insertPoint.
(defun plq-text (str insertPoint hoehe)
(entmake (list
'(0 . "TEXT")
(cons 10 insertPoint)
(cons 40 hoehe)
(cons 1 str)
(cons 50 0.0)
'(72 . 1)
'(73 . 2)
(cons 11 insertPoint)
))
)
;; ============================================================
;; DRAW_PLANQUADRATE - Raster zeichnen/neu aufbauen
;; ============================================================
(defun c:DRAW_PLANQUADRATE ( / section x-step x-alpha y-step y-alpha
ext-min ext-max bbox x-origin y-origin
x0 y0 x1 y1 lay hoehe n-x n-y i x y
label textabstand lastEnt ss e)
(ssg-start "DRAW_PLANQUADRATE" '(("OSMODE")))
(setvar "OSMODE" 0)
(if (null *export-cfg*) (export:load-cfg))
(setq section (export:cfg "Planquadrate" "section" ""))
(if (or (null section) (= section ""))
(princ (ssg-text "plq-keine-konfiguration"))
(progn
(setq x-step (* 1000.0 (atof (export:cfg section "x_step" "1"))))
(setq y-step (* 1000.0 (atof (export:cfg section "y_step" "1"))))
(setq x-alpha (wcmatch (strcase (substr (plq-strip-comment (export:cfg section "x_description" "numerisch")) 1 5)) "ALPHA*"))
(setq y-alpha (wcmatch (strcase (substr (plq-strip-comment (export:cfg section "y_description" "numerisch")) 1 5)) "ALPHA*"))
(if (or (<= x-step 0.0) (<= y-step 0.0))
(princ (ssg-text "plq-ungueltige-schrittweite"))
(progn
;; Vorhandenes Raster zuerst entfernen (Block + Definition) -
;; wiederholte Aufrufe erzeugen so keine Dubletten UND ein alter
;; Planquadrat-Block verfaelscht nicht die Bloecke-BBox unten
;; (Raster-Linien/-Texte zaehlen ohnehin nicht zu den Export-
;; Blockmustern, aber so ist es unabhaengig davon sauber).
(plq-block-entfernen)
;; Referenzbereich: dieselbe Blockmenge wie EXPORTCSV
;; (csv:collect-export-blocks, export.lsp) - siehe plq-export-bbox.
;; Fallback auf die Zeichnungs-Extents (EXTMIN/EXTMAX), falls keine
;; exportfaehigen Bloecke in der Zeichnung stehen.
(setq bbox (plq-export-bbox))
(setq ext-min (getvar "EXTMIN"))
(setq ext-max (getvar "EXTMAX"))
(setq x-origin (export:cfg "Planquadrate" "x_origin" ""))
(setq x-origin (if (and x-origin (/= x-origin ""))
(* 1000.0 (atof x-origin))
(if bbox (nth 0 bbox) (car ext-min))
))
(setq y-origin (export:cfg "Planquadrate" "y_origin" ""))
(setq y-origin (if (and y-origin (/= y-origin ""))
(* 1000.0 (atof y-origin))
(if bbox (nth 1 bbox) (cadr ext-min))
))
(setq x0 x-origin)
(setq y0 y-origin)
;; Anzahl Spalten/Zeilen: mind. 1, sonst so viele volle Schritte,
;; bis der Referenzbereich (mind.) abgedeckt ist - AUFGERUNDET,
;; damit x1/y1 exakt auf einem Rasterschritt liegen. Damit endet
;; JEDE senkrechte Linie exakt auf der letzten waagerechten (bei
;; y1) und jede waagerechte exakt auf der letzten senkrechten (bei
;; x1) - das Raster bildet immer ein geschlossenes Rechteck.
(setq n-x (max 1 (fix (+ 0.999999 (/ (- (if bbox (nth 2 bbox) (car ext-max)) x0) x-step)))))
(setq n-y (max 1 (fix (+ 0.999999 (/ (- (if bbox (nth 3 bbox) (cadr ext-max)) y0) y-step)))))
(setq x1 (+ x0 (* n-x x-step)))
(setq y1 (+ y0 (* n-y y-step)))
(setq lay (plq-layer))
(setq hoehe (atof (export:cfg "Planquadrate" "text_hoehe_mm" "300")))
(setq textabstand (* hoehe 1.5))
;; Letztes Entity VOR dem Zeichnen merken, um danach per entnext
;; genau die neu erzeugten Objekte einzusammeln (wie
;; kreisel-make-label/draw-module in KreiselInsert.lsp).
(setq lastEnt (entlast))
;; Senkrechte Linien (konstantes X) + Spalten-Beschriftung unten.
(setq i 0)
(while (<= i n-x)
(setq x (+ x0 (* i x-step)))
(plq-line (list x y0 0.0) (list x y1 0.0))
(if (< i n-x)
(progn
(setq label (plq-label (1+ i) x-alpha))
(plq-text label (list (+ x (/ x-step 2.0)) (- y0 textabstand) 0.0) hoehe)
)
)
(setq i (1+ i))
)
;; Waagerechte Linien (konstantes Y) + Zeilen-Beschriftung links.
(setq i 0)
(while (<= i n-y)
(setq y (+ y0 (* i y-step)))
(plq-line (list x0 y 0.0) (list x1 y 0.0))
(if (< i n-y)
(progn
(setq label (plq-label (1+ i) y-alpha))
(plq-text label (list (- x0 textabstand) (+ y (/ y-step 2.0)) 0.0) hoehe)
)
)
(setq i (1+ i))
)
;; --- Neu erzeugte Entities einsammeln und zu einem Block zusammenfassen ---
(setq ss (ssadd))
(if (null lastEnt) (setq e (entnext)) (setq e (entnext lastEnt)))
(while e
(ssadd e ss)
(setq e (entnext e))
)
(if (> (sslength ss) 0)
(progn
(command "_.-BLOCK" *plq-blockname* (list 0.0 0.0 0.0) ss "")
(command "_.INSERT" *plq-blockname* (list 0.0 0.0 0.0) 1 1 0)
)
)
(princ (ssg-textf "plq-fertig" (list (itoa n-x) (itoa n-y) lay)))
)
)
)
)
(ssg-end)
(princ)
)
;; ============================================================
;; DELETE_PLANQUADRATE - Raster-Block entfernen
;; ============================================================
(defun c:DELETE_PLANQUADRATE ( / n)
(ssg-start "DELETE_PLANQUADRATE" nil)
(setq n (plq-block-entfernen))
(if (> n 0)
(princ (ssg-textf "plq-geloescht" (list (itoa n))))
(princ (ssg-text "plq-nichts-zu-loeschen"))
)
(ssg-end)
(princ)
)
;; ============================================================
;; PLANQUADRATE_TOGGLE - "Planquadrate an/aus": zeichnet das Raster, falls
;; noch keines in der Zeichnung existiert, sonst entfernt es das bestehende.
;; ============================================================
(defun c:PLANQUADRATE_TOGGLE ( / vorhanden)
(setq vorhanden (and (tblsearch "BLOCK" *plq-blockname*)
(ssget "X" (list (cons 0 "INSERT") (cons 2 *plq-blockname*)))))
(if vorhanden
(c:DELETE_PLANQUADRATE)
(c:DRAW_PLANQUADRATE)
)
(princ)
)
(princ "\n>>> DrawPlanquadrate.lsp geladen. Befehle: DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE")
(princ)