tools: Löcher in der Gesamtansicht korrekt zusammensetzen
Ein Loch mit Wasserfarbe zu übermalen war falsch — der Innenhof eines Blocks liegt auf Stadtfläche, nicht im Wasser. Die Fläche wird jetzt über eine Maske eingesetzt, sodass durch die Löcher die Ebene darunter bleibt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3003504151
commit
857121e5fb
1 file changed
+25
-10
+25
-10
@@ -30,17 +30,32 @@ def _to_px(ring, ox, oy):
|
||||
return [((x + ox - SLICE["x0"]) * SCALE, (SLICE["y1"] - (oy - z)) * SCALE) for x, z in ring]
|
||||
|
||||
|
||||
def _draw(draw, rings, ox, oy, fill, outline=None):
|
||||
"""Außenring füllen, Löcher wieder mit Wasserfarbe ausstanzen."""
|
||||
draw.polygon(_to_px(rings[0], ox, oy), fill=fill, outline=outline)
|
||||
def _draw(img, rings, ox, oy, fill, outline=None):
|
||||
"""Fläche über eine Maske einsetzen, damit durch die Löcher die Ebene darunter bleibt.
|
||||
|
||||
Ein Loch mit Wasserfarbe zu übermalen wäre falsch: der Innenhof eines Blocks liegt auf
|
||||
Stadtfläche, nicht im Wasser.
|
||||
"""
|
||||
outer = _to_px(rings[0], ox, oy)
|
||||
xs = [p[0] for p in outer]
|
||||
ys = [p[1] for p in outer]
|
||||
x0, y0 = int(min(xs)) - 1, int(min(ys)) - 1
|
||||
x1, y1 = int(max(xs)) + 2, int(max(ys)) + 2
|
||||
if x1 <= x0 or y1 <= y0:
|
||||
return
|
||||
mask = Image.new("L", (x1 - x0, y1 - y0), 0)
|
||||
md = ImageDraw.Draw(mask)
|
||||
md.polygon([(x - x0, y - y0) for x, y in outer], fill=255, outline=255)
|
||||
for hole in rings[1:]:
|
||||
draw.polygon(_to_px(hole, ox, oy), fill=WATER)
|
||||
md.polygon([(x - x0, y - y0) for x, y in _to_px(hole, ox, oy)], fill=0)
|
||||
img.paste(fill, (x0, y0), mask)
|
||||
if outline:
|
||||
ImageDraw.Draw(img).polygon(outer, fill=None, outline=outline)
|
||||
|
||||
|
||||
def render(out_dir: Path, dst: Path) -> dict:
|
||||
size = int((SLICE["x1"] - SLICE["x0"]) * SCALE)
|
||||
img = Image.new("RGB", (size, size), WATER)
|
||||
draw = ImageDraw.Draw(img)
|
||||
chunks = [json.loads(f.read_text()) for f in sorted(out_dir.glob("chunk_*.json"))]
|
||||
stats = {"land": 0, "strassen": 0, "bruecken": 0, "gebaeude": 0, "palmen": 0}
|
||||
|
||||
@@ -48,27 +63,27 @@ def render(out_dir: Path, dst: Path) -> dict:
|
||||
for chunk in chunks:
|
||||
ox, oy = chunk["origin"][0], -chunk["origin"][1]
|
||||
for rings in chunk["land"]:
|
||||
_draw(draw, rings, ox, oy, LAND)
|
||||
_draw(img, rings, ox, oy, LAND)
|
||||
stats["land"] += 1
|
||||
for chunk in chunks:
|
||||
ox, oy = chunk["origin"][0], -chunk["origin"][1]
|
||||
for surface in chunk["surfaces"]:
|
||||
_draw(draw, surface["poly"], ox, oy, SURFACE[surface["kind"]])
|
||||
_draw(img, surface["poly"], ox, oy, SURFACE[surface["kind"]])
|
||||
for chunk in chunks:
|
||||
ox, oy = chunk["origin"][0], -chunk["origin"][1]
|
||||
for road in chunk["roads"]:
|
||||
_draw(draw, road["poly"], ox, oy, BRIDGE if road["bridge"] else ROAD)
|
||||
_draw(img, road["poly"], ox, oy, BRIDGE if road["bridge"] else ROAD)
|
||||
stats["bruecken" if road["bridge"] else "strassen"] += 1
|
||||
for chunk in chunks:
|
||||
ox, oy = chunk["origin"][0], -chunk["origin"][1]
|
||||
for building in chunk["buildings"]:
|
||||
t = min(1.0, building["height"] / TALL)
|
||||
shade = (int(70 + 170 * t), int(70 + 120 * t), int(80 + 60 * t))
|
||||
_draw(draw, building["poly"], ox, oy, shade, outline=(30, 30, 30))
|
||||
_draw(img, building["poly"], ox, oy, shade, outline=(30, 30, 30))
|
||||
stats["gebaeude"] += 1
|
||||
for x, z in chunk["palms"]:
|
||||
px, py = _to_px([(x, z)], ox, oy)[0]
|
||||
draw.ellipse([px - 1.6, py - 1.6, px + 1.6, py + 1.6], fill=PALM)
|
||||
ImageDraw.Draw(img).ellipse([px - 1.6, py - 1.6, px + 1.6, py + 1.6], fill=PALM)
|
||||
stats["palmen"] += 1
|
||||
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
Reference in new issue
Block a user