summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-28 18:04:48 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-28 18:04:48 +0800
commit931b0acbc06b9d9c7e4e16ae8d714e14e840cae8 (patch)
treef81228b7cb7bb44228462373ae4cb7d370572398 /scripts
parentc59da29bba304152e3826ab869a421c7c75f780d (diff)
downloadliving-village-931b0acbc06b9d9c7e4e16ae8d714e14e840cae8.tar.gz
feat(p40): 乌篷船篷面实心拱形重绘与像素回归
- make-cc0-art.py 重绘 boat 篷面为实心拱形竹篷:拱线以下逐列实心填充, 连续脊线高光跨越两半在瓦片边界相接,肋骨/端盖/甲板阴影收边(原 1px 竖柱 栅栏式画法改为整片拱面) - 重生成 Assets/cc0-tiles.png(同参数逐字节稳定) - P40ArtTests 新增 BoatAwningIsASolidArchWithAContinuousRidge 像素级断言: 脊线无透明断列、拱下每列有实心竹色、左右两半拱顶同高 属 P40 整改的篷面实心 + 回归部分。
Diffstat (limited to 'scripts')
-rw-r--r--scripts/make-cc0-art.py120
1 files changed, 79 insertions, 41 deletions
diff --git a/scripts/make-cc0-art.py b/scripts/make-cc0-art.py
index 351c20a..3290fa0 100644
--- a/scripts/make-cc0-art.py
+++ b/scripts/make-cc0-art.py
@@ -235,6 +235,7 @@ PAVE_JOINT = (72, 72, 70, 255)
BAMBOO = (44, 58, 34, 255)
BAMBOO_HI = (86, 104, 62, 255)
BAMBOO_RIB = (28, 38, 22, 255)
+BAMBOO_DARK = (22, 30, 17, 255)
RED_LANTERN = (208, 58, 46, 255)
RED_LANTERN_HI = (238, 104, 84, 255)
RED_LANTERN_DARK = (150, 34, 28, 255)
@@ -284,54 +285,91 @@ def draw_bridge_end(c, ox):
def draw_boat(c, ox, right_half):
"""Wu Peng boat half (2 tiles wide): dark hull + tall curved bamboo awning.
- P40: the boat was scaled up to fill the 64x32 footprint so the wu peng (拱形
- 竹篷) reads at a glance from a bridge or bank. Each half is 32px wide; the
- awning is a tall elliptical bamboo arch spanning the middle of the 2-tile
- boat with visible ribs, and the hull is a solid dark band with an upturned
- bow/stern tip. The gunwale sits 8px down, leaving open water ripple above.
+ P40 fix: the awning is a *solid* arched span, not a row of 1px posts. For
+ every column across the awning the tile fills a horizontal band from the
+ arch top y(x) down to the deck line, so the silhouette reads as one filled
+ 拱形竹篷. The ridge (arch top plateau) is a continuous highlight band with no
+ transparent break. Each half is 32px wide; the right half mirrors the left,
+ so the two halves join into a single continuous arch with the ridge meeting
+ at the tile boundary (no broken arc).
+
+ Left half local profile y(x):
+ x < 1 : outside the awning (bow only)
+ x in [1,6) : rising arc 9,7,5,3,1 (stepped outer end)
+ x in [6,31]: ridge plateau y=1
+ The right half mirrors it (local lx -> 31-lx). The stepped end is capped with
+ one BAMBOO_DARK column; the hull has plank seams (y=14/22) and a waterline.
"""
c.fill(ox, 0, 32, 32, CLEAR)
- # --- Hull: solid dark band spanning the full cell, 8px down to 30px. ---
+ deck_y = 9
+
+ def top_at(x):
+ """Left-half local arch top row for column x, or None outside the awning."""
+ if x < 1:
+ return None
+ if x < 6:
+ # Rounded rise: 9,7,5,3,1 across x=1..5 so the ridge plateau starts at x=6.
+ return 9 - 2 * (x - 1)
+ return 1
+
+ # --- Hull: solid dark band, 8px down to 30px, with a raised bow/stern tip. ---
c.fill(ox, 8, 32, 22, BOAT_HULL)
c.fill(ox, 8, 32, 2, BOAT_HULL_DARK)
c.fill(ox, 28, 32, 2, BOAT_HULL_DARK)
- # --- Awning (wu peng): tall bamboo arch over the middle of the 2-tile boat. ---
- # Arc rises from the outer edge up to a high ridge, so both halves join into
- # one continuous arch. Left half: low at x=0, ridge near the inner (right) edge.
- arc = [
- (0, 12), (2, 9), (4, 6), (6, 4), (8, 3),
- (10, 2), (12, 1), (14, 1), (16, 1), (18, 1),
- (20, 1), (22, 1), (24, 2), (26, 3), (28, 4),
- (30, 5), (32, 6),
- ]
+ # Plank seams (y=14 / y=22) + waterline (bottom rows read as below the water).
+ c.hline(ox, 14, 32, BOAT_HULL_DARK)
+ c.hline(ox, 22, 32, BOAT_HULL_DARK)
+ c.hline(ox, 29, 32, BOAT_HULL_DARK)
+ c.hline(ox, 30, 32, BOAT_HULL_DARK)
if not right_half:
- # Left half: bow tip at the far left, rising; awning covers inner 20px.
- for i in range(24):
- half = 4 + i // 2
- c.fill(ox, 8 + i, min(32, half), 1, BOAT_HULL)
- c.fill(ox, 6, 4, 3, BOAT_HULL_DARK)
- c.fill(ox, 5, 2, 3, BOAT_HULL)
- for (x, top) in arc:
- c.fill(ox + x, top, 1, 32 - top, BAMBOO)
- c.fill(ox + 12, 1, 20, 2, BAMBOO_HI)
- for rib in (14, 18, 22, 26, 30):
- c.fill(ox + rib, 1, 1, 30, BAMBOO_RIB)
- c.fill(ox + 12, 30, 20, 2, BAMBOO_RIB)
- c.fill(ox + 12, 6, 2, 24, BAMBOO_HI)
+ c.fill(ox, 5, 3, 3, BOAT_HULL)
+ c.fill(ox, 6, 3, 2, BOAT_HULL_DARK)
+ awning_columns = list(range(1, 32))
+ rib_columns = [14, 20, 26]
+ cap_columns = [1] # stepped outer end of the awning
+
+ def local_top(lx):
+ return top_at(lx)
else:
- # Right half: stern tip at the far right, mirrored continuous hull.
- for i in range(24):
- half = 4 + i // 2
- c.fill(ox + 32 - min(32, half), 8 + i, min(32, half), 1, BOAT_HULL)
- c.fill(ox + 28, 6, 4, 3, BOAT_HULL_DARK)
- c.fill(ox + 30, 5, 2, 3, BOAT_HULL)
- for (x, top) in arc:
- c.fill(ox + 31 - x, top, 1, 32 - top, BAMBOO)
- c.fill(ox, 1, 20, 2, BAMBOO_HI)
- for rib in (1, 5, 9, 13, 17):
- c.fill(ox + rib, 1, 1, 30, BAMBOO_RIB)
- c.fill(ox, 30, 20, 2, BAMBOO_RIB)
- c.fill(ox + 18, 6, 2, 24, BAMBOO_HI)
+ c.fill(ox + 29, 5, 3, 3, BOAT_HULL)
+ c.fill(ox + 28, 6, 3, 2, BOAT_HULL_DARK)
+ awning_columns = list(range(0, 31))
+ rib_columns = [5, 11, 17]
+ cap_columns = [29] # mirrored stepped outer end
+
+ def local_top(lx):
+ return top_at(31 - lx)
+
+ # --- Solid arched span: each column fills from the arch line to the deck. ---
+ for lx in awning_columns:
+ t = local_top(lx)
+ if t is None:
+ continue
+ c.fill(ox + lx, t, 1, deck_y - t + 1, BAMBOO)
+ # Continuous ridge highlight: rows t..t+1 across the whole awning (no break).
+ for lx in awning_columns:
+ t = local_top(lx)
+ if t is None:
+ continue
+ c.fill(ox + lx, t, 1, min(2, deck_y - t + 1), BAMBOO_HI)
+ # Bamboo ribs below the ridge (texture only; ridge band stays continuous).
+ for lx in rib_columns:
+ t = local_top(lx)
+ if t is None or t + 2 > deck_y:
+ continue
+ c.fill(ox + lx, t + 2, 1, deck_y - t - 1, BAMBOO_RIB)
+ # Stepped end cap: one dark column closing the outer end of the awning.
+ for lx in cap_columns:
+ t = local_top(lx)
+ if t is None:
+ continue
+ c.fill(ox + lx, t, 1, deck_y - t + 1, BAMBOO_DARK)
+ # Deck shadow where the awning meets the hull.
+ for lx in awning_columns:
+ t = local_top(lx)
+ if t is None:
+ continue
+ c.set(ox + lx, deck_y, BAMBOO_RIB)
def draw_paving(c, ox):