# ドーナツ：トーラス（生地）→上半分を複製して厚みを付けたアイシング→スプリンクルを散らす
import bpy, bmesh, sys, os, random, math
from mathutils import Vector
sys.path.append(os.path.dirname(__file__))
from common import reset, mat, stage, render

reset()
random.seed(7)
R, r = 1.0, 0.45  # 大半径（中心から管の中心まで）・小半径（管の太さ）
bpy.ops.mesh.primitive_torus_add(major_radius=R, minor_radius=r,
                                 major_segments=96, minor_segments=32, location=(0, 0, r * 0.8))
dough = bpy.context.object; dough.name = "Dough"
dough.scale.z = 0.8  # 少し平たく
# 手作り感：表面をわずかに凸凹させる
tex = bpy.data.textures.new("lumps", "CLOUDS"); tex.noise_scale = 0.35
dp = dough.modifiers.new("lumps", "DISPLACE"); dp.texture = tex; dp.strength = 0.04
bpy.ops.object.shade_smooth()
dough.data.materials.append(mat("dough", (0.72, 0.42, 0.18), rough=0.6, sss=0.1))

# アイシング：生地を複製して上側（z が中心より上）だけ残す
bpy.ops.object.duplicate()
icing = bpy.context.object; icing.name = "Icing"
icing.modifiers.clear()
bm = bmesh.new(); bm.from_mesh(icing.data)
bmesh.ops.delete(bm, geom=[v for v in bm.verts if v.co.z < -0.08 * r + 0.06 * math.sin(8 * math.atan2(v.co.y, v.co.x))], context="VERTS")
bm.to_mesh(icing.data); bm.free()
icing.data.materials.clear()
icing.data.materials.append(mat("icing", (0.95, 0.45, 0.62), rough=0.25, sss=0.15))
sol = icing.modifiers.new("thick", "SOLIDIFY"); sol.thickness = 0.03; sol.offset = 1
dp2 = icing.modifiers.new("lumps", "DISPLACE"); dp2.texture = tex; dp2.strength = 0.04
sub = icing.modifiers.new("smooth", "SUBSURF"); sub.levels = 1; sub.render_levels = 2

# スプリンクル：アイシング上面の点を拾い、法線に沿って細い円柱を寝かせて置く
dg = bpy.context.evaluated_depsgraph_get()
ev = icing.evaluated_get(dg).to_mesh()
mw = icing.matrix_world
cands = [(mw @ p.center, (mw.to_3x3() @ p.normal).normalized()) for p in ev.polygons if p.normal.z > 0.6]
colors = [(0.9, 0.1, 0.1), (0.1, 0.4, 0.9), (1.0, 0.85, 0.1), (0.2, 0.75, 0.3), (1, 1, 1)]
smats = [mat(f"spr{i}", c, rough=0.35) for i, c in enumerate(colors)]
bpy.ops.mesh.primitive_cylinder_add(vertices=8, radius=0.012, depth=0.09)
proto = bpy.context.object; proto.name = "SprinkleProto"
bm = bmesh.new(); bm.from_mesh(proto.data)
bmesh.ops.bevel(bm, geom=bm.edges[:], offset=0.008, segments=2)  # 端を丸める
bm.to_mesh(proto.data); bm.free()
N = 140
for i, (co, nrm) in enumerate(random.sample(cands, N)):
    s = proto.copy(); s.data = proto.data.copy()
    s.data.materials.append(smats[i % len(smats)])
    bpy.context.collection.objects.link(s)
    # 円柱の軸（Z）を表面の接線方向へ、向きはランダム
    t = nrm.cross(Vector((random.uniform(-1, 1), random.uniform(-1, 1), 0))).normalized()
    s.rotation_euler = t.to_track_quat("Z", "Y").to_euler()
    s.location = co + nrm * 0.02
bpy.data.objects.remove(proto)
print(f"CHECK スプリンクル {N} 本（候補面 {len(cands)}）")

stage(target=(0, 0, 0.25), cam_loc=(0, -4.6, 3.6), lens=50, floor_color=(0.55, 0.75, 0.85))
bpy.data.lights[0].energy = 350
out = os.path.join(os.path.dirname(__file__), "out")
os.makedirs(out, exist_ok=True)
bpy.ops.wm.save_as_mainfile(filepath=os.path.join(out, "donut.blend"))
render(os.path.join(out, "donut.png"), samples=128)
