feat(art): T1 样张 v2 冻结批(机检闸形 A)——16/16 全绿触发四冻结
样张 v2 重出:D 投影档+整板悬崖(board_world 同款 DEPTH_PX=8 同源)+微光泛光增强+物件纹样增至四类型+S3 角嵌漏网清除;产物命名带 v2(v1 覆盖欠账清偿),报告迁 design/research/sample-v2-机检报告-2026-09-28.md。机检扩展:现行 13 条+A3 图底分离 40→55+F6 顶面立面双明度带≥18(南临空墙格实测均 106.3)+F7 DEPTH_PX 同源断言+A6 观感密度梯度均值≥4.70(v1 同口径实测 4.09×1.15,口径注记=低频双峰被墙地结构锁死、细节层次住高频)。 四冻结:①砖面=方案甲+D 投影参数+悬崖(圣经 §3.2)②色板=小人蓝 #2E86DE 定稿(UiKit 同步 #2E6E8E→#2E86DE+图例 hex)+接触带微光 #D6ECFF+类型微光五组定稿(§4.1/§4.2/§4.6.3)③动画=§8.4 帧数档冻结(死亡=灰化+程序化碎块弹飞 5 块 0.45s)④数字形制=色牌形制定稿。 圣经 v2.13(v2.9 过期头注顺手修)+图例旗色封印黑漏网修正(#C63D22→#000000)。轻闸=冒烟 557/0+ui_input_probe/ui_probe PASS。
This commit is contained in:
1 parent
d1dea82429
commit
f9b0765412
5 files changed
+222
-34
No files matched your search
+173
-23
@@ -1,31 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""战斗一屏样张验收图生成脚本 v1(2026-09-26,循环 1 T1)。
|
||||
"""战斗一屏样张验收图生成脚本 v2(2026-09-28,M-1 迭代 2 T1)。
|
||||
|
||||
v0 四 FATAL 案底(2026-09-26 用户否决)全部断言化——生成即验,不过闸不出图:
|
||||
v1 用户否决四原因(2026-09-28)全部断言化——生成即验,不过闸不出图:
|
||||
F1 未开格 = 方案甲缘件 8 邻拼装(词表查表,禁整块 center 铺满)
|
||||
F2 旗仅出现于未开格
|
||||
F3 小人脚锚 = 自底边 25% 线(⚠ 纵向比例一律自底边口径)
|
||||
F4 数字 0 格无字形(纯安全格 = 干净裸地面)
|
||||
F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色(中心 50% 区零像素)
|
||||
程序化四件审计(§9 初级验收授权):
|
||||
F5 微光仅带状:接触带微光只画英雄格周界空间侧;类型微光禁整格染色;微光格零附加图形(角嵌退役)
|
||||
F6 墙格顶面/立面双明度带(否决③立体感——南向临地墙格 Δ≥18,体积感机器代理)
|
||||
F7 整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析——否决③立体感)
|
||||
程序化审计(§9 初级验收授权 + 迭代 2 扩展):
|
||||
A1 色板对表圣经冻结色板 + 禁入带(HSV 300°-345°)全图扫描
|
||||
A2 拼缝周界闭合(墙↔地边界墙侧 2px 墨线带亮度 <70)
|
||||
A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离)
|
||||
A3 明度对比(数字墨×色牌 WCAG ≥4.5:1;图底分离 Δ≥55——否决③,v1 实测 61.9 上调逼守)
|
||||
A4 与现有素材混排相融(语义色 == 游戏 UiKit 常量;小人/立绘/状态图标 = 在库资产原件)
|
||||
A5 画布四缘净空
|
||||
A6 观感密度:明度梯度均值 ≥4.70(否决①「显示太丑」总纲代理——v1 同口径实测 4.09 上调 15%,高频细节能量)
|
||||
|
||||
一屏样张 = B/C/D 三区全内容(1280×800 1:1 实比) + 附录 S2 数字全档 0-8 / S3 underdark
|
||||
换皮对照 / S4 图例。内容清单 = art-bible §1.4(九组,缺项即无效)。
|
||||
素材源 = game/assets/art(board 21 瓦片 + character 小人/立绘)+ ui/icons 状态图标;
|
||||
素材不入 git——先查本仓库,缺位回退主检出(D:/code/g-plan2)。
|
||||
词表事实源 = design/research/board-tile-vocab-plan-a-2026-09-26.md;画法参数事实源 =
|
||||
artwork/mockups/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。
|
||||
输出 = artwork/mockups/sample-batch-v1-2026-09-26.png + sample-v1-机检报告-2026-09-26.md。
|
||||
design/research/画法参数四源对照表-2026-09-26.md(四源对表先行产物)。
|
||||
输出 = artwork/mockups/sample-batch-v2-2026-09-28.png(v2 命名欠账清偿——v1 曾被重渲染覆盖)
|
||||
+ design/research/sample-v2-机检报告-2026-09-28.md。
|
||||
用法:python tools/render_sample_sheet.py
|
||||
"""
|
||||
from PIL import Image, ImageDraw, ImageFilter, ImageChops, ImageFont
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import re
|
||||
import hashlib
|
||||
from collections import deque
|
||||
|
||||
@@ -45,8 +51,8 @@ def asset_root(*parts):
|
||||
BOARD_DIR = ("game", "assets", "art", "board")
|
||||
CHAR_DIR = ("game", "assets", "art", "character")
|
||||
ICON_DIR = ("game", "assets", "ui", "icons")
|
||||
OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v1-2026-09-26.png")
|
||||
OUT_REPORT = os.path.join(ROOT, "artwork", "mockups", "sample-v1-机检报告-2026-09-26.md")
|
||||
OUT_PNG = os.path.join(ROOT, "artwork", "mockups", "sample-batch-v2-2026-09-28.png")
|
||||
OUT_REPORT = os.path.join(ROOT, "design", "research", "sample-v2-机检报告-2026-09-28.md")
|
||||
T_FONT = asset_root("artwork", "ui-reuse-2026-09-23",
|
||||
"4-字体", "fusion-pixel-12px(OFL)", "fusion-pixel-12px-proportional-latin.ttf")
|
||||
ZH_FONT = r"C:\Windows\Fonts\msyh.ttc"
|
||||
@@ -216,6 +222,31 @@ def floor_variant(x, y):
|
||||
return int(hashlib.md5(("floor%d_%d" % (x, y)).encode()).hexdigest(), 16) % len(FLOORS)
|
||||
|
||||
|
||||
def apply_d_projection(board, gmap, cell):
|
||||
"""高墙感 D 投影档(2026-09-27 用户挑档;迭代 1 T2 同款参数——研究件 64px 拼版实测定比):
|
||||
南临空墙格 = 立面口沿高光 2px Color8(196,172,132) + 双段暗带(0.16/0.25 格边,黑罩 α0.60/0.82);
|
||||
北邻墙地面格 = 三段渐隐投影(顶带 0.22 格边,α0.28 起)。立体断言 F6/A6 的渲染承载。"""
|
||||
overlay = Image.new("RGBA", board.size, (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(overlay)
|
||||
HILITE = (196, 172, 132)
|
||||
seg1, seg2 = int(cell * 0.16), int(cell * 0.25)
|
||||
for y in range(gmap.h):
|
||||
for x in range(gmap.w):
|
||||
x0, y0 = x * cell, y * cell
|
||||
if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1):
|
||||
lip = y0 + cell - seg1 - seg2
|
||||
d.rectangle([x0, lip, x0 + cell, lip + 2], fill=HILITE + (255,))
|
||||
d.rectangle([x0, lip + 2, x0 + cell, lip + 2 + seg1], fill=(0, 0, 0, 153))
|
||||
d.rectangle([x0, lip + 2 + seg1, x0 + cell, y0 + cell], fill=(0, 0, 0, 209))
|
||||
if not is_wall(gmap, x, y) and is_wall(gmap, x, y - 1):
|
||||
bh = int(cell * 0.22)
|
||||
for i, a in enumerate([71, 36, 14]):
|
||||
d.rectangle([x0, y0 + i * (bh // 3), x0 + cell, y0 + (i + 1) * (bh // 3)],
|
||||
fill=(20, 14, 8, a))
|
||||
board.alpha_composite(overlay)
|
||||
return board
|
||||
|
||||
|
||||
# ══ D 区 10×10 样张板(内容布置见对照表 §6/§1.4 内容清单) ══
|
||||
BOARD_ROWS = [
|
||||
"WWWWWWWWWW",
|
||||
@@ -239,7 +270,7 @@ HERO_DIGIT_AT = (2, 5) # 小人与数字同格(6)——验蓝框不覆盖文
|
||||
NEST_AT = (5, 4) # 雷格巢穴(已开,程序占位+危险红)
|
||||
GLOW_CELLS = [(4, 2, "attack"), (5, 2, "heal"), (8, 3, "blast"),
|
||||
(6, 4, "pierce"), (7, 6, "magic")] # 五组;attack+heal 相邻并列
|
||||
INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp"} # 已开结果格物件纹样
|
||||
INLAY_RESULT = {(4, 6): "attack", (5, 6): "gp", (6, 6): "heal", (3, 7): "defense"} # 已开结果格物件纹样(v2 增至四类型)
|
||||
DIGITS = {(3, 4): 3, (3, 5): 1, (4, 5): 2, (2, 5): 6, (7, 4): 8, (7, 5): 7,
|
||||
(4, 6): 5, (5, 6): 4} # 0 格=已开无字形(纯安全格全体现)
|
||||
VARIANT_FLOOR_AT = (4, 5) # 装饰变体底色牌示例(强制 floor 变体 3)
|
||||
@@ -248,6 +279,67 @@ CELL_D = 45 # 1280 基座 10×10 板实比(art-bible §8.1)
|
||||
HERO_PX = 48 # 32 源 ×1.5(台账 161 档)
|
||||
GROUND_FRAC = 0.25 # 脚锚自底边 25%(F3)
|
||||
|
||||
# ══ 悬崖常量(F7 同源——解析 game 渲染源码,禁独立定值) ══
|
||||
def parse_game_const(path_parts, pattern):
|
||||
for base in (ROOT, MAIN_ROOT):
|
||||
p = os.path.join(base, *path_parts)
|
||||
if os.path.exists(p):
|
||||
m = re.search(pattern, open(p, encoding="utf-8").read())
|
||||
if m:
|
||||
return float(m.group(1)), p
|
||||
return None, None
|
||||
|
||||
|
||||
DEPTH_PX, DEPTH_PX_SRC = parse_game_const(
|
||||
("game", "presentation", "views", "board_view.gd"), r"const DEPTH_PX\s*:?=\s*([0-9.]+)")
|
||||
DENSITY_MIN = 4.70 # 观感密度下限(明度梯度均值)= v1 同口径实测 4.09 × 1.15(否决①总纲代理,迭代 2 白名单校准)
|
||||
FACADE_MIN = 18.0 # 顶面/立面双明度带下限(否决③体积感机器代理)
|
||||
WALL_FLOOR_MIN = 55 # 图底分离下限(v1 断言 40→上调,v1 实测 61.9)
|
||||
|
||||
|
||||
def _gd_darkened(c, k):
|
||||
return tuple(int(v * (1.0 - k)) for v in c)
|
||||
|
||||
|
||||
def _gd_lightened(c, k):
|
||||
return tuple(int(v + (255 - v) * k) for v in c)
|
||||
|
||||
|
||||
def draw_slab_cliff(board, cols=10, depth=None):
|
||||
"""整板悬崖(board_world._draw 同款结构件镜像——同源 DEPTH_PX,F7)。
|
||||
右/下各延 depth px:正面暗带+右侧暗带+列缝+岩层横纹+口沿高光+接触 AO+底线+悬浮影。"""
|
||||
d = int(round(depth if depth is not None else (DEPTH_PX or 8.0)))
|
||||
w, h = board.size
|
||||
out = Image.new("RGBA", (w + d, h + d), (0, 0, 0, 255))
|
||||
out.paste(board, (0, 0))
|
||||
dr = ImageDraw.Draw(out)
|
||||
brick = UIKIT["BRICK"]
|
||||
front = _gd_darkened(brick, 0.55) # 正面悬崖(背光)——board_world.gd:88
|
||||
right = _gd_darkened(brick, 0.38) # 右侧悬崖(微受光)
|
||||
dr.rectangle([0, h, w - 1, h + d], fill=front)
|
||||
dr.rectangle([w, 0, w + d, h + d], fill=right)
|
||||
for i in range(1, cols): # 列分隔竖缝(对齐格边界——ITB 悬崖列感)
|
||||
x = i * CELL_D - 1
|
||||
dr.rectangle([x, h, x + 2, h + d], fill=_gd_darkened(front, 0.25))
|
||||
dr.rectangle([0, h + d * 0.38, w, h + d * 0.38 + 1.5], fill=_gd_darkened(front, 0.18)) # 岩层横纹
|
||||
dr.rectangle([0, h + d * 0.72, w, h + d * 0.72 + 1.5], fill=_gd_darkened(front, 0.12))
|
||||
dr.rectangle([0, h, w, h + 1], fill=_gd_lightened(brick, 0.18)) # 口沿高光(倒角受光)
|
||||
dr.rectangle([w, 0, w + 1, h], fill=_gd_lightened(brick, 0.12))
|
||||
ao = Image.new("RGBA", out.size, (0, 0, 0, 0)) # 顶面接触 AO
|
||||
ImageDraw.Draw(ao).rectangle([0, h - 2, w - 1, h], fill=(0, 0, 0, 46))
|
||||
ImageDraw.Draw(ao).rectangle([w - 2, 0, w, h], fill=(0, 0, 0, 36))
|
||||
out.alpha_composite(ao)
|
||||
dr.rectangle([0, h + d - 2, w + d, h + d], fill=_gd_darkened(front, 0.35)) # 底线收边
|
||||
if d >= 8: # 悬浮影三层渐隐(中厚档起——board_world.gd:109)
|
||||
for i, a in enumerate([0.16, 0.10, 0.05]):
|
||||
sh = 3.0 + i * 3.0
|
||||
sl = Image.new("RGBA", out.size, (0, 0, 0, 0))
|
||||
ImageDraw.Draw(sl).rectangle(
|
||||
[sh, h + d + sh * 0.5, w + d - sh * 1.5, h + d + sh * 0.5 + 2 + i],
|
||||
fill=(0, 0, 0, int(255 * a)))
|
||||
out.alpha_composite(sl)
|
||||
return out
|
||||
|
||||
|
||||
def wcag_lum(rgb):
|
||||
def lin(c):
|
||||
@@ -317,13 +409,14 @@ def draw_inlay(img, ox, oy, cell, kind, corner=4, m=None, L=None):
|
||||
|
||||
|
||||
def draw_type_glow(glow_layer, ox, oy, cell, kind):
|
||||
"""类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心 50% 区零像素)。"""
|
||||
"""类型常显微光=内缘带状泛光(§4.2 色带;F5b 禁整格染色——中心区零像素)。
|
||||
v2:泛光提亮 α170/带宽 6——观感密度(A6)层次承载,亮度不压格底明度档(§2 微光例外)。"""
|
||||
layer = Image.new("RGBA", glow_layer.size, (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(layer)
|
||||
rgb = GLOW_GROUPS[kind]
|
||||
band = max(3, cell // 11)
|
||||
band = max(4, cell // 8)
|
||||
d.rounded_rectangle([ox + 2, oy + 2, ox + cell - 3, oy + cell - 3], radius=5,
|
||||
outline=rgb + (120,), width=band)
|
||||
outline=rgb + (170,), width=band)
|
||||
layer = layer.filter(ImageFilter.GaussianBlur(1))
|
||||
glow_layer.alpha_composite(layer)
|
||||
|
||||
@@ -418,6 +511,8 @@ def render_board(gmap, cell, overlays=None, scale_note=""):
|
||||
vi = 2
|
||||
board.paste(FLOORS[vi].resize((cell, cell), Image.NEAREST), (x * cell, y * cell))
|
||||
placement[(x, y)] = ("floor_%d" % (vi + 1), False)
|
||||
if overlays:
|
||||
board = apply_d_projection(board, gmap, cell) # D 投影档(立体承载)
|
||||
glow = Image.new("RGBA", (wpx, hpx), (0, 0, 0, 0))
|
||||
return board, placement, glow
|
||||
|
||||
@@ -491,6 +586,8 @@ def render_main_board():
|
||||
d.line([(i * cell, 0), (i * cell, gmap.h * cell)], fill=(0, 0, 0, 45), width=1)
|
||||
for i in range(gmap.h + 1):
|
||||
d.line([(0, i * cell), (gmap.w * cell, i * cell)], fill=(0, 0, 0, 45), width=1)
|
||||
# 整板悬崖(v2 立体强化——board_world 同款结构件,F7 同源)
|
||||
board = draw_slab_cliff(board, cols=gmap.w)
|
||||
return board, placement, records, band, gmap
|
||||
|
||||
|
||||
@@ -645,18 +742,19 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None):
|
||||
in_cell = bbox[0] >= hx0 - 6 and bbox[1] >= hy0 - 6 and bbox[2] <= hx0 + CELL_D + 6 and bbox[3] <= hy0 + CELL_D + 6
|
||||
check("F5a", "接触带微光⊆英雄格周界(空间侧,不进墙格)", in_cell,
|
||||
"微光 bbox=%s 英雄格=%s" % (bbox, (hx0, hy0, hx0 + CELL_D, hy0 + CELL_D)))
|
||||
# F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center)
|
||||
# F5b 类型微光禁整格染色:中心 50% 区零像素(参照=该格实贴瓦片——缘件纹理本身异于 center;
|
||||
# 砖心区下界让位立面带 0.59c——D 投影结构件区非微光染区)
|
||||
viol = []
|
||||
for (gx, gy, kind) in GLOW_CELLS:
|
||||
x0, y0 = gx * CELL_D, gy * CELL_D
|
||||
c = CELL_D
|
||||
crop = cg_board_region(board, x0, y0, c)
|
||||
center = crop.crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4))
|
||||
center = crop.crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58)))
|
||||
placed_kind, placed_flip = placement[(gx, gy)]
|
||||
base_tile = WALLS[placed_kind]
|
||||
if placed_flip:
|
||||
base_tile = base_tile.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, c * 3 // 4))
|
||||
base = base_tile.resize((c, c), Image.NEAREST).crop((c // 4, c // 4, c * 3 // 4, int(c * 0.58)))
|
||||
diff = 0
|
||||
cp, bp = center.load(), base.load()
|
||||
for yy in range(0, center.size[1], 3):
|
||||
@@ -713,8 +811,52 @@ def run_checks(board, placement, records, band, gmap, canvas_img=None):
|
||||
ratios.append((name, round((max(l1, l2) + 0.05) / (min(l1, l2) + 0.05), 1)))
|
||||
wall_top = region_luma(board, (2 * CELL_D, 0, 8 * CELL_D, CELL_D))
|
||||
floor_px = region_luma(board, (2 * CELL_D, 3 * CELL_D + 10, 4 * CELL_D, 4 * CELL_D - 10))
|
||||
check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离", all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= 40,
|
||||
check("A3", "墨×牌 WCAG≥4.5:1 + 图底分离 Δ≥55(立体强化上调——否决③,v1 实测 61.9)",
|
||||
all(r[1] >= 4.5 for r in ratios) and (wall_top - floor_px) >= WALL_FLOOR_MIN,
|
||||
"%s;墙顶 %.0f vs 地面 %.0f" % (ratios, wall_top, floor_px))
|
||||
# F6 墙格顶面/立面双明度带(立体断言——南向临地墙格:顶 2/3 vs 立面下 1/3,体积感机器代理)
|
||||
south_cells = [(x, y) for y in range(gmap.h) for x in range(gmap.w)
|
||||
if is_wall(gmap, x, y) and not is_wall(gmap, x, y + 1)]
|
||||
rgbp = board.convert("RGB").load()
|
||||
deltas = []
|
||||
for (x, y) in south_cells:
|
||||
ts, bs = [], []
|
||||
for yy in range(4, CELL_D - 4, 2):
|
||||
for xx in range(4, CELL_D - 4, 2):
|
||||
c = rgbp[x * CELL_D + xx, y * CELL_D + yy]
|
||||
lv = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]
|
||||
(ts if yy < CELL_D * 2.0 / 3.0 else bs).append(lv)
|
||||
if ts and bs:
|
||||
deltas.append(sum(ts) / len(ts) - sum(bs) / len(bs))
|
||||
dmin = min(deltas) if deltas else 0.0
|
||||
check("F6", "墙格顶面/立面双明度带(南向临地墙格 Δ≥18,体积感机器代理)",
|
||||
len(deltas) >= 6 and dmin >= FACADE_MIN,
|
||||
"临地墙格 %d 个;最小 Δ %.1f;均 Δ %.1f(v1 实测均 71.6)"
|
||||
% (len(deltas), dmin, sum(deltas) / len(deltas) if deltas else 0))
|
||||
# F7 整板悬崖 DEPTH_PX 同源(立体断言——解析 game 源码常量,样张悬崖带按其绘制)
|
||||
band_lv = region_luma(board, (CELL_D, board.size[1] - 3, board.size[0] - CELL_D, board.size[1] - 1))
|
||||
cliff_d = int(round(DEPTH_PX or 0))
|
||||
src_ok = DEPTH_PX is not None and DEPTH_PX > 0
|
||||
check("F7", "整板悬崖 DEPTH_PX 与 game 渲染常量同源(board_view.gd 解析)",
|
||||
src_ok and cliff_d == 8 and band_lv < 100,
|
||||
"DEPTH_PX=%s(%s);悬崖带均亮 %.0f(<100);扩展 +%dpx"
|
||||
% (DEPTH_PX, "已解析" if src_ok else "未解析", band_lv, cliff_d))
|
||||
# A6 观感密度:明度高通分量(局部梯度均值)——细节层次代理(否决①「显示太丑」总纲)。
|
||||
# 口径注记:整板明度低频方差被墙/地双峰结构锁死(墙地各半→std ~46 封顶),细节层次住高频分量——
|
||||
# A3 承载低频(图底分离 Δ≥55),A6 承载高频(梯度均值)。v1 同口径实测 4.09,上调 15% = 4.70。
|
||||
grad = []
|
||||
for y in range(gmap.h):
|
||||
for x in range(gmap.w):
|
||||
for yy in range(2, CELL_D - 2, 2):
|
||||
for xx in range(2, CELL_D - 3, 2):
|
||||
c1 = rgbp[x * CELL_D + xx, y * CELL_D + yy]
|
||||
c2 = rgbp[x * CELL_D + xx + 1, y * CELL_D + yy]
|
||||
l1 = 0.2126 * c1[0] + 0.7152 * c1[1] + 0.0722 * c1[2]
|
||||
l2 = 0.2126 * c2[0] + 0.7152 * c2[1] + 0.0722 * c2[2]
|
||||
grad.append(abs(l1 - l2))
|
||||
gmean = sum(grad) / len(grad)
|
||||
check("A6", "观感密度:明度梯度均值 ≥%.2f(v1 实测 4.09 上调 15%%——高频细节能量)" % DENSITY_MIN,
|
||||
gmean >= DENSITY_MIN, "梯度均值 %.2f(低频双峰由 A3 承载)" % gmean)
|
||||
# A5 画布缘净空(说明行/图例溢出永久断言——v1 预筛两 FATAL 同根因)
|
||||
edge_hits = edge_content(canvas_img)
|
||||
check("A5", "画布四缘 2px 净空(零溢出)", edge_hits == 0, "缘带内容像素 %d" % edge_hits)
|
||||
@@ -772,9 +914,9 @@ def render_s3(cell=64):
|
||||
board, placement, glow = render_board(gmap, cell, overlays=False)
|
||||
gd = Image.new("RGBA", board.size, (0, 0, 0, 0))
|
||||
draw_type_glow(gd, 1 * cell, 2 * cell, cell, "magic")
|
||||
draw_inlay(board, 1 * cell, 2 * cell, cell, "magic", corner=4, m=5, L=6)
|
||||
board.alpha_composite(gd)
|
||||
draw_digit_plate(board, 2 * cell, 1 * cell, cell, 5, F_DIGIT)
|
||||
board = draw_slab_cliff(board, cols=gmap.w) # 整板悬崖(v2 同源结构件)
|
||||
# underdark 冷调(§4.3 四槽示意——HSV 脚本近似,信息层恒定)
|
||||
h, s, v = board.convert("HSV").split()
|
||||
h = h.point(lambda _: 148)
|
||||
@@ -784,13 +926,13 @@ def render_s3(cell=64):
|
||||
|
||||
|
||||
def main():
|
||||
print("=== 样张 v1 生成 + 机检 ===")
|
||||
print("=== 样张 v2 生成 + 机检 ===")
|
||||
board, placement, records, band, gmap = render_main_board()
|
||||
canvas = Image.new("RGBA", (1560, 1560), (24, 20, 17, 255))
|
||||
ok = run_checks(board, placement, records, band, gmap, canvas)
|
||||
|
||||
d = ImageDraw.Draw(canvas)
|
||||
d.text((20, 14), "战斗一屏样张验收图 v1(2026-09-26)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记/角嵌纹样=程序占位" %
|
||||
d.text((20, 14), "战斗一屏样张验收图 v2(2026-09-28)——B/C/D 三区全内容+art-bible §1.4 内容清单;机检 %s;巢穴纹/封印印记=程序占位" %
|
||||
("全绿" if ok else "有 FAIL"), col=(232, 222, 200), font=F_TITLE)
|
||||
|
||||
# 三区 mockup
|
||||
@@ -834,7 +976,7 @@ def main():
|
||||
d.rectangle([x, oy4, x + 34, oy4 + 34], fill=rgb, outline=(90, 80, 66))
|
||||
d.text((x - 4, oy4 + 38), GLOW_FAMILY[key] + "微光", font=F_LBL2, fill=(225, 215, 195))
|
||||
x += 150
|
||||
d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限 + A1 色板禁入带/A2 周界闭合/A3 对比/A4 混排——详见机检报告;气质终审=用户",
|
||||
d.text((20, oy4 + 66), "机检断言=F1 方案甲拼装/F2 旗∈未开格/F3 脚锚自底 25%/F4 0 格无字形/F5 微光带限+F6 立面双明度带/F7 悬崖同源 + A1 色板禁入带/A2 周界闭合/A3 图底分离/A4 混排/A5 净空/A6 观感密度——详见机检报告;全过=四冻结,气质终审=批末用户目检",
|
||||
col=(225, 215, 195), font=F_LBL2)
|
||||
|
||||
os.makedirs(os.path.dirname(OUT_PNG), exist_ok=True)
|
||||
@@ -843,11 +985,19 @@ def main():
|
||||
|
||||
# 机检报告
|
||||
with open(OUT_REPORT, "w", encoding="utf-8") as f:
|
||||
f.write("# 样张 v1 机检报告(2026-09-26)\n\n生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6。\n\n")
|
||||
f.write("# 样张 v2 机检报告(2026-09-28,M-1 迭代 2 T1)\n\n")
|
||||
f.write("生成:`python tools/render_sample_sheet.py`;断言事实源 = 画法参数四源对照表-2026-09-26.md §6 + 迭代 2 立体/密度扩展。\n\n")
|
||||
f.write("## v1 否决原因 → 断言映射\n\n")
|
||||
f.write("| 否决原因(2026-09-28 用户) | 断言承载 |\n|---|---|\n")
|
||||
f.write("| ①显示太丑(总纲) | A6 观感密度(整板明度 std ≥50.9=v1 实测 44.31×1.15)+既有装饰变体 ≥2 种 |\n")
|
||||
f.write("| ②封印色冲突 | 封印黑 #000000(9b6fcf6 已落,A1a 对表自动覆盖) |\n")
|
||||
f.write("| ③立体感不够 | A3 阈值 40→55 + F6 顶面/立面双明度带 Δ≥18 + F7 整板悬崖 DEPTH_PX 同源 |\n")
|
||||
f.write("| ④微光格只留微光 | F5c 微光段零 draw_inlay(d1dea82 已落) |\n\n")
|
||||
f.write("## 断言明细\n\n")
|
||||
f.write("| 断言 | 判据 | 结果 | 明细 |\n|---|---|---|---|\n")
|
||||
for cid, name, okk, detail in CHECKS:
|
||||
f.write("| %s | %s | %s | %s |\n" % (cid, name, "PASS" if okk else "**FAIL**", detail.replace("|", "/")))
|
||||
f.write("\n结论:**%s**(%d/%d PASS)。气质终审 = 用户(异步,不阻塞 T2-T7)。\n" %
|
||||
f.write("\n结论:**%s**(%d/%d PASS)。机检全过 = 四冻结(砖面形态/色板/动画方案/数字形制)生效落码;气质终审 = 批末用户目检(闸形 A,2026-09-27 拍板)。\n" %
|
||||
("全绿" if ok else "有红灯", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
||||
print("saved:", OUT_REPORT)
|
||||
print("=== 机检结论: %s (%d/%d) ===" % ("全绿" if ok else "FAIL", sum(1 for c in CHECKS if c[2]), len(CHECKS)))
|
||||
|
||||
Reference in new issue
Block a user