From 0e0f011ea4197d3960c7d9da6e62359e9ec03699 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Tue, 8 Sep 2026 12:37:58 +0200 Subject: [PATCH] Aufbau der Planquadrate nur durch Boundingboxen. Ansonsten werden die Bereiche der Lage nicht korrekt erzeugt --- CLAUDE.md | 2 +- Lisp/DrawPlanquadrate.lsp | 116 +++++++++++++++++++++++++++++++++----- lib/export_csv.py | 16 ++++-- lib/export_planquadrat.py | 80 ++++++++++++++++++++++---- 4 files changed, 182 insertions(+), 32 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6e3ebf6..0b0de11 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -107,7 +107,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `SSG_LIB_Commands.lsp` | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (`ils-insert-station`: wiederholtes Einfuegen von `BTMT-Beladung` bzw. `SC_Entladung`, Drehung interaktiv, Layer aus `LAYER`-Attribut, Dimension via `ssg-ils-block-laden`) + Sensoren (`ils-insert-sensor`) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber `ils-insert-station` mit den Rohnamen `Eingang`/`Ausgang`, Bloecke `Eingang_.dwg`/`Ausgang_.dwg` in data/ils) + Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | | `Tro_Edit.lsp` | TRO_INSERT, TRO_EDIT | TRO-Marker (Block `TRO_SYM_`) fuer sps_skel (lib/tro_annotate.py): TRO_INSERT fuegt eine bestehende Blockdefinition an einem gewaehlten Punkt ein und setzt ID/TYPE (bricht ab, wenn der Typ noch keine Blockdefinition in der Zeichnung hat - die entsteht nur ueber tro_annotate.py); TRO_EDIT aendert ID/TYPE eines bestehenden Markers per Dialog. Typliste aus `tro_types.lsp` (von `tro_annotate.py --emit-lisp` nach DXFM_LISP geschrieben). Der Dispatcher SSG_BLOCKEDIT leitet Blocknamen `TRO_*` zu TRO_EDIT. | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | -| `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht) ueber die aktuellen Zeichnungs-Extents, im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export) - Ursprung (Index 1) aus `x_origin`/`y_origin` bzw. Fallback EXTMIN. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte (nicht auf die exakte Extent-Grenze), damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. | +| `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, 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/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). | ## DCL-Dialoge diff --git a/Lisp/DrawPlanquadrate.lsp b/Lisp/DrawPlanquadrate.lsp index 381b39a..ec37b2c 100644 --- a/Lisp/DrawPlanquadrate.lsp +++ b/Lisp/DrawPlanquadrate.lsp @@ -10,15 +10,33 @@ ;; je Spalte die zugehoerige Zaehlung (alphabetisch oder numerisch, je nach ;; x_description/y_description). ;; -;; Bereich: die aktuellen Zeichnungs-Extents (EXTMIN/EXTMAX), aber 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). +;; 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); sind sie leer, faellt der Ursprung auf EXTMIN zurueck -;; (= kleinstes X/Y der Zeichnung, analog zur Auto-Ermittlung in -;; export_planquadrat.resolve_origins). +;; (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 @@ -108,6 +126,60 @@ (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 @@ -135,7 +207,7 @@ ;; DRAW_PLANQUADRATE - Raster zeichnen/neu aufbauen ;; ============================================================ (defun c:DRAW_PLANQUADRATE ( / section x-step x-alpha y-step y-alpha - ext-min ext-max x-origin y-origin + 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"))) @@ -156,28 +228,42 @@ (princ (ssg-text "plq-ungueltige-schrittweite")) (progn ;; Vorhandenes Raster zuerst entfernen (Block + Definition) - - ;; wiederholte Aufrufe erzeugen so keine Dubletten. + ;; 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)) (car ext-min))) + (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)) (cadr ext-min))) + (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 die Zeichnungs-Extents (mind.) abgedeckt sind - AUFGERUNDET, + ;; 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 (/ (- (car ext-max) x0) x-step))))) - (setq n-y (max 1 (fix (+ 0.999999 (/ (- (cadr ext-max) y0) y-step))))) + (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))) diff --git a/lib/export_csv.py b/lib/export_csv.py index 5436223..77935d4 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -432,20 +432,28 @@ def bbox_columns(block): def planquadrat_column(block, planquadrat_cfg): - """Berechnet das Planquadrat aus x/y des Blocks (siehe export_planquadrat.py). + """Berechnet das Planquadrat aus der Bounding-Box des Blocks, falls + vorhanden (siehe export_planquadrat.py), sonst aus dem Einfuegepunkt. planquadrat_cfg = Rueckgabe von resolve_origins() fuer den aktuellen Exportlauf (x_origin_mm/y_origin_mm bereits aufgeloest). Leerer String, wenn keine Planquadrat-Konfiguration geladen werden konnte (fehlende/unvollstaendige [Planquadrate]-Sektion in cfg/export.cfg). + + Ueberdeckt die Bounding-Box des Elements mehr als eine Zelle (z.B. ein + Kreisel oder eine lange VarioFoerderer-Kette), liefert compute_planquadrat + eine SPANNE "start-ende" (z.B. "A/1-J/1") statt nur der Mittelpunkt-Zelle. """ if planquadrat_cfg is None: return "" - x = block.get("x") - y = block.get("y") + bbox = block.get("bbox") or {} + x = bbox.get("cx", block.get("x")) + y = bbox.get("cy", block.get("y")) if x is None or y is None: return "" - return compute_planquadrat(float(x), float(y), planquadrat_cfg) + dx = bbox.get("dx", 0.0) + dy = bbox.get("dy", 0.0) + return compute_planquadrat(float(x), float(y), planquadrat_cfg, dx_mm=float(dx), dy_mm=float(dy)) # --------------------------------------------------------------------------- diff --git a/lib/export_planquadrat.py b/lib/export_planquadrat.py index 1e73d15..ec29f2f 100644 --- a/lib/export_planquadrat.py +++ b/lib/export_planquadrat.py @@ -54,17 +54,42 @@ def load_planquadrat_config(cfg_path=None): } +def _bbox_min(block, axis): + """Untere Bounding-Box-Kante eines Blocks auf einer Achse ("x"/"y"), oder + dessen Einfuegepunkt, falls kein bbox-Feld vorhanden ist (z.B. + EXPORTSIVAS, das keine Bounding-Box mitschickt - siehe include-bbox in + csv:block-to-json, Lisp/export.lsp). + + MUSS dieselbe Referenz (BBox statt nur Einfuegepunkt) wie + compute_planquadrat/planquadrat_column verwenden, sonst driften Ursprung + (Index 1) und Zellzuordnung auseinander: ein Element, dessen Bounding-Box- + Mitte weit von seinem Einfuegepunkt abweicht (z.B. ein um 90 Grad + gedrehter Kreisel, dessen Insertpunkt an einem Rand statt der Mitte + liegt), wuerde sonst in eine andere Spalte/Zeile einsortiert als der + Ursprung erwarten laesst. + """ + bbox = block.get("bbox") + if bbox: + c = bbox.get("c" + axis) + d = bbox.get("d" + axis) + if c is not None and d is not None: + return float(c) - float(d) / 2.0 + v = block.get(axis) + return float(v) if v is not None else None + + def resolve_origins(cfg, blocks): """Loest x_origin_mm/y_origin_mm auf, falls in export.cfg nicht gesetzt. cfg = Rueckgabe von load_planquadrat_config() (kann None sein). - blocks = alle Bloecke des aktuellen Exportlaufs (Liste von dicts mit - "x"/"y", wie im Roh-JSON von export.lsp). + blocks = alle Bloecke des aktuellen Exportlaufs (Liste von dicts, wie im + Roh-JSON von export.lsp - "x"/"y" und optional "bbox"). - Automatik: das am weitesten links liegende Element (min x) setzt - x_origin, das am weitesten unten liegende (min y) setzt y_origin - - beide landen damit exakt in Planquadrat-Index 1 auf ihrer Achse. - Explizit in export.cfg gesetzte Werte werden nicht ueberschrieben. + Automatik: die am weitesten links liegende Bounding-Box-Kante (min x) + setzt x_origin, die am weitesten unten liegende (min y) setzt y_origin - + beide landen damit exakt in Planquadrat-Index 1 auf ihrer Achse. Fehlt + einem Block die Bounding-Box, faellt _bbox_min auf dessen Einfuegepunkt + zurueck. Explizit in export.cfg gesetzte Werte werden nicht ueberschrieben. Rueckgabe: neues dict (cfg bleibt unveraendert) oder None, wenn cfg None ist. """ @@ -72,10 +97,12 @@ def resolve_origins(cfg, blocks): return None resolved = dict(cfg) if resolved.get("x_origin_mm") is None: - xs = [float(b["x"]) for b in blocks if b.get("x") is not None] + xs = [_bbox_min(b, "x") for b in blocks] + xs = [v for v in xs if v is not None] resolved["x_origin_mm"] = min(xs) if xs else 0.0 if resolved.get("y_origin_mm") is None: - ys = [float(b["y"]) for b in blocks if b.get("y") is not None] + ys = [_bbox_min(b, "y") for b in blocks] + ys = [v for v in ys if v is not None] resolved["y_origin_mm"] = min(ys) if ys else 0.0 return resolved @@ -104,10 +131,39 @@ def _index_for(coord_mm, step_mm, origin_mm=0.0): return int((coord_mm - origin_mm) // step_mm) + 1 -def compute_planquadrat(x_mm, y_mm, cfg): - """Berechnet die Planquadrat-Bezeichnung fuer eine x/y-Koordinate (mm).""" - x_idx = _index_for(x_mm, cfg["x_step_mm"], cfg.get("x_origin_mm", 0.0)) - y_idx = _index_for(y_mm, cfg["y_step_mm"], cfg.get("y_origin_mm", 0.0)) +def _cell_label(x_idx, y_idx, cfg): x_label = _index_to_alpha(x_idx) if cfg["x_alpha"] else str(x_idx) y_label = _index_to_alpha(y_idx) if cfg["y_alpha"] else str(y_idx) return cfg["format"].format(x=x_label, y=y_label) + + +def compute_planquadrat(x_mm, y_mm, cfg, dx_mm=0.0, dy_mm=0.0): + """Berechnet die Planquadrat-Bezeichnung fuer ein Element. + + x_mm/y_mm = Mittelpunkt (Bounding-Box-Zentrum bzw. Einfuegepunkt, mm). + dx_mm/dy_mm = Ausdehnung der Bounding-Box (mm), optional (Default 0). + + Ohne Ausdehnung (dx_mm/dy_mm <= 0, z.B. Sensoren/Weichen ohne eigene BBox) + wird wie bisher NUR die Zelle des Mittelpunkts geliefert (z.B. "A/1"). + + Mit Ausdehnung > 0 wird die Zelle der unteren/linken UND der oberen/ + rechten Bounding-Box-Ecke berechnet - deckt das Element mehr als eine + Zelle ab (z.B. ein Kreisel ueber A/1 bis J/1, ein VarioFoerderer ueber + B/2 bis C/3), wird eine SPANNE "start-ende" geliefert (z.B. "A/1-J/1", + "B/2-C/3"); liegen beide Ecken in derselben Zelle, wird weiterhin nur + diese eine Zelle geliefert (kein "A/1-A/1"). + """ + if dx_mm and dx_mm > 0 and dy_mm and dy_mm > 0: + x_lo_idx = _index_for(x_mm - dx_mm / 2.0, cfg["x_step_mm"], cfg.get("x_origin_mm", 0.0)) + x_hi_idx = _index_for(x_mm + dx_mm / 2.0, cfg["x_step_mm"], cfg.get("x_origin_mm", 0.0)) + y_lo_idx = _index_for(y_mm - dy_mm / 2.0, cfg["y_step_mm"], cfg.get("y_origin_mm", 0.0)) + y_hi_idx = _index_for(y_mm + dy_mm / 2.0, cfg["y_step_mm"], cfg.get("y_origin_mm", 0.0)) + if x_lo_idx == x_hi_idx and y_lo_idx == y_hi_idx: + return _cell_label(x_lo_idx, y_lo_idx, cfg) + start = _cell_label(x_lo_idx, y_lo_idx, cfg) + ende = _cell_label(x_hi_idx, y_hi_idx, cfg) + return f"{start}-{ende}" + + x_idx = _index_for(x_mm, cfg["x_step_mm"], cfg.get("x_origin_mm", 0.0)) + y_idx = _index_for(y_mm, cfg["y_step_mm"], cfg.get("y_origin_mm", 0.0)) + return _cell_label(x_idx, y_idx, cfg)