Tiers drive physical traits and serial checks; guide item
Fans (1/2/3/3) and power connectors (6/8/8/8+8) follow the real chip's tier, so a fake shows its real tier physically. Serial numbers (brand prefix, tier code, factory code, four digits) are drawn on the sticker from card state; the label bar gets power-and-fans and serial rows, eight in total, with a framed panel. A graphics card appraisal guide item holds the rules. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9d19df407a
commit
7f458b0139
113 files changed
+227
-133
No files matched your search
+72
-34
@@ -137,13 +137,14 @@ def bracket(c):
|
||||
c.put(2, 27, 'h'); c.put(4, 27, 'd')
|
||||
|
||||
|
||||
def fan(c, cx, cy, accent, burnt=False):
|
||||
def fan(c, cx, cy, accent, burnt=False, R=7):
|
||||
"""风扇:深色风道里六片弯曲扇叶(前缘高光、叶面按受光渐变),外圈细边,轴心带品牌色点;光从左上来。"""
|
||||
import math
|
||||
for y in range(cy - 7, cy + 8):
|
||||
for x in range(cx - 7, cx + 8):
|
||||
k = 7.0 / R # 按半径折算到 R=7 的画法
|
||||
for y in range(cy - R, cy + R + 1):
|
||||
for x in range(cx - R, cx + R + 1):
|
||||
dx, dy = x - cx, y - cy
|
||||
r = math.hypot(dx, dy)
|
||||
r = math.hypot(dx, dy) * k
|
||||
if r > 7.4:
|
||||
continue
|
||||
ang = (math.atan2(dy, dx) / (2 * math.pi)) % 1.0
|
||||
@@ -164,7 +165,10 @@ def fan(c, cx, cy, accent, burnt=False):
|
||||
c.put(cx - 1, cy - 1, 'w')
|
||||
|
||||
|
||||
def gpu_front(accent, accent_dark, burnt=False):
|
||||
FAN_LAYOUT_SMALL = {1: ([(26, 13)], 9), 2: ([(17, 13), (34, 13)], 7), 3: ([(14, 13), (26, 13), (38, 13)], 5)}
|
||||
|
||||
|
||||
def gpu_front(accent, accent_dark, burnt=False, tier=1):
|
||||
"""显卡正面(48×32,3×2 格),略微俯视:石墨色散热罩带斜切角与品牌色斜条,两个七叶风扇,
|
||||
右端露出鳍片,顶沿露出铜热管和 8 针供电口,前沿是带元件的 PCB 和逐根画出的金手指。每格中心都不透明。"""
|
||||
c = Canvas(W, H)
|
||||
@@ -182,26 +186,33 @@ def gpu_front(accent, accent_dark, burnt=False):
|
||||
for y in range(TOP0 + 2, TOP1):
|
||||
c.put(x, y, 'g' if (x - (R - 4)) % 2 == 0 else 'k')
|
||||
c.rect(R - 5, TOP0 + 2, R - 5, TOP1 - 1, 'x')
|
||||
# 中间的筋条(两个风扇之间)与上下沿的槽
|
||||
for y in range(TOP0 + 3, TOP1 - 2):
|
||||
c.put(25, y, 'k'); c.put(26, y, 'x')
|
||||
count = (1, 2, 3, 3)[tier]
|
||||
fans, fr = FAN_LAYOUT_SMALL[count]
|
||||
# 风扇之间的筋条
|
||||
for a, b in zip(fans, fans[1:]):
|
||||
mx = (a[0] + b[0]) // 2
|
||||
for y in range(TOP0 + 3, TOP1 - 2):
|
||||
c.put(mx, y, 'k'); c.put(mx + 1, y, 'x')
|
||||
# 品牌色斜条(从前沿向左上延伸)与前沿色条
|
||||
for x in range(L + 1, R - 5):
|
||||
c.put(x, TOP1 - 1, accent if x % 9 else accent_dark)
|
||||
c.put(x, TOP1, accent_dark)
|
||||
for cx, cy in FANS:
|
||||
fan(c, cx, cy, accent)
|
||||
for dx, dy in ((-7, -7), (7, -7), (-7, 7), (7, 7)): # 风扇四角的固定孔
|
||||
c.put(cx + dx, cy + dy, 'n' if dx + dy else 'k')
|
||||
for cx, cy in fans:
|
||||
fan(c, cx, cy, accent, R=fr)
|
||||
if count < 3:
|
||||
for dx, dy in ((-7, -7), (7, -7), (-7, 7), (7, 7)): # 风扇四角的固定孔
|
||||
c.put(cx + dx, cy + dy, 'n' if dx + dy else 'k')
|
||||
# 顶沿:铜热管端头 + 8 针供电口(带针脚)
|
||||
for x0 in (L + 6, L + 8, L + 10, L + 12):
|
||||
c.put(x0, TOP0 - 1, 'p'); c.put(x0 + 1, TOP0 - 1, 'q')
|
||||
for x in range(L + 18, L + 28):
|
||||
c.put(x, TOP0 - 1, 'n')
|
||||
for x in range(L + 18, L + 28, 2):
|
||||
c.put(x, TOP0 - 2, 'k')
|
||||
for x in range(L + 19, L + 28, 2):
|
||||
c.put(x, TOP0 - 2, 'Y')
|
||||
# 供电口:低端 6 针;中端、高端 8 针;旗舰两个 8 针(每针占两像素)
|
||||
for left, pins in (((L + 22, 6),) if tier == 0 else ((L + 18, 8),) if tier < 3 else ((L + 8, 8), (L + 25, 8))):
|
||||
for x in range(left, left + pins * 2):
|
||||
c.put(x, TOP0 - 1, 'n')
|
||||
for x in range(left, left + pins * 2, 2):
|
||||
c.put(x, TOP0 - 2, 'k')
|
||||
for x in range(left + 1, left + pins * 2, 2):
|
||||
c.put(x, TOP0 - 2, 'Y')
|
||||
# 前沿:PCB(绿)带元件,之后金手指
|
||||
for x in range(L, R + 1):
|
||||
c.put(x, TOP1 + 1, 'G' if x % 7 else 'g2'); c.put(x, TOP1 + 2, 'g2')
|
||||
@@ -221,7 +232,7 @@ def gpu_front(accent, accent_dark, burnt=False):
|
||||
c.put(x, y, 'c')
|
||||
for x in range(L + 1, R - 5):
|
||||
c.put(x, TOP1 - 1, 'e')
|
||||
for cx, cy in FANS:
|
||||
for cx, cy in fans:
|
||||
for dx, dy in ((0, 0), (1, 1), (-1, 1), (2, -2), (-2, -2)):
|
||||
c.put(cx + dx, cy + dy, 'c')
|
||||
outline(c)
|
||||
@@ -298,10 +309,10 @@ def overlay():
|
||||
return Canvas(W, H)
|
||||
|
||||
|
||||
def ov_fan(kind):
|
||||
def ov_fan(kind, count=2):
|
||||
"""风扇叠加:1 积灰(叶片上一层灰),2 叶片缺角(边缘缺一块并露出黑底)。"""
|
||||
c = overlay()
|
||||
for cx, cy in FANS:
|
||||
for cx, cy in FAN_LAYOUT_SMALL[count][0]:
|
||||
if kind == 1:
|
||||
for dx, dy in ((-4, -2), (-2, -4), (3, -3), (4, 1), (-3, 3), (2, 4), (-5, 0), (0, 5), (1, -5), (-1, 2), (5, -1), (3, 2)):
|
||||
c.put(cx + dx, cy + dy, 'l')
|
||||
@@ -363,7 +374,7 @@ def ov_sticker(kind):
|
||||
return c
|
||||
|
||||
|
||||
MODELS = ['RZX 50', 'RZX 60', 'RZX 70', 'RZX 80 Ti', 'RZX 90', 'RZ 470', 'RZ 580', 'RZ 6800', 'RZ 7900']
|
||||
MODELS = ['RZX 60', 'RZX 70', 'RZX 80', 'RZX 90', 'RZ 70', 'RZ 80', 'RZ 800', 'RZ 900']
|
||||
|
||||
|
||||
def rig():
|
||||
@@ -503,24 +514,43 @@ def key(model):
|
||||
return model.replace(' ', '').lower()
|
||||
|
||||
|
||||
def guide():
|
||||
"""鉴定手册(16×16,一格):竖放的小册子,封面带品牌色条,书脊在左。"""
|
||||
c = Canvas(16, 16)
|
||||
c.box(2, 1, 13, 14, 'b')
|
||||
c.rect(3, 2, 4, 13, 'd') # 书脊
|
||||
c.rect(5, 2, 12, 2, 'h')
|
||||
c.rect(6, 4, 11, 5, 'G') # 封面色块
|
||||
c.rect(6, 4, 11, 4, 'g2')
|
||||
for y in (7, 9, 11): # 文字行
|
||||
c.rect(6, y, 11 if y != 11 else 9, y, 'm')
|
||||
c.put(12, 13, 'd')
|
||||
outline(c)
|
||||
return c
|
||||
|
||||
|
||||
SPRITES = {
|
||||
'gpu_probidia_f': lambda: gpu_front('G', 'g2'),
|
||||
'gpu_probidia_b': lambda: gpu_back('G', 'g2'),
|
||||
'gpu_pmd_f': lambda: gpu_front('R', 'r2'),
|
||||
'gpu_pmd_b': lambda: gpu_back('R', 'r2'),
|
||||
'gpu_guide': guide,
|
||||
'gpu_burnt_f': lambda: gpu_front('e', 'c', burnt=True),
|
||||
'gpu_burnt_b': lambda: gpu_back('e', 'c', burnt=True),
|
||||
'gpu_probidia_b': lambda: gpu_back('G', 'g2'),
|
||||
'gpu_pmd_b': lambda: gpu_back('R', 'r2'),
|
||||
'rig': rig,
|
||||
'terminal': terminal,
|
||||
}
|
||||
for _tier in range(4):
|
||||
SPRITES['gpu_probidia_f_t%d' % _tier] = (lambda t=_tier: gpu_front('G', 'g2', tier=t))
|
||||
SPRITES['gpu_pmd_f_t%d' % _tier] = (lambda t=_tier: gpu_front('R', 'r2', tier=t))
|
||||
|
||||
# 叠加图:和底图同尺寸,位置 (0,0) 对齐,只画变化的像素,不参与判格
|
||||
OVERLAYS = {
|
||||
'gpu_ov_fan1': lambda: ov_fan(1), 'gpu_ov_fan2': lambda: ov_fan(2),
|
||||
'gpu_ov_fin1': lambda: ov_fingers(1), 'gpu_ov_fin2': lambda: ov_fingers(2),
|
||||
'gpu_ov_brk1': lambda: ov_bracket(1), 'gpu_ov_brk2': lambda: ov_bracket(2),
|
||||
'gpu_ov_stk1': lambda: ov_sticker(1), 'gpu_ov_stk2': lambda: ov_sticker(2),
|
||||
'gpu_ov_brk1': lambda: ov_bracket(1),
|
||||
'gpu_ov_stk2': lambda: ov_sticker(2),
|
||||
}
|
||||
for _n in (1, 2, 3):
|
||||
OVERLAYS['gpu_ov_fan1_%d' % _n] = (lambda n=_n: ov_fan(1, n))
|
||||
OVERLAYS['gpu_ov_fan2_%d' % _n] = (lambda n=_n: ov_fan(2, n))
|
||||
for _m in MODELS:
|
||||
OVERLAYS['gpu_ov_txt_' + key(_m)] = (lambda m=_m: ov_text(m, False))
|
||||
OVERLAYS['gpu_ov_txs_' + key(_m)] = (lambda m=_m: ov_text(m, True))
|
||||
@@ -551,13 +581,13 @@ if __name__ == '__main__':
|
||||
if oc.px[y][x] is not None:
|
||||
b.px[y][x] = oc.px[y][x]
|
||||
return b
|
||||
stack('gpu_probidia_f', 'gpu_ov_fan1', 'gpu_ov_fin1', 'gpu_ov_brk1').png(os.path.join(sys.argv[2], 'prev_front_worn.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
|
||||
stack('gpu_pmd_f', 'gpu_ov_fan2', 'gpu_ov_fin2', 'gpu_ov_brk2').png(os.path.join(sys.argv[2], 'prev_front_fake.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
|
||||
stack('gpu_probidia_b', 'gpu_ov_txt_rzx80ti').png(os.path.join(sys.argv[2], 'prev_back_ok.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
|
||||
stack('gpu_pmd_b', 'gpu_ov_txs_rz6800', 'gpu_ov_stk1', 'gpu_ov_stk2').png(os.path.join(sys.argv[2], 'prev_back_fake.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
|
||||
stack('gpu_probidia_f_t1', 'gpu_ov_fan2_2', 'gpu_ov_fin1', 'gpu_ov_brk1').png(os.path.join(sys.argv[2], 'prev_front_worn.png'), scale=6, bg=(0xA0, 0xA8, 0x9C))
|
||||
print(sys.argv[2])
|
||||
else:
|
||||
out = os.path.normpath(os.path.join(here, '..', 'src', 'Decentralized', 'ModFiles', 'Textures', 'Items'))
|
||||
for f in os.listdir(out): # 清掉旧的显卡图,避免留下不再使用的文件
|
||||
if f.startswith('gpu_'):
|
||||
os.remove(os.path.join(out, f))
|
||||
for name, make in SPRITES.items():
|
||||
canvas = make()
|
||||
check_cells(canvas, name)
|
||||
@@ -568,8 +598,16 @@ if __name__ == '__main__':
|
||||
for name, make in big.BIG.items():
|
||||
make().png(os.path.join(out, name + '.png'))
|
||||
for name, make in big_cases.CASES.items():
|
||||
if not name.startswith('gpu_big_ov_print_'):
|
||||
if name.startswith('gpu_big_ov_fan_'):
|
||||
continue
|
||||
if not name.startswith('gpu_big_ov_print_') and name in ('gpu_big_ov_fin_dim', 'gpu_big_ov_fin_oxide', 'gpu_big_ov_brk_scratch', 'gpu_big_ov_seal_missing'):
|
||||
make().png(os.path.join(out, name + '.png'))
|
||||
# 风扇叠加按风扇数各出一套:叠加模块里的风扇位置是模块级变量,换成对应布局后再画
|
||||
for count, (centres, radius) in big.FAN_LAYOUT.items():
|
||||
big_cases.FANS = list(centres) if len(centres) > 1 else [centres[0], centres[0]]
|
||||
big_cases.FR = radius
|
||||
for fname, short in (('fan_dust1', 'fan_dust1'), ('fan_dust2', 'fan_dust2'), ('fan_chip', 'fan_chip')):
|
||||
getattr(big_cases, fname)().png(os.path.join(out, 'gpu_big_ov_%s_%d.png' % (short, count)))
|
||||
for m in MODELS:
|
||||
for style in ('normal', 'skew', 'stamp'):
|
||||
big_cases.print_overlay(m, style).png(os.path.join(out, 'gpu_big_ov_print_%s_%s.png' % (style, key(m))))
|
||||
|
||||
Reference in new issue
Block a user