git.schokokeks.org
Repositories
Help
Report an Issue
schraegbilder.git
Code
Commits
Branches
Tags
Suche
Strukturansicht:
b7d78fa
Branches
Tags
master
schraegbilder.git
schraegbild
svg_builder.py
Linien und Permalink
Bernd Wurst
commited
b7d78fa
at 2026-06-21 10:55:28
svg_builder.py
Blame
History
Raw
import math import svgwrite from schraegbild.projektion import projiziere_3d_auf_2d class SchraegbildBuilder: def __init__(self, len_x1: int, len_x2: int, len_x3: int, neg_x1: int, neg_x2: int, neg_x3: int, mod_x1: int = 2, mod_x2: int = 2, mod_x3: int = 2, skalierung: float = 40.0, punkte: list = None, kanten: list = None, show_labels: bool = True, show_coords: bool = True): self.len_x1 = len_x1 self.len_x2 = len_x2 self.len_x3 = len_x3 self.neg_x1 = neg_x1 self.neg_x2 = neg_x2 self.neg_x3 = neg_x3 self.mod_x1 = mod_x1 self.mod_x2 = mod_x2 self.mod_x3 = mod_x3 self.skalierung = skalierung self.punkte = punkte if punkte is not None else [] self.kanten = kanten if kanten is not None else [] self.show_labels = show_labels self.show_coords = show_coords # Ein schnelles Dictionary bauen, um Koordinaten via Name sofort zu finden self.punkt_dict = {p["name"]: p["coords"] for p in self.punkte} # [Rest der Viewport-Berechnung bleibt unverändert wie zuvor] kaestchen = skalierung / 2.0 padding = 2 * skalierung punkte_fuer_viewport = [ projiziere_3d_auf_2d(self.len_x1 + 1, 0, 0, skalierung), projiziere_3d_auf_2d(-self.neg_x1, 0, 0, skalierung), projiziere_3d_auf_2d(0, self.len_x2 + 1, 0, skalierung), projiziere_3d_auf_2d(0, -self.neg_x2, 0, skalierung), projiziere_3d_auf_2d(0, 0, self.len_x3 + 1, skalierung), projiziere_3d_auf_2d(0, 0, -self.neg_x3, skalierung) ] for p in self.punkte: p_x1, p_x2, p_x3 = p["coords"] punkte_fuer_viewport.append(projiziere_3d_auf_2d(p_x1, p_x2, p_x3, skalierung)) min_x = min(p[0] for p in punkte_fuer_viewport) - padding max_x = max(p[0] for p in punkte_fuer_viewport) + padding min_y = min(p[1] for p in punkte_fuer_viewport) - padding max_y = max(p[1] for p in punkte_fuer_viewport) + padding min_x_grid = math.floor(min_x / kaestchen) * kaestchen min_y_grid = math.floor(min_y / kaestchen) * kaestchen max_x_grid = math.ceil(max_x / kaestchen) * kaestchen max_y_grid = math.ceil(max_y / kaestchen) * kaestchen self.breite = int(max_x_grid - min_x_grid) self.hoehe = int(max_y_grid - min_y_grid) self.ursprung_x = int(-min_x_grid) self.ursprung_y = int(-min_y_grid) self.dwg = svgwrite.Drawing(size=(f"{self.breite}px", f"{self.hoehe}px"), profile='full') self.dwg.viewbox(0, 0, self.breite, self.hoehe) self.pfeil = self.dwg.marker(insert=(9, 4), size=(10, 8), orient='auto') self.pfeil.add(self.dwg.path(d="M 0,0 L 10,4 L 0,8 Z", fill='black')) self.dwg.defs.add(self.pfeil) self.gitter_pattern = self.dwg.pattern(size=(f"{kaestchen}px", f"{kaestchen}px"), patternUnits='userSpaceOnUse') self.gitter_pattern.add( self.dwg.path(d=f"M {kaestchen},0 L 0,0 0,{kaestchen}", fill='none', stroke='#e5e5e5', stroke_width=0.5)) self.dwg.defs.add(self.gitter_pattern) def to_svg(self, x1, x2, x3): rel_x, rel_y = projiziere_3d_auf_2d(x1, x2, x3, self.skalierung) return (self.ursprung_x + rel_x, self.ursprung_y + rel_y) def generiere_koordinatensystem(self) -> str: dwg = self.dwg # [Hier Achsen, Hintergrund & Ticks wie gewohnt zeichnen lassen...] dwg.add(dwg.rect(insert=(0, 0), size=(self.breite, self.hoehe), fill=self.gitter_pattern.get_paint_server())) p_ursprung = self.to_svg(0, 0, 0) dwg.add(dwg.line(p_ursprung, self.to_svg(self.len_x1 + 1, 0, 0), stroke='black', stroke_width=1.5, marker_end=self.pfeil.get_funciri())) dwg.add(dwg.line(p_ursprung, self.to_svg(0, self.len_x2 + 1, 0), stroke='black', stroke_width=1.5, marker_end=self.pfeil.get_funciri())) dwg.add(dwg.line(p_ursprung, self.to_svg(0, 0, self.len_x3 + 1), stroke='black', stroke_width=1.5, marker_end=self.pfeil.get_funciri())) style_neg = {"stroke": "#777777", "stroke_width": 1.2, "stroke_dasharray": "4,4"} if self.neg_x1 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(-self.neg_x1-1, 0, 0), **style_neg)) if self.neg_x2 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(0, -self.neg_x2-1, 0), **style_neg)) if self.neg_x3 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(0, 0, -self.neg_x3-1), **style_neg)) text_style = "font-family:Arial, sans-serif; font-size:12px;" text_style_neg = "font-family:Arial, sans-serif; font-size:12px; fill: #666666;" for i in range(-self.neg_x1, self.len_x1 + 1): if i == 0: continue p_tick = self.to_svg(i, 0, 0) dwg.add(dwg.line((p_tick[0] - 3, p_tick[1] - 3), (p_tick[0] + 3, p_tick[1] + 3), stroke='black' if i > 0 else '#777777', stroke_width=1)) if i % self.mod_x1 == 0: offset_x = p_tick[0] - 18 if i < 0 else p_tick[0] - 16 dwg.add(dwg.text(str(i), insert=(offset_x, p_tick[1] - 6 if i > 0 else p_tick[1] + 8), style=text_style if i > 0 else text_style_neg)) for i in range(-self.neg_x2, self.len_x2 + 1): if i == 0: continue p_tick = self.to_svg(0, i, 0) dwg.add( dwg.line((p_tick[0], p_tick[1] - 3), (p_tick[0], p_tick[1] + 3), stroke='black' if i > 0 else '#777777', stroke_width=1)) if i % self.mod_x2 == 0: offset_x = p_tick[0] - 5 if i < 0 else p_tick[0] - 3 dwg.add( dwg.text(str(i), insert=(offset_x, p_tick[1] + 16), style=text_style if i > 0 else text_style_neg)) for i in range(-self.neg_x3, self.len_x3 + 1): if i == 0: continue p_tick = self.to_svg(0, 0, i) dwg.add( dwg.line((p_tick[0] - 3, p_tick[1]), (p_tick[0] + 3, p_tick[1]), stroke='black' if i > 0 else '#777777', stroke_width=1)) if i % self.mod_x3 == 0: offset_x = p_tick[0] - 22 if i < 0 else p_tick[0] - 16 dwg.add( dwg.text(str(i), insert=(offset_x, p_tick[1] + 4), style=text_style if i > 0 else text_style_neg)) p_x1_label = self.to_svg(self.len_x1 + 1, 0, 0) p_x2_label = self.to_svg(0, self.len_x2 + 1, 0) p_x3_label = self.to_svg(0, 0, self.len_x3 + 1) dwg.add(dwg.text("x₁", insert=(p_x1_label[0] - 15, p_x1_label[1] + 20), style="font-family:Arial; font-size:14px; font-weight:bold;")) dwg.add(dwg.text("x₂", insert=(p_x2_label[0] + 12, p_x2_label[1] + 5), style="font-family:Arial; font-size:14px; font-weight:bold;")) dwg.add(dwg.text("x₃", insert=(p_x3_label[0] - 5, p_x3_label[1] - 15), style="font-family:Arial; font-size:14px; font-weight:bold;")) # --- NEU: ERST DIE KANTEN ZEICHNEN --- for k in self.kanten: if k["von"] in self.punkt_dict and k["nach"] in self.punkt_dict: v_x, v_y = self.to_svg(*self.punkt_dict[k["von"]]) n_x, n_y = self.to_svg(*self.punkt_dict[k["nach"]]) if k["style"] == "sichtbar": # Durchgezogene, blaue Schulbuchkante dwg.add(dwg.line((v_x, v_y), (n_x, n_y), stroke='#0055ff', stroke_width=2)) else: # Gestrichelte Kante für verdeckte Geometrien dwg.add( dwg.line((v_x, v_y), (n_x, n_y), stroke='#0055ff', stroke_width=1.5, stroke_dasharray='4,4')) # --- DANN DIE PUNKTE DARÜBER LEGEN --- for p in self.punkte: x1, x2, x3 = p["coords"] pt = self.to_svg(x1, x2, x3) dwg.add(dwg.line((pt[0] - 4, pt[1] - 4), (pt[0] + 4, pt[1] + 4), stroke='red', stroke_width=1.5)) dwg.add(dwg.line((pt[0] - 4, pt[1] + 4), (pt[0] + 4, pt[1] - 4), stroke='red', stroke_width=1.5)) if self.show_labels: if self.show_coords: fmt_coords = f"({int(x1) if x1.is_integer() else x1}|{int(x2) if x2.is_integer() else x2}|{int(x3) if x3.is_integer() else x3})" label_text = f"{p['name']}{fmt_coords}" else: label_text = p["name"] dwg.add(dwg.text(label_text, insert=(pt[0] + 6, pt[1] - 6), style="font-family:Arial, sans-serif; font-size:13px; font-weight:bold; fill:red;")) return dwg.tostring()