"""参数化软乳几何生成 + 导出 JSON（主管线）。
在局部坐标建模：乳根在 z=0 平面（贴胸壁），乳轴沿 +Z，乳尖朝 +Z。
拓扑：规整环形(rings x segs)，配合 bmesh 统一重算面法线，平滑着色。
导出：positions/normals/uvs/indices 四个 Float32 数组的 JSON，供网页 BufferGeometry 读入。
用法:
  blender --background --python build_breast.py -- -baseR 0.22 -depth 0.3 -sag 0.6 -squash 0.35 -point 1.15 -out /tmp/opencode/breast.json
"""
import bpy, bmesh, math, sys, json

def get(a, default):
    return default if a not in sys.argv else float(sys.argv[sys.argv.index(a)+1])
def geti(a, default):
    return default if a not in sys.argv else int(sys.argv[sys.argv.index(a)+1])

baseR  = get("-baseR", 0.20)
depth  = get("-depth", 0.26)
sag    = get("-sag", 0.55)
squash = get("-squash", 0.30)
point  = get("-point", 1.15)
rings  = geti("-rings", 24)
segs   = geti("-segs", 36)
out    = sys.argv[sys.argv.index("-out")+1] if "-out" in sys.argv else "/tmp/opencode/breast.json"

bpy.ops.wm.read_factory_settings(use_empty=True)
mesh = bpy.data.meshes.new("breast")
bm = bmesh.new()

# 顶点: 环 i=0..rings (0=根,rings=尖), 每环 segs 段; 尖端单独1点
rown = []
for i in range(rings+1):
    t = i/rings
    z = t*depth
    y = -sag*depth*math.sin(t*math.pi*0.5)
    r = baseR*math.sin(min(1.0, t*1.15)*math.pi*0.5)*(1.0-0.10*t*t)
    if point > 1.0:
        k = max(0.0, (t-0.72)/0.28)
        r *= (1.0-(1.0-1.0/point)*k*k)
    if i == rings:
        rown.append(bm.verts.new((0.0, y, z)))
        continue
    row = []
    for j in range(segs):
        a = j/segs*math.pi*2
        cx = math.cos(a)*r
        cy = math.sin(a)*r
        if math.sin(a) > 0:   # 上半(朝上 cy+)压扁 → 桃形上扁下圆
            cy *= (1.0-squash)
        row.append(bm.verts.new((cx, cy+y, z)))
    rown.append(row)

# 面
def vert_at(i, j):
    if i == rings:
        return rown[i]
    return rown[i][j]
for i in range(rings):
    next_i = i+1
    for j in range(segs):
        jn = (j+1) % segs
        a = vert_at(i, j)
        b = vert_at(i, jn)
        if next_i == rings:
            tip = rown[next_i]
            bm.faces.new((a, b, tip))
        else:
            c = vert_at(next_i, jn)
            d = vert_at(next_i, j)
            bm.faces.new((a, b, c, d))
# 底部盖: 乳根平面圆盘
center = bm.verts.new((0.0, 0.0, 0.0))
for j in range(segs):
    jn = (j+1) % segs
    bm.faces.new((rown[0][jn], rown[0][j], center))

bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
bm.to_mesh(mesh)
bm.free()
for p in mesh.polygons:
    p.use_smooth = True
mesh.update()

# UV: 简单平面展开 (环->v, 角度->u)
uv = mesh.uv_layers.new(name="UVMap")
for loop in mesh.loops:
    v = mesh.vertices[loop.vertex_index].co
    # 以 z/depth 为 v, 绕角 atan2 为 u
    u = (math.atan2(v.y, v.x)+math.pi) / (math.pi*2)
    vv = min(1.0, max(0.0, v.z/max(depth,1e-6)))
    uv.data[loop.index].uv = (u, vv)

# 导出
me = mesh
me.calc_loop_triangles()
n = len(me.vertices)
data = {
  "positions": [round(c,6) for v in me.vertices for c in v.co],
  "normals":   [round(c,6) for v in me.vertices for c in v.normal],
  "uvs":       [round(c,6) for loop_uv in me.uv_layers["UVMap"].data for c in loop_uv.uv],
  "indices":   [idx for tri in me.loop_triangles for idx in tri.vertices],
  "meta": {"baseR": baseR, "depth": depth, "sag": sag, "squash": squash,
           "rings": rings, "segs": segs, "verts": n, "tris": len(me.loop_triangles)}
}
with open(out, "w") as f:
    json.dump(data, f, separators=(",",":"))
print("EXPORTED", out, "verts", n, "tris", len(me.loop_triangles))
