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
Binary file not shown.
Binary file not shown.
+27
-29
@@ -7,8 +7,9 @@ import random
|
||||
from sprites import Canvas, C, key, MODELS
|
||||
|
||||
BW, BH = 192, 128
|
||||
FANS = ((68, 51), (142, 51))
|
||||
FR = 30 # 风扇半径
|
||||
# 风扇布局(按风扇数):中心与半径;FANS / FR 保留为双风扇的值,给叠加图模块当默认
|
||||
FAN_LAYOUT = {1: ([(105, 55)], 34), 2: ([(68, 51), (142, 51)], 30), 3: ([(52, 51), (96, 51), (140, 51)], 21)}
|
||||
FANS, FR = FAN_LAYOUT[2][0], FAN_LAYOUT[2][1]
|
||||
SH0, SH1 = (24, 14), (186, 96) # 散热罩 左上 / 右下
|
||||
FING_Y0, FING_Y1 = 109, 122
|
||||
NOTCH_X = 112
|
||||
@@ -94,9 +95,9 @@ def bracket(c):
|
||||
c.rect(9, 10, 14, 12, 'k')
|
||||
|
||||
|
||||
def fan(c, cx, cy, accent, accent_dark):
|
||||
def fan(c, cx, cy, accent, accent_dark, R=None):
|
||||
"""风扇:九片弯曲扇叶(前缘高光、叶面渐变、叶尖暗边),外圈带四个固定螺丝,轴心带品牌圆标。"""
|
||||
R = FR
|
||||
R = R or FR
|
||||
for y in range(cy - R - 1, cy + R + 2):
|
||||
for x in range(cx - R - 1, cx + R + 2):
|
||||
dx, dy = x - cx, y - cy
|
||||
@@ -135,7 +136,10 @@ def fan(c, cx, cy, accent, accent_dark):
|
||||
c.put(sx, sy, 'g')
|
||||
|
||||
|
||||
def gpu_front(accent, accent_dark, brand_text, burnt=False):
|
||||
def gpu_front(accent, accent_dark, brand_text, tier=1, burnt=False):
|
||||
"""tier 决定实物特征:风扇数(1/2/3/3)与供电口(6 针 / 8 针 / 8 针 / 双 8 针)。"""
|
||||
count = (1, 2, 3, 3)[tier]
|
||||
fans, R = FAN_LAYOUT[count]
|
||||
c = Canvas(BW, BH)
|
||||
bracket(c)
|
||||
x0, y0 = SH0
|
||||
@@ -161,21 +165,26 @@ def gpu_front(accent, accent_dark, brand_text, burnt=False):
|
||||
c.put(x, y, 'x' if (x - (x1 - 18)) % 3 == 0 else 'g' if (x - (x1 - 18)) % 3 == 1 else 'n')
|
||||
c.rect(x1 - 20, y0 + 10, x1 - 19, y1 - 4, 'x')
|
||||
# 风扇之间的中筋
|
||||
c.rect(105, y0 + 8, 106, y1 - 5, 'k'); c.rect(107, y0 + 8, 107, y1 - 5, 'x')
|
||||
for cx, cy in FANS:
|
||||
fan(c, cx, cy, accent, accent_dark)
|
||||
for a, b in zip(fans, fans[1:]):
|
||||
mx = (a[0] + b[0]) // 2 + 1
|
||||
c.rect(mx - 1, y0 + 8, mx, y1 - 5, 'k'); c.rect(mx + 1, y0 + 8, mx + 1, y1 - 5, 'x')
|
||||
for cx, cy in fans:
|
||||
fan(c, cx, cy, accent, accent_dark, R)
|
||||
# 前沿斜切装饰条:品牌色 + 品牌字
|
||||
for x in range(x0 + 4, x1 - 20):
|
||||
c.rect(x, y1 - 5, x, y1 - 3, accent if x % 11 else accent_dark)
|
||||
text57(c, 112, 84, brand_text, 'h')
|
||||
text57(c, 136 if count == 1 else 112, 84, brand_text, 'h')
|
||||
# 顶沿:四根铜热管端头与 8 针供电口
|
||||
for i in range(4):
|
||||
x = 38 + i * 9
|
||||
c.rect(x, 7, x + 6, y0 - 1, 'p'); c.rect(x, 7, x + 1, y0 - 1, 'h' if i == 0 else 'p')
|
||||
c.rect(x + 5, 7, x + 6, y0 - 1, 'q'); c.rect(x, 7, x + 6, 7, 'q')
|
||||
c.rect(112, 5, 154, y0 - 1, 'n'); c.rect(112, 5, 154, 6, 'z')
|
||||
for i in range(8):
|
||||
c.rect(114 + i * 5, 7, 116 + i * 5, 9, 'k'); c.put(115 + i * 5, 8, 'Y')
|
||||
# 供电口:低端 6 针;中端、高端 8 针;旗舰两个 8 针
|
||||
for left, pins in ((((118, 6),) if tier == 0 else ((112, 8),) if tier < 3 else ((78, 8), (126, 8)))):
|
||||
right = left + pins * 5 + 2
|
||||
c.rect(left, 5, right, y0 - 1, 'n'); c.rect(left, 5, right, 6, 'z')
|
||||
for i in range(pins):
|
||||
c.rect(left + 2 + i * 5, 7, left + 4 + i * 5, 9, 'k'); c.put(left + 3 + i * 5, 8, 'Y')
|
||||
# PCB:阻焊绿、边缘高光、元件与走线
|
||||
for y in range(y1 + 1, FING_Y0):
|
||||
for x in range(x0, x1 + 1):
|
||||
@@ -248,8 +257,6 @@ def sticker(c, seed):
|
||||
w = 1 + ((x * 7 + seed) % 3)
|
||||
if (x * 5 + seed) % 4 != 0:
|
||||
c.rect(x, 70, x, 84, 'n')
|
||||
text57(c, 118, 88, "0X5A70", 'n')
|
||||
c.rect(150, 88, 166, 94, 'm')
|
||||
|
||||
|
||||
def seal(c):
|
||||
@@ -407,19 +414,12 @@ def ov_sticker(kind):
|
||||
|
||||
BIG = {}
|
||||
for _b, (_a, _ad, _t) in {'probidia': ('G', 'g2', 'PROBIDIA'), 'pmd': ('R', 'r2', 'PMD')}.items():
|
||||
BIG['gpu_big_%s_f' % _b] = (lambda a=_a, ad=_ad, t=_t: gpu_front(a, ad, t))
|
||||
for _tier in range(4):
|
||||
BIG['gpu_big_%s_f_t%d' % (_b, _tier)] = (lambda a=_a, ad=_ad, t=_t, k=_tier: gpu_front(a, ad, t, k))
|
||||
BIG['gpu_big_%s_b' % _b] = (lambda a=_a, ad=_ad: gpu_back(a, ad))
|
||||
BIG['gpu_big_burnt_f'] = lambda: gpu_front('e', 'c', 'PROBIDIA', burnt=True)
|
||||
BIG['gpu_big_burnt_f'] = lambda: gpu_front('e', 'c', 'PROBIDIA', 1, burnt=True)
|
||||
BIG['gpu_big_burnt_b'] = lambda: gpu_back('e', 'c', burnt=True)
|
||||
BIG_OV = {
|
||||
'gpu_big_ov_fan1': lambda: ov_fan(1), 'gpu_big_ov_fan2': lambda: ov_fan(2),
|
||||
'gpu_big_ov_fin1': lambda: ov_fingers(1), 'gpu_big_ov_fin2': lambda: ov_fingers(2),
|
||||
'gpu_big_ov_brk1': lambda: ov_bracket(1), 'gpu_big_ov_brk2': lambda: ov_bracket(2),
|
||||
'gpu_big_ov_stk1': lambda: ov_sticker(1), 'gpu_big_ov_stk2': lambda: ov_sticker(2),
|
||||
}
|
||||
for _m in MODELS:
|
||||
BIG_OV['gpu_big_ov_txt_' + key(_m)] = (lambda m=_m: ov_text(m, False))
|
||||
BIG_OV['gpu_big_ov_txs_' + key(_m)] = (lambda m=_m: ov_text(m, True))
|
||||
BIG_OV = {}
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys, os
|
||||
@@ -435,7 +435,5 @@ if __name__ == '__main__':
|
||||
b.px[y][x] = oc.px[y][x]
|
||||
return b
|
||||
bg = (0x6A, 0x72, 0x70)
|
||||
stack('gpu_big_probidia_f').png(os.path.join(d, 'big_f.png'), scale=5, bg=bg)
|
||||
stack('gpu_big_pmd_f', 'gpu_big_ov_fan1', 'gpu_big_ov_fin2', 'gpu_big_ov_brk2').png(os.path.join(d, 'big_f_fake.png'), scale=5, bg=bg)
|
||||
stack('gpu_big_pmd_b', 'gpu_big_ov_txs_rz6800', 'gpu_big_ov_stk1').png(os.path.join(d, 'big_b_fake.png'), scale=5, bg=bg)
|
||||
stack('gpu_big_probidia_b', 'gpu_big_ov_txt_rzx80ti').png(os.path.join(d, 'big_b.png'), scale=5, bg=bg)
|
||||
for t in range(4):
|
||||
stack('gpu_big_probidia_f_t%d' % t).png(os.path.join(d, 'big_f_t%d.png' % t), scale=3, bg=bg)
|
||||
+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