#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ export_planquadrat.py - Berechnet das Planquadrat eines Blocks aus x/y-Koordinate. Liest die aktive Planquadrat-Konfiguration aus cfg/export.cfg ([Planquadrate] -> section=, z.B. Planquadrate_AN) und rundet die x/y-Koordinate eines Blocks (mm, wie im Roh-JSON von export.lsp) je Achse auf die konfigurierte Schrittweite (in Metern) herunter. x_origin/y_origin ([Planquadrate], in m) legen die DXF-Koordinate der Planquadrat-Ecke A/1 fest. Sind sie leer/nicht gesetzt, ermittelt resolve_origins() sie automatisch je Exportlauf aus den zu exportierenden Bloecken (am weitesten links/unten liegendes Element = Ursprung) - siehe dort. Nur von export_csv.py genutzt (EXPORTCSV), nicht von export_sivas.py. """ from export_blockpatterns import cfg_path_from_env, load_export_cfg def load_planquadrat_config(cfg_path=None): """Liest die aktive Planquadrat-Konfiguration aus export.cfg. Rueckgabe: dict mit x_step_mm, x_alpha, y_step_mm, y_alpha, x_origin_mm/y_origin_mm (None, falls in [Planquadrate] nicht gesetzt - siehe resolve_origins()) und format, oder None, wenn [Planquadrate] bzw. die referenzierte Sub-Sektion fehlt. """ if cfg_path is None: cfg_path = cfg_path_from_env() parser = load_export_cfg(cfg_path) if not parser.has_section("Planquadrate"): return None section_name = parser.get("Planquadrate", "section", fallback=None) if not section_name or not parser.has_section(section_name): return None sec = parser[section_name] def _origin_mm(key): raw = parser.get("Planquadrate", key, fallback="").strip() return float(raw) * 1000.0 if raw else None return { "x_step_mm": float(sec.get("x_step", "1")) * 1000.0, "x_alpha": sec.get("x_description", "numerisch").strip().lower().startswith("alpha"), "y_step_mm": float(sec.get("y_step", "1")) * 1000.0, "y_alpha": sec.get("y_description", "numerisch").strip().lower().startswith("alpha"), "x_origin_mm": _origin_mm("x_origin"), "y_origin_mm": _origin_mm("y_origin"), "format": sec.get("format", "{x}/{y}").strip(), } 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). 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. Rueckgabe: neues dict (cfg bleibt unveraendert) oder None, wenn cfg None ist. """ if cfg is None: 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] 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] resolved["y_origin_mm"] = min(ys) if ys else 0.0 return resolved def _index_to_alpha(n): """Wandelt einen 1-basierten Index in eine Buchstaben-Bezeichnung um. 1=A, 26=Z, 27=AA, 28=AB, ... (wie Excel-Spaltennamen). """ result = "" while n > 0: n, remainder = divmod(n - 1, 26) result = chr(65 + remainder) + result return result def _index_for(coord_mm, step_mm, origin_mm=0.0): """1-basierter Planquadrat-Index einer Koordinate fuer eine Schrittweite. origin_mm ist die Koordinate der Planquadrat-Ecke A/1 (x_origin/y_origin aus cfg/export.cfg) - Koordinaten werden vor der Index-Berechnung um diesen Nullpunkt verschoben, damit reale Layouts mit negativen DXF-Koordinaten (Zeichnungsursprung liegt nicht in der Gebaeudeecke) trotzdem ab Index 1 gezaehlt werden. """ 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)) 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)