CoolFace
Apppublic

lerp666/cricket-commons

sourceHugging Facemitupdated 3mo agoView on Hugging Face
0likes
render.py136 linesDownload Raw Back to root
1"""参数化 SVG 渲染: traits + age + stage + sick + mood → 蛐蛐形象.2 3设计原则: 零依赖、零成本、演化连续可见。4映射(可随时调,但保持单调直觉):5  勇 brave   → 触须张角/前肢姿态(越勇越张扬)6  萌 cute    → 眼睛大小/身体圆润度7  怨 grudge  → 体色向紫黑偏移/眉角下压8  智 wit     → 额头花纹复杂度(条纹数)9  馋 glutton → 肚子大小10  stage      → 整体尺寸 + 翅膀层数(蜕皮=大变样)11  sick       → 整体降饱和 + 绿色病气 + 眼睛变 X12"""13from __future__ import annotations14 15from traits import TRAIT_KEYS, molt_stage16 17 18def _lerp(a: float, b: float, t: float) -> float:19    return a + (b - a) * t20 21 22def _color(traits: dict, sick: bool) -> tuple[str, str]:23    """主体色: 健康=暖棕→怨气高偏紫黑; 生病=灰绿."""24    g = traits.get("grudge", 0.5)25    if sick:26        return "#7a8a6a", "#5a6a4a"27    r = int(_lerp(150, 90, g))28    gr = int(_lerp(95, 60, g))29    b = int(_lerp(60, 110, g))30    return f"rgb({r},{gr},{b})", f"rgb({max(r-40,0)},{max(gr-30,0)},{max(b-20,0)})"31 32 33def render_svg(traits: dict, feed_count: int = 0, sick: bool = False,34               mood: str = "calm", width: int = 360, height: int = 280) -> str:35    """返回完整 <svg> 字符串."""36    t = {k: float(traits.get(k, 0.5)) for k in TRAIT_KEYS}37    stage = molt_stage(feed_count)38    scale = _lerp(0.7, 1.15, min(stage, 3) / 3)39    body_fill, body_dark = _color(t, sick)40 41    cx, cy = width / 2, height / 2 + 2042    belly = _lerp(34, 52, t["glutton"]) * scale          # 馋→肚子43    body_ry = _lerp(belly * 0.78, belly * 0.95, t["cute"])  # 萌→圆润44    eye_r = _lerp(4.5, 9.5, t["cute"]) * scale            # 萌→大眼45    ant_spread = _lerp(8, 42, t["brave"])                 # 勇→触须张角(度)46    ant_len = _lerp(55, 85, t["brave"]) * scale47    stripes = 1 + int(round(t["wit"] * 4))                # 智→花纹条数48    brow_drop = _lerp(-3, 5, t["grudge"])                 # 怨→眉角下压49 50    head_cx = cx - belly * 0.9551    head_cy = cy - body_ry * 0.4552    head_r = _lerp(16, 22, t["cute"]) * scale53 54    parts: list[str] = []55    parts.append(56        f'<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" '57        f'viewBox="0 0 {width} {height}">'58    )59    # 地面60    parts.append(f'<ellipse cx="{cx}" cy="{cy + body_ry + 14}" rx="{belly * 1.6}" ry="10" fill="#00000014"/>')61    # 后腿(蛐蛐标志性大跳腿)62    leg = belly * 1.2563    parts.append(64        f'<path d="M {cx + belly * 0.5} {cy} q {leg * 0.55} {-leg * 0.7} {leg * 0.32} {leg * 0.05} '65        f'l {-leg * 0.12} {leg * 0.62}" stroke="{body_dark}" stroke-width="{5 * scale}" fill="none" stroke-linecap="round"/>'66    )67    # 身体68    parts.append(f'<ellipse cx="{cx}" cy="{cy}" rx="{belly}" ry="{body_ry}" fill="{body_fill}"/>')69    # 翅膀层数 = stage+1(蜕皮可见)70    for i in range(stage + 1):71        wy = cy - body_ry * 0.55 - i * 4 * scale72        parts.append(73            f'<ellipse cx="{cx + 6}" cy="{wy}" rx="{belly * 0.85}" ry="{body_ry * 0.45}" '74            f'fill="{body_dark}" opacity="{0.55 - i * 0.12}"/>'75        )76    # 智力花纹77    for i in range(stripes):78        sx = cx - belly * 0.4 + i * (belly * 0.8 / max(stripes - 1, 1))79        parts.append(80            f'<line x1="{sx}" y1="{cy - body_ry * 0.25}" x2="{sx}" y2="{cy + body_ry * 0.35}" '81            f'stroke="{body_dark}" stroke-width="{2 * scale}" opacity="0.6"/>'82        )83    # 头84    parts.append(f'<circle cx="{head_cx}" cy="{head_cy}" r="{head_r}" fill="{body_fill}"/>')85    # 触须(勇→张角)86    for sign in (-1, 1):87        ang = (-90 + sign * ant_spread) * 3.14159 / 18088        ex = head_cx + ant_len * 1.0 * __import__("math").cos(ang) * (0.6 if sign < 0 else 0.8)89        ey = head_cy + ant_len * __import__("math").sin(ang)90        mx = head_cx + sign * 1091        my = head_cy - ant_len * 0.692        parts.append(93            f'<path d="M {head_cx} {head_cy - head_r * 0.7} Q {mx} {my} {ex} {ey}" '94            f'stroke="{body_dark}" stroke-width="{2.2 * scale}" fill="none" stroke-linecap="round"/>'95        )96    # 眼睛 / 病眼97    eye_x, eye_y = head_cx - head_r * 0.25, head_cy - head_r * 0.1598    if sick:99        s = eye_r * 0.8100        parts.append(101            f'<g stroke="#3a3a3a" stroke-width="2.4" stroke-linecap="round">'102            f'<line x1="{eye_x - s}" y1="{eye_y - s}" x2="{eye_x + s}" y2="{eye_y + s}"/>'103            f'<line x1="{eye_x - s}" y1="{eye_y + s}" x2="{eye_x + s}" y2="{eye_y - s}"/></g>'104        )105        # 病气泡106        parts.append(f'<circle cx="{head_cx - head_r * 1.4}" cy="{head_cy - head_r}" r="5" fill="#9ab87a" opacity="0.8"/>')107    else:108        parts.append(f'<circle cx="{eye_x}" cy="{eye_y}" r="{eye_r}" fill="#241a12"/>')109        parts.append(f'<circle cx="{eye_x + eye_r * 0.3}" cy="{eye_y - eye_r * 0.3}" r="{eye_r * 0.3}" fill="#fff"/>')110    # 怨气眉111    parts.append(112        f'<line x1="{eye_x - eye_r * 1.4}" y1="{eye_y - eye_r * 1.6 + brow_drop}" '113        f'x2="{eye_x + eye_r * 1.2}" y2="{eye_y - eye_r * 1.6 - brow_drop}" '114        f'stroke="#241a12" stroke-width="2.6" stroke-linecap="round"/>'115    )116    # 嘴(mood 简单弯曲)117    smile = {"happy": 6, "excited": 7, "loved": 6, "content": 4, "calm": 1,118             "sad": -5, "angry": -6, "hurt": -6, "disgusted": -4}.get(mood, 1)119    parts.append(120        f'<path d="M {head_cx - 7} {head_cy + head_r * 0.45} q 7 {smile} 14 0" '121        f'stroke="#241a12" stroke-width="2.2" fill="none" stroke-linecap="round"/>'122    )123    parts.append("</svg>")124    return "".join(parts)125 126 127def svg_params(traits: dict, feed_count: int, sick: bool, mood: str) -> dict:128    """快照里存的渲染参数(回放史料)."""129    return {130        "traits": {k: round(float(traits.get(k, 0.5)), 4) for k in TRAIT_KEYS},131        "stage": molt_stage(feed_count),132        "sick": bool(sick),133        "mood": mood,134        "feed_count": feed_count,135    }136