Bernd Wurst commited on 2026-06-21 10:36:00
Zeige 12 geänderte Dateien mit 426 Einfügungen und 0 Löschungen.
| ... | ... |
@@ -0,0 +1,12 @@ |
| 1 |
+<component name="InspectionProjectProfileManager"> |
|
| 2 |
+ <profile version="1.0"> |
|
| 3 |
+ <option name="myName" value="Project Default" /> |
|
| 4 |
+ <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true"> |
|
| 5 |
+ <option name="ignoredIdentifiers"> |
|
| 6 |
+ <list> |
|
| 7 |
+ <option value="lib.DSFinVK.dsfinvk" /> |
|
| 8 |
+ </list> |
|
| 9 |
+ </option> |
|
| 10 |
+ </inspection_tool> |
|
| 11 |
+ </profile> |
|
| 12 |
+</component> |
|
| 0 | 13 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,8 @@ |
| 1 |
+<?xml version="1.0" encoding="UTF-8"?> |
|
| 2 |
+<project version="4"> |
|
| 3 |
+ <component name="ProjectModuleManager"> |
|
| 4 |
+ <modules> |
|
| 5 |
+ <module fileurl="file://$PROJECT_DIR$/.idea/schraegbild.iml" filepath="$PROJECT_DIR$/.idea/schraegbild.iml" /> |
|
| 6 |
+ </modules> |
|
| 7 |
+ </component> |
|
| 8 |
+</project> |
|
| 0 | 9 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,10 @@ |
| 1 |
+<?xml version="1.0" encoding="UTF-8"?> |
|
| 2 |
+<module type="PYTHON_MODULE" version="4"> |
|
| 3 |
+ <component name="NewModuleRootManager"> |
|
| 4 |
+ <content url="file://$MODULE_DIR$"> |
|
| 5 |
+ <excludeFolder url="file://$MODULE_DIR$/.venv" /> |
|
| 6 |
+ </content> |
|
| 7 |
+ <orderEntry type="jdk" jdkName="Python 3.12 (schraegbild)" jdkType="Python SDK" /> |
|
| 8 |
+ <orderEntry type="sourceFolder" forTests="false" /> |
|
| 9 |
+ </component> |
|
| 10 |
+</module> |
|
| 0 | 11 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,98 @@ |
| 1 |
+<!DOCTYPE html> |
|
| 2 |
+<html lang="de"> |
|
| 3 |
+<head> |
|
| 4 |
+ <meta charset="UTF-8"> |
|
| 5 |
+ <title>Schrägbild-Generator</title> |
|
| 6 |
+ <link rel="stylesheet" href="styles.css"> |
|
| 7 |
+ <link rel="stylesheet" href="https://pyscript.net/releases/2026.6.1/core.css" /> |
|
| 8 |
+ <script type="module" src="https://pyscript.net/releases/2026.6.1/core.js"></script> |
|
| 9 |
+</head> |
|
| 10 |
+<body> |
|
| 11 |
+ |
|
| 12 |
+<div class="sidebar"> |
|
| 13 |
+ <h2>Koordinatensystem</h2> |
|
| 14 |
+ |
|
| 15 |
+ <div class="control-group row"> |
|
| 16 |
+ <div> |
|
| 17 |
+ <label for="len-x1">Max x₁ (+):</label> |
|
| 18 |
+ <input type="number" id="len-x1" min="0" max="30" value="5" py-input="zeichne_koordinatensystem"> |
|
| 19 |
+ </div> |
|
| 20 |
+ <div> |
|
| 21 |
+ <label for="neg-x1">Max x₁ (-):</label> |
|
| 22 |
+ <input type="number" id="neg-x1" min="0" max="30" value="2" py-input="zeichne_koordinatensystem"> |
|
| 23 |
+ </div> |
|
| 24 |
+ <div> |
|
| 25 |
+ <label for="mod-x1">Schrittw.:</label> |
|
| 26 |
+ <input type="number" id="mod-x1" min="1" max="10" value="2" py-input="zeichne_koordinatensystem"> |
|
| 27 |
+ </div> |
|
| 28 |
+ </div> |
|
| 29 |
+ |
|
| 30 |
+ <div class="control-group row"> |
|
| 31 |
+ <div> |
|
| 32 |
+ <label for="len-x2">Max x₂ (+):</label> |
|
| 33 |
+ <input type="number" id="len-x2" min="0" max="30" value="6" py-input="zeichne_koordinatensystem"> |
|
| 34 |
+ </div> |
|
| 35 |
+ <div> |
|
| 36 |
+ <label for="neg-x2">Max x₂ (-):</label> |
|
| 37 |
+ <input type="number" id="neg-x2" min="0" max="30" value="2" py-input="zeichne_koordinatensystem"> |
|
| 38 |
+ </div> |
|
| 39 |
+ <div> |
|
| 40 |
+ <label for="mod-x2">Schrittw.:</label> |
|
| 41 |
+ <input type="number" id="mod-x2" min="1" max="10" value="2" py-input="zeichne_koordinatensystem"> |
|
| 42 |
+ </div> |
|
| 43 |
+ </div> |
|
| 44 |
+ |
|
| 45 |
+ <div class="control-group row"> |
|
| 46 |
+ <div> |
|
| 47 |
+ <label for="len-x3">Max x₃ (+):</label> |
|
| 48 |
+ <input type="number" id="len-x3" min="0" max="30" value="6" py-input="zeichne_koordinatensystem"> |
|
| 49 |
+ </div> |
|
| 50 |
+ <div> |
|
| 51 |
+ <label for="neg-x3">Max x₃ (-):</label> |
|
| 52 |
+ <input type="number" id="neg-x3" min="0" max="30" value="2" py-input="zeichne_koordinatensystem"> |
|
| 53 |
+ </div> |
|
| 54 |
+ <div> |
|
| 55 |
+ <label for="mod-x3">Schrittw.:</label> |
|
| 56 |
+ <input type="number" id="mod-x3" min="1" max="10" value="2" py-input="zeichne_koordinatensystem"> |
|
| 57 |
+ </div> |
|
| 58 |
+ </div> |
|
| 59 |
+ <hr> |
|
| 60 |
+ <h2>Punkte eintragen</h2> |
|
| 61 |
+ <div class="control-group"> |
|
| 62 |
+ <label for="punkte-input">Punkte (z.B. A(2|4|3) oder B(-1|2|4)):</label> |
|
| 63 |
+ <textarea id="punkte-input" rows="6" placeholder="A(2|4|3) B(-1|2|4)" py-input="zeichne_koordinatensystem">A(2|4|3)</textarea> |
|
| 64 |
+ </div> |
|
| 65 |
+ |
|
| 66 |
+ <div class="control-group checkbox-group"> |
|
| 67 |
+ <label> |
|
| 68 |
+ <input type="checkbox" id="show-labels" checked py-input="zeichne_koordinatensystem"> |
|
| 69 |
+ Punkte überhaupt beschriften |
|
| 70 |
+ </label> |
|
| 71 |
+ </div> |
|
| 72 |
+ <div class="control-group checkbox-group"> |
|
| 73 |
+ <label> |
|
| 74 |
+ <input type="checkbox" id="show-coords" checked py-input="zeichne_koordinatensystem"> |
|
| 75 |
+ Koordinaten mit anzeigen (z.B. A(2|4|3)) |
|
| 76 |
+ </label> |
|
| 77 |
+ </div> |
|
| 78 |
+ <hr> |
|
| 79 |
+ <button id="btn-download" class="btn-primary" py-click="download_svg"> |
|
| 80 |
+ SVG herunterladen |
|
| 81 |
+ </button> |
|
| 82 |
+ |
|
| 83 |
+</div> |
|
| 84 |
+ |
|
| 85 |
+ <div class="main-content"> |
|
| 86 |
+ <div id="canvas-container"></div> |
|
| 87 |
+ </div> |
|
| 88 |
+ |
|
| 89 |
+ <script type="py" src="./main.py" config='{
|
|
| 90 |
+ "packages": ["svgwrite"], |
|
| 91 |
+ "files": {
|
|
| 92 |
+ "./schraegbild/__init__.py": "schraegbild/__init__.py", |
|
| 93 |
+ "./schraegbild/projektion.py": "schraegbild/projektion.py", |
|
| 94 |
+ "./schraegbild/svg_builder.py": "schraegbild/svg_builder.py" |
|
| 95 |
+ } |
|
| 96 |
+ }'></script> |
|
| 97 |
+</body> |
|
| 98 |
+</html> |
|
| 0 | 99 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,75 @@ |
| 1 |
+import re |
|
| 2 |
+from pyscript import document, window |
|
| 3 |
+from schraegbild.svg_builder import SchraegbildBuilder |
|
| 4 |
+ |
|
| 5 |
+# Globale Variable, um den aktuellen SVG-String für den Download zwischenzuspeichern |
|
| 6 |
+aktuelles_svg = "" |
|
| 7 |
+ |
|
| 8 |
+ |
|
| 9 |
+def zeichne_koordinatensystem(event=None): |
|
| 10 |
+ global aktuelles_svg |
|
| 11 |
+ |
|
| 12 |
+ # 1. Werte auslesen wie bisher |
|
| 13 |
+ len_x1 = int(document.querySelector("#len-x1").value)
|
|
| 14 |
+ len_x2 = int(document.querySelector("#len-x2").value)
|
|
| 15 |
+ len_x3 = int(document.querySelector("#len-x3").value)
|
|
| 16 |
+ neg_x1 = int(document.querySelector("#neg-x1").value)
|
|
| 17 |
+ neg_x2 = int(document.querySelector("#neg-x2").value)
|
|
| 18 |
+ neg_x3 = int(document.querySelector("#neg-x3").value)
|
|
| 19 |
+ mod_x1 = int(document.querySelector("#mod-x1").value)
|
|
| 20 |
+ mod_x2 = int(document.querySelector("#mod-x2").value)
|
|
| 21 |
+ mod_x3 = int(document.querySelector("#mod-x3").value)
|
|
| 22 |
+ |
|
| 23 |
+ show_labels = document.querySelector("#show-labels").checked
|
|
| 24 |
+ show_coords = document.querySelector("#show-coords").checked
|
|
| 25 |
+ punkte_text = document.querySelector("#punkte-input").value
|
|
| 26 |
+ |
|
| 27 |
+ # 2. Punkte parsen |
|
| 28 |
+ punkte_liste = [] |
|
| 29 |
+ pattern = re.compile(r"([A-Za-z0-9_]+)\s*\(\s*([-\d.]+)\s*\|\s*([-\d.]+)\s*\|\s*([-\d.]+)\s*\)") |
|
| 30 |
+ |
|
| 31 |
+ for zeile in punkte_text.splitlines(): |
|
| 32 |
+ match = pattern.search(zeile) |
|
| 33 |
+ if match: |
|
| 34 |
+ name = match.group(1) |
|
| 35 |
+ x1 = float(match.group(2)) |
|
| 36 |
+ x2 = float(match.group(3)) |
|
| 37 |
+ x3 = float(match.group(4)) |
|
| 38 |
+ punkte_liste.append({"name": name, "coords": (x1, x2, x3)})
|
|
| 39 |
+ |
|
| 40 |
+ # 3. Builder aufrufen |
|
| 41 |
+ builder = SchraegbildBuilder( |
|
| 42 |
+ len_x1, len_x2, len_x3, neg_x1, neg_x2, neg_x3, mod_x1, mod_x2, mod_x3, |
|
| 43 |
+ punkte=punkte_liste, show_labels=show_labels, show_coords=show_coords |
|
| 44 |
+ ) |
|
| 45 |
+ |
|
| 46 |
+ # SVG generieren und global speichern |
|
| 47 |
+ aktuelles_svg = builder.generiere_koordinatensystem() |
|
| 48 |
+ |
|
| 49 |
+ # Im UI anzeigen |
|
| 50 |
+ document.querySelector("#canvas-container").innerHTML = aktuelles_svg
|
|
| 51 |
+ |
|
| 52 |
+ |
|
| 53 |
+def download_svg(event=None): |
|
| 54 |
+ global aktuelles_svg |
|
| 55 |
+ if not aktuelles_svg: |
|
| 56 |
+ return |
|
| 57 |
+ |
|
| 58 |
+ # Über das JavaScript-Window-Objekt einen unsichtbaren Download-Link bauen |
|
| 59 |
+ js_code = f""" |
|
| 60 |
+ var blob = new Blob([{repr(aktuelles_svg)}], {{type: "image/svg+xml;charset=utf-8"}});
|
|
| 61 |
+ var url = URL.createObjectURL(blob); |
|
| 62 |
+ var a = document.createElement("a");
|
|
| 63 |
+ a.href = url; |
|
| 64 |
+ a.download = "schraegbild.svg"; |
|
| 65 |
+ document.body.appendChild(a); |
|
| 66 |
+ a.click(); |
|
| 67 |
+ document.body.removeChild(a); |
|
| 68 |
+ URL.revokeObjectURL(url); |
|
| 69 |
+ """ |
|
| 70 |
+ # Den JS-Code im Browser-Kontext ausführen |
|
| 71 |
+ window.eval(js_code) |
|
| 72 |
+ |
|
| 73 |
+ |
|
| 74 |
+# Initialer Aufruf |
|
| 75 |
+zeichne_koordinatensystem() |
|
| 0 | 76 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,41 @@ |
| 1 |
+import math |
|
| 2 |
+ |
|
| 3 |
+# Konstanten für die Schulbuch-Projektion |
|
| 4 |
+WINKEL_X1_GRAD = 135 # 135 Grad im mathematisch positiven Drehsinn (nach unten-links) |
|
| 5 |
+VERKUERZUNG_X1 = 1.0 / math.sqrt(2) # ca. 0.7071 |
|
| 6 |
+ |
|
| 7 |
+ |
|
| 8 |
+def projiziere_3d_auf_2d( |
|
| 9 |
+ x1: float, |
|
| 10 |
+ x2: float, |
|
| 11 |
+ x3: float, |
|
| 12 |
+ skalierung: float = 40.0 |
|
| 13 |
+) -> tuple[float, float]: |
|
| 14 |
+ """ |
|
| 15 |
+ Rechnet eine 3D-Koordinate (x1, x2, x3) in eine relative 2D-Ebene um. |
|
| 16 |
+ |
|
| 17 |
+ :param x1: Koordinate nach vorne-links |
|
| 18 |
+ :param x2: Koordinate nach rechts |
|
| 19 |
+ :param x3: Koordinate nach oben |
|
| 20 |
+ :param skalierung: Multiplikator, wie viele Pixel eine Längeneinheit (LE) groß ist (z.B. 40px = 1cm). |
|
| 21 |
+ :return: (x_relativ, y_relativ) bezogen auf den mathematischen Ursprung (0,0). |
|
| 22 |
+ """ |
|
| 23 |
+ # Winkel in Bogenmaß umrechnen |
|
| 24 |
+ # Da wir nach unten-links wollen, nutzen wir den Winkel direkt. |
|
| 25 |
+ winkel_rad = math.radians(WINKEL_X1_GRAD) |
|
| 26 |
+ |
|
| 27 |
+ # 1. Berechnung des x-Werts auf dem Papier (relativ zum Ursprung): |
|
| 28 |
+ # x2 geht direkt nach rechts (+). |
|
| 29 |
+ # x1 geht nach links (-), das ist im cos(135°) bereits mathematisch korrekt enthalten. |
|
| 30 |
+ x_2d = x2 + (x1 * VERKUERZUNG_X1 * math.cos(winkel_rad)) |
|
| 31 |
+ |
|
| 32 |
+ # 2. Berechnung des y-Werts auf dem Papier (relativ zum Ursprung): |
|
| 33 |
+ # x3 geht nach oben (+). |
|
| 34 |
+ # x1 geht nach unten (-), das ist im sin(135°) positiv, aber da im SVG 'unten' positiv ist, |
|
| 35 |
+ # müssen wir die mathematische Richtung umkehren. |
|
| 36 |
+ # Mathematisch: y = x3 + x1 * verkürzung * sin(135°) |
|
| 37 |
+ # Für SVG (da y nach unten wächst): Wir negieren den gesamten mathematischen y-Wert. |
|
| 38 |
+ y_2d = (x1 * VERKUERZUNG_X1 * math.sin(winkel_rad)) - x3 |
|
| 39 |
+ |
|
| 40 |
+ # Mit dem Skalierungsfaktor (Pixel pro Längeneinheit) multiplizieren |
|
| 41 |
+ return x_2d * skalierung, y_2d * skalierung |
|
| 0 | 42 |
\ No newline at end of file |
| ... | ... |
@@ -0,0 +1,164 @@ |
| 1 |
+import math |
|
| 2 |
+import svgwrite |
|
| 3 |
+from schraegbild.projektion import projiziere_3d_auf_2d |
|
| 4 |
+ |
|
| 5 |
+ |
|
| 6 |
+class SchraegbildBuilder: |
|
| 7 |
+ def __init__(self, len_x1: int, len_x2: int, len_x3: int, neg_x1: int, neg_x2: int, neg_x3: int, |
|
| 8 |
+ mod_x1: int = 2, mod_x2: int = 2, mod_x3: int = 2, skalierung: float = 40.0, |
|
| 9 |
+ punkte: list = None, show_labels: bool = True, show_coords: bool = True): |
|
| 10 |
+ |
|
| 11 |
+ self.len_x1 = len_x1 |
|
| 12 |
+ self.len_x2 = len_x2 |
|
| 13 |
+ self.len_x3 = len_x3 |
|
| 14 |
+ self.neg_x1 = neg_x1 |
|
| 15 |
+ self.neg_x2 = neg_x2 |
|
| 16 |
+ self.neg_x3 = neg_x3 |
|
| 17 |
+ self.mod_x1 = mod_x1 |
|
| 18 |
+ self.mod_x2 = mod_x2 |
|
| 19 |
+ self.mod_x3 = mod_x3 |
|
| 20 |
+ self.skalierung = skalierung |
|
| 21 |
+ |
|
| 22 |
+ # NEU: Parameter für Punkte sichern |
|
| 23 |
+ self.punkte = punkte if punkte is not None else [] |
|
| 24 |
+ self.show_labels = show_labels |
|
| 25 |
+ self.show_coords = show_coords |
|
| 26 |
+ |
|
| 27 |
+ kaestchen = skalierung / 2.0 |
|
| 28 |
+ padding = 2 * skalierung |
|
| 29 |
+ |
|
| 30 |
+ # --- Extrempunkte berechnen (Jetzt inkl. der eingetragenen Punkte!) --- |
|
| 31 |
+ # Damit das Skript nicht abstürzt, wenn ein Lehrer einen Punkt außerhalb des Systems einträgt, |
|
| 32 |
+ # beziehen wir die Punkte dynamisch in den Viewport mit ein: |
|
| 33 |
+ punkte_fuer_viewport = [ |
|
| 34 |
+ projiziere_3d_auf_2d(self.len_x1 + 1, 0, 0, skalierung), |
|
| 35 |
+ projiziere_3d_auf_2d(-self.neg_x1, 0, 0, skalierung), |
|
| 36 |
+ projiziere_3d_auf_2d(0, self.len_x2 + 1, 0, skalierung), |
|
| 37 |
+ projiziere_3d_auf_2d(0, -self.neg_x2, 0, skalierung), |
|
| 38 |
+ projiziere_3d_auf_2d(0, 0, self.len_x3 + 1, skalierung), |
|
| 39 |
+ projiziere_3d_auf_2d(0, 0, -self.neg_x3, skalierung) |
|
| 40 |
+ ] |
|
| 41 |
+ for p in self.punkte: |
|
| 42 |
+ p_x1, p_x2, p_x3 = p["coords"] |
|
| 43 |
+ punkte_fuer_viewport.append(projiziere_3d_auf_2d(p_x1, p_x2, p_x3, skalierung)) |
|
| 44 |
+ |
|
| 45 |
+ min_x = min(p[0] for p in punkte_fuer_viewport) - padding |
|
| 46 |
+ max_x = max(p[0] for p in punkte_fuer_viewport) + padding |
|
| 47 |
+ min_y = min(p[1] for p in punkte_fuer_viewport) - padding |
|
| 48 |
+ max_y = max(p[1] for p in punkte_fuer_viewport) + padding |
|
| 49 |
+ |
|
| 50 |
+ min_x_grid = math.floor(min_x / kaestchen) * kaestchen |
|
| 51 |
+ min_y_grid = math.floor(min_y / kaestchen) * kaestchen |
|
| 52 |
+ max_x_grid = math.ceil(max_x / kaestchen) * kaestchen |
|
| 53 |
+ max_y_grid = math.ceil(max_y / kaestchen) * kaestchen |
|
| 54 |
+ |
|
| 55 |
+ self.breite = int(max_x_grid - min_x_grid) |
|
| 56 |
+ self.hoehe = int(max_y_grid - min_y_grid) |
|
| 57 |
+ self.ursprung_x = int(-min_x_grid) |
|
| 58 |
+ self.ursprung_y = int(-min_y_grid) |
|
| 59 |
+ |
|
| 60 |
+ self.dwg = svgwrite.Drawing(size=(f"{self.breite}px", f"{self.hoehe}px"), profile='full')
|
|
| 61 |
+ self.dwg.viewbox(0, 0, self.breite, self.hoehe) |
|
| 62 |
+ |
|
| 63 |
+ self.pfeil = self.dwg.marker(insert=(9, 4), size=(10, 8), orient='auto') |
|
| 64 |
+ self.pfeil.add(self.dwg.path(d="M 0,0 L 10,4 L 0,8 Z", fill='black')) |
|
| 65 |
+ self.dwg.defs.add(self.pfeil) |
|
| 66 |
+ |
|
| 67 |
+ self.gitter_pattern = self.dwg.pattern(size=(f"{kaestchen}px", f"{kaestchen}px"), patternUnits='userSpaceOnUse')
|
|
| 68 |
+ self.gitter_pattern.add( |
|
| 69 |
+ self.dwg.path(d=f"M {kaestchen},0 L 0,0 0,{kaestchen}", fill='none', stroke='#e5e5e5', stroke_width=0.5))
|
|
| 70 |
+ self.dwg.defs.add(self.gitter_pattern) |
|
| 71 |
+ |
|
| 72 |
+ def to_svg(self, x1, x2, x3): |
|
| 73 |
+ rel_x, rel_y = projiziere_3d_auf_2d(x1, x2, x3, self.skalierung) |
|
| 74 |
+ return (self.ursprung_x + rel_x, self.ursprung_y + rel_y) |
|
| 75 |
+ |
|
| 76 |
+ def generiere_koordinatensystem(self) -> str: |
|
| 77 |
+ dwg = self.dwg |
|
| 78 |
+ # [Hier bleibt der komplette bisherige Code für Hintergrund, Achsen, Ticks, Beschriftungen unverändert] |
|
| 79 |
+ dwg.add(dwg.rect(insert=(0, 0), size=(self.breite, self.hoehe), fill=self.gitter_pattern.get_paint_server())) |
|
| 80 |
+ |
|
| 81 |
+ p_ursprung = self.to_svg(0, 0, 0) |
|
| 82 |
+ dwg.add(dwg.line(p_ursprung, self.to_svg(self.len_x1 + 1, 0, 0), stroke='black', stroke_width=1.5, |
|
| 83 |
+ marker_end=self.pfeil.get_funciri())) |
|
| 84 |
+ dwg.add(dwg.line(p_ursprung, self.to_svg(0, self.len_x2 + 1, 0), stroke='black', stroke_width=1.5, |
|
| 85 |
+ marker_end=self.pfeil.get_funciri())) |
|
| 86 |
+ dwg.add(dwg.line(p_ursprung, self.to_svg(0, 0, self.len_x3 + 1), stroke='black', stroke_width=1.5, |
|
| 87 |
+ marker_end=self.pfeil.get_funciri())) |
|
| 88 |
+ |
|
| 89 |
+ style_neg = {"stroke": "#777777", "stroke_width": 1.2, "stroke_dasharray": "4,4"}
|
|
| 90 |
+ if self.neg_x1 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(-self.neg_x1-1, 0, 0), **style_neg)) |
|
| 91 |
+ if self.neg_x2 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(0, -self.neg_x2-1, 0), **style_neg)) |
|
| 92 |
+ if self.neg_x3 > 0: dwg.add(dwg.line(p_ursprung, self.to_svg(0, 0, -self.neg_x3-1), **style_neg)) |
|
| 93 |
+ |
|
| 94 |
+ text_style = "font-family:Arial, sans-serif; font-size:12px;" |
|
| 95 |
+ text_style_neg = "font-family:Arial, sans-serif; font-size:12px; fill: #666666;" |
|
| 96 |
+ |
|
| 97 |
+ for i in range(-self.neg_x1, self.len_x1 + 1): |
|
| 98 |
+ if i == 0: continue |
|
| 99 |
+ p_tick = self.to_svg(i, 0, 0) |
|
| 100 |
+ dwg.add(dwg.line((p_tick[0] - 3, p_tick[1] - 3), (p_tick[0] + 3, p_tick[1] + 3), |
|
| 101 |
+ stroke='black' if i > 0 else '#777777', stroke_width=1)) |
|
| 102 |
+ if i % self.mod_x1 == 0: |
|
| 103 |
+ offset_x = p_tick[0] - 18 if i < 0 else p_tick[0] - 16 |
|
| 104 |
+ dwg.add(dwg.text(str(i), insert=(offset_x, p_tick[1] - 6 if i > 0 else p_tick[1] + 8), |
|
| 105 |
+ style=text_style if i > 0 else text_style_neg)) |
|
| 106 |
+ |
|
| 107 |
+ for i in range(-self.neg_x2, self.len_x2 + 1): |
|
| 108 |
+ if i == 0: continue |
|
| 109 |
+ p_tick = self.to_svg(0, i, 0) |
|
| 110 |
+ dwg.add( |
|
| 111 |
+ dwg.line((p_tick[0], p_tick[1] - 3), (p_tick[0], p_tick[1] + 3), stroke='black' if i > 0 else '#777777', |
|
| 112 |
+ stroke_width=1)) |
|
| 113 |
+ if i % self.mod_x2 == 0: |
|
| 114 |
+ offset_x = p_tick[0] - 5 if i < 0 else p_tick[0] - 3 |
|
| 115 |
+ dwg.add( |
|
| 116 |
+ dwg.text(str(i), insert=(offset_x, p_tick[1] + 16), style=text_style if i > 0 else text_style_neg)) |
|
| 117 |
+ |
|
| 118 |
+ for i in range(-self.neg_x3, self.len_x3 + 1): |
|
| 119 |
+ if i == 0: continue |
|
| 120 |
+ p_tick = self.to_svg(0, 0, i) |
|
| 121 |
+ dwg.add( |
|
| 122 |
+ dwg.line((p_tick[0] - 3, p_tick[1]), (p_tick[0] + 3, p_tick[1]), stroke='black' if i > 0 else '#777777', |
|
| 123 |
+ stroke_width=1)) |
|
| 124 |
+ if i % self.mod_x3 == 0: |
|
| 125 |
+ offset_x = p_tick[0] - 22 if i < 0 else p_tick[0] - 16 |
|
| 126 |
+ dwg.add( |
|
| 127 |
+ dwg.text(str(i), insert=(offset_x, p_tick[1] + 4), style=text_style if i > 0 else text_style_neg)) |
|
| 128 |
+ |
|
| 129 |
+ p_x1_label = self.to_svg(self.len_x1 + 1, 0, 0) |
|
| 130 |
+ p_x2_label = self.to_svg(0, self.len_x2 + 1, 0) |
|
| 131 |
+ p_x3_label = self.to_svg(0, 0, self.len_x3 + 1) |
|
| 132 |
+ dwg.add(dwg.text("x₁", insert=(p_x1_label[0] - 15, p_x1_label[1] + 20),
|
|
| 133 |
+ style="font-family:Arial; font-size:14px; font-weight:bold;")) |
|
| 134 |
+ dwg.add(dwg.text("x₂", insert=(p_x2_label[0] + 12, p_x2_label[1] + 5),
|
|
| 135 |
+ style="font-family:Arial; font-size:14px; font-weight:bold;")) |
|
| 136 |
+ dwg.add(dwg.text("x₃", insert=(p_x3_label[0] - 5, p_x3_label[1] - 15),
|
|
| 137 |
+ style="font-family:Arial; font-size:14px; font-weight:bold;")) |
|
| 138 |
+ |
|
| 139 |
+ # --- AB HIER NEU: PUNKTE ZEICHNEN --- |
|
| 140 |
+ for p in self.punkte: |
|
| 141 |
+ x1, x2, x3 = p["coords"] |
|
| 142 |
+ pt = self.to_svg(x1, x2, x3) |
|
| 143 |
+ |
|
| 144 |
+ # Punktkreuz zeichnen (Größe: 4px Armlänge, rot hervorgehoben für Fokus) |
|
| 145 |
+ dwg.add(dwg.line((pt[0] - 4, pt[1] - 4), (pt[0] + 4, pt[1] + 4), stroke='red', stroke_width=1.5)) |
|
| 146 |
+ dwg.add(dwg.line((pt[0] - 4, pt[1] + 4), (pt[0] + 4, pt[1] - 4), stroke='red', stroke_width=1.5)) |
|
| 147 |
+ |
|
| 148 |
+ # Beschriftungslogik auswerten |
|
| 149 |
+ if self.show_labels: |
|
| 150 |
+ if self.show_coords: |
|
| 151 |
+ # Format: A(2|4|3) - wir formatieren eventuelle Floats sauber weg, wenn es Ganzzahlen sind |
|
| 152 |
+ 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})"
|
|
| 153 |
+ label_text = f"{p['name']}{fmt_coords}"
|
|
| 154 |
+ else: |
|
| 155 |
+ # Nur Name: A |
|
| 156 |
+ label_text = p["name"] |
|
| 157 |
+ |
|
| 158 |
+ dwg.add(dwg.text( |
|
| 159 |
+ label_text, |
|
| 160 |
+ insert=(pt[0] + 6, pt[1] - 6), |
|
| 161 |
+ style="font-family:Arial, sans-serif; font-size:13px; font-weight:bold; fill:red;" |
|
| 162 |
+ )) |
|
| 163 |
+ |
|
| 164 |
+ return dwg.tostring() |
|
| 0 | 165 |
\ No newline at end of file |