ZAIDX11's picture
Add files using upload-large-folder tool
39d1e9f verified
from __future__ import annotations
import json
import os
from typing import Iterable, List, Optional, Sequence, Tuple
from alphageometry.modules import utils
Point = Tuple[float, float]
Triangle = Tuple[int, int, int]
def _scale_and_translate(points: Iterable[Point], width: float = 400, height: float = 400, margin: float = 8.0):
pts = list(points)
if not pts:
return [], width, height
(minx, miny), (maxx, maxy) = utils.bounding_box(pts)
w = maxx - minx
h = maxy - miny
if w == 0 and h == 0:
# single point
return [(width / 2.0, height / 2.0)], width, height
sx = (width - 2 * margin) / (w if w != 0 else 1.0)
sy = (height - 2 * margin) / (h if h != 0 else 1.0)
s = min(sx, sy)
out = [((p[0] - minx) * s + margin, (maxy - p[1]) * s + margin) for p in pts]
return out, width, height
def polygon_to_svg(
poly: Iterable[Point],
stroke: str = "black",
fill: str = "none",
width: int = 400,
height: int = 400,
label: Optional[str] = None,
show_vertex_labels: bool = False,
) -> str:
"""Render a single polygon to an SVG string.
Parameters:
- label: optional text label for the polygon (renders at centroid)
- show_vertex_labels: when True, each vertex is annotated with its index
"""
pts = list(poly)
scaled, w, h = _scale_and_translate(pts, width=width, height=height)
path = " ".join(f"{x:.2f},{y:.2f}" for x, y in scaled)
svg = [f"<svg xmlns='http://www.w3.org/2000/svg' width='{w}' height='{h}'>"]
svg.append(f" <polygon points='{path}' stroke='{stroke}' fill='{fill}' stroke-width='1' />")
if label is not None and pts:
cx, cy = utils.polygon_centroid(pts)
# scale centroid into svg coords
scaled_centroid, _, _ = _scale_and_translate([(cx, cy)], width=w, height=h)
if scaled_centroid:
sx, sy = scaled_centroid[0]
svg.append(f" <text x='{sx:.2f}' y='{sy:.2f}' font-size='12' fill='black'>{label}</text>")
if show_vertex_labels and pts:
for i, (x, y) in enumerate(scaled):
svg.append(f" <text x='{x:.2f}' y='{y:.2f}' font-size='10' fill='red'>{i}</text>")
svg.append("</svg>")
return "\n".join(svg)
def mesh_to_svg(
vertices: Iterable[Point],
triangles: Iterable[Triangle],
stroke: str = "black",
fill: str = "none",
width: int = 600,
height: int = 600,
show_triangle_labels: bool = False,
) -> str:
verts = list(vertices)
tri = list(triangles)
scaled, w, h = _scale_and_translate(verts, width=width, height=height)
svg = [f"<svg xmlns='http://www.w3.org/2000/svg' width='{w}' height='{h}'>"]
svg.append("<g fill='none' stroke='black' stroke-width='1'>")
for t_idx, (a, b, c) in enumerate(tri):
if a < 0 or b < 0 or c < 0 or a >= len(scaled) or b >= len(scaled) or c >= len(scaled):
continue
xa, ya = scaled[a]
xb, yb = scaled[b]
xc, yc = scaled[c]
svg.append(f" <path d='M {xa:.2f} {ya:.2f} L {xb:.2f} {yb:.2f} L {xc:.2f} {yc:.2f} Z' stroke='{stroke}' fill='{fill}' />")
if show_triangle_labels:
tx = (xa + xb + xc) / 3.0
ty = (ya + yb + yc) / 3.0
svg.append(f" <text x='{tx:.2f}' y='{ty:.2f}' font-size='10' fill='blue'>{t_idx}</text>")
svg.append("</g>")
svg.append("</svg>")
return "\n".join(svg)
def render_scene(
polygons: Optional[Sequence[Iterable[Point]]] = None,
meshes: Optional[Sequence[Tuple[Iterable[Point], Iterable[Triangle]]]] = None,
width: int = 800,
height: int = 600,
background: str = "white",
) -> str:
"""Render multiple polygons and meshes into a single SVG scene.
This composes objects into one SVG canvas. Each polygon/mesh will be
scaled independently to the full canvas; callers who need consistent
coordinates should pre-scale externally.
"""
svg = [f"<svg xmlns='http://www.w3.org/2000/svg' width='{width}' height='{height}'>"]
svg.append(f" <rect width='100%' height='100%' fill='{background}' />")
if polygons:
for i, poly in enumerate(polygons):
# small inset to avoid overlay issues
svg.append(polygon_to_svg(poly, width=width, height=height, label=str(i)))
if meshes:
for i, (verts, tris) in enumerate(meshes):
svg.append(mesh_to_svg(verts, tris, width=width, height=height, show_triangle_labels=False))
svg.append("</svg>")
return "\n".join(svg)
def write_svg(path: str, svg_text: str) -> None:
"""Write the SVG string to disk, creating parent directories if needed."""
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf8") as f:
f.write(svg_text)
def mesh_to_json(vertices: Iterable[Point], triangles: Iterable[Triangle]) -> str:
payload = {"vertices": [list(v) for v in vertices], "triangles": [list(t) for t in triangles]}
return json.dumps(payload)