# 로우크롭 트랙터 (1939~1953 파몰 H형) — Blender Python 코드만으로 만든 모델.
# 큰 뒷바퀴와 앞쪽에 모인 쌍바퀴(세발자전거형), 좁고 긴 보닛, 보닛 위로 솟은 배기관·흡기관,
# 보닛 위를 지나 앞 받침대까지 이어지는 긴 조향축이 이 형식을 알아보게 하는 특징이다.
# 뒷타이어는 회전체(lathe) 단면에 갈매기(쉐브론) 러그 46개를 bmesh 로 붙여 만들고,
# 보닛·라디에이터 덮개는 둥근 단면 돌출, 펜더는 원호 띠에 두께를 주어 만들었다.
# 같은 바퀴·러그·볼트는 메시를 공유하는 복제로 배치했다. 단위 m, 앞쪽이 -Y.
from atelier import *

SHOT = {"azimuth": -40, "elevation": 13, "lens": 55, "light": "studio", "background": "#26303a"}

# ---------------------------------------------------------------- 재질
RED = mat('IH_Red_Paint', '#a51f18', 0.38, 0.0, coat=0.4)
RUBBER = mat('Tire_Rubber', '#262626', 0.85, 0.0)
IRON = mat('Cast_Iron_Dark', '#3b3836', 0.6, 0.5)
STEEL = mat('Worn_Steel', '#8b8d8f', 0.35, 0.9)
CHROME = mat('Chrome', '#d6d8da', 0.12, 1.0)
EXHAUST = mat('Exhaust_Rust', '#4a3426', 0.75, 0.3)
BLACK = mat('Black_Enamel', '#1f1f21', 0.4, 0.0)
CREAM = mat('Cream_Lettering', '#e6dcc2', 0.45, 0.0)
LENS = mat('Lamp_Lens', '#efe6c8', 0.08, 0.0, emit='#fff2c8', emit_strength=0.6)
GLASSB = glass('Precleaner_Glass', '#c9d6d0', 0.08)

# ---------------------------------------------------------------- 치수
RA_Y, RA_Z = 0.95, 0.775      # 뒷차축 (y, z)
REAR_X = 0.86                 # 뒷바퀴 중심 x
FA_Y, FA_Z = -1.33, 0.345     # 앞차축
FRONT_X = 0.165               # 앞 쌍바퀴 간격 절반


def obj_from_bm(name, bm, material, loc=(0, 0, 0), rot=(0, 0, 0), shade=True):
    me = bpy.data.meshes.new(name)
    bm.to_mesh(me)
    bm.free()
    ob = bpy.data.objects.new(name, me)
    link(ob)
    ob.location = Vector(loc)
    ob.rotation_euler = Euler(rot)
    assign(ob, material)
    if shade:
        smooth(ob)
    return ob


def bm_lathe(bm, profile, seg, closed=False):
    """profile=[(r, z)] 를 Z축 회전 — 기존 bm 에 추가."""
    rings = []
    for i in range(seg):
        a = 2 * math.pi * i / seg
        rings.append([bm.verts.new((r * math.cos(a), r * math.sin(a), z)) for r, z in profile])
    n = len(profile) - (0 if closed else 1)
    for i in range(seg):
        r0, r1 = rings[i], rings[(i + 1) % seg]
        for j in range(n):
            k = (j + 1) % len(profile)
            bm.faces.new((r0[j], r1[j], r1[k], r0[k]))


def rod(name, p0, p1, r, material, seg=16):
    """두 점을 잇는 원기둥."""
    p0, p1 = Vector(p0), Vector(p1)
    d = p1 - p0
    ob = cylinder(name, r=r, h=d.length, loc=p0, material=material, seg=seg)
    ob.rotation_euler = d.to_track_quat('Z', 'Y').to_euler()
    smooth(ob)
    return ob


def rounded_profile(w, z0, z1, rc, n=8):
    """아래가 평평하고 위 모서리가 둥근 단면 (x, z)."""
    hw = w / 2
    pts = [(-hw, z0), (hw, z0), (hw, z1 - rc)]
    for i in range(1, n):
        a = math.pi / 2 * i / n
        pts.append((hw - rc + rc * math.cos(a), z1 - rc + rc * math.sin(a)))
    for i in range(0, n + 1):
        a = math.pi / 2 + math.pi / 2 * i / n
        pts.append((-hw + rc + rc * math.cos(a), z1 - rc + rc * math.sin(a)))
    return pts


# ---------------------------------------------------------------- 뒷바퀴
def rear_tire():
    bm = bmesh.new()
    prof = [(0.495, 0.118), (0.56, 0.138), (0.64, 0.146), (0.695, 0.132), (0.722, 0.095),
            (0.732, 0.04), (0.734, 0.0), (0.732, -0.04), (0.722, -0.095), (0.695, -0.132),
            (0.64, -0.146), (0.56, -0.138), (0.495, -0.118)]
    bm_lathe(bm, prof, 120)
    N = 23
    for side in (1, -1):
        for i in range(N):
            th = 2 * math.pi * i / N + (math.pi / N if side < 0 else 0.0)
            m = (Matrix.Rotation(th, 4, 'Z') @ Matrix.Translation((0.738, 0, side * 0.066))
                 @ Matrix.Rotation(math.radians(38) * side, 4, 'X')
                 @ Matrix.Diagonal((0.075, 0.05, 0.165, 1.0)))
            res = bmesh.ops.create_cube(bm, size=1.0, matrix=m)
    bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
    return obj_from_bm('Rear_Tire_11-38', bm, RUBBER)


def rear_rim():
    bm = bmesh.new()
    prof = [(0.525, 0.14), (0.50, 0.125), (0.488, 0.10), (0.488, -0.10), (0.50, -0.125), (0.525, -0.14),
            (0.515, -0.145), (0.478, -0.11), (0.478, 0.11), (0.515, 0.145)]
    bm_lathe(bm, prof, 96, closed=True)
    ob = obj_from_bm('Rear_Rim', bm, RED)
    # 오목한 휠 디스크
    disc = lathe('Rear_Wheel_Disc', [(0.48, 0.03), (0.40, 0.05), (0.26, 0.075), (0.12, 0.075),
                                     (0.12, 0.06), (0.26, 0.06), (0.40, 0.035), (0.48, 0.015)], material=RED, seg=64)
    hub = cylinder('Rear_Hub', r=0.13, h=0.16, loc=(0, 0, 0.02), material=RED, seg=32)
    cap = cylinder('Rear_Axle_End', r=0.075, h=0.08, loc=(0, 0, 0.18), material=STEEL, seg=24)
    parts = [ob, disc, hub, cap]
    bolt = cylinder('Rear_Wheel_Bolt', r=0.016, h=0.03, loc=(0.2, 0, 0.07), material=STEEL, seg=8)
    parts.append(bolt)
    for i in range(1, 8):
        a = 2 * math.pi * i / 8
        parts.append(duplicate(bolt, loc=(0.2 * math.cos(a), 0.2 * math.sin(a), 0.07), linked=False))
    # 디스크 보강 리브
    for i in range(6):
        a = 2 * math.pi * (i + 0.5) / 6
        rb = box('Rear_Disc_Rib', (0.24, 0.035, 0.03), loc=(0.32 * math.cos(a), 0.32 * math.sin(a), 0.055),
                 rot=(0, math.radians(-12), a), material=RED)
        parts.append(rb)
    return join(parts, 'Rear_Wheel_Rim')


tire = rear_tire()
rim = rear_rim()
for sx, nm in ((1, 'Right'), (-1, 'Left')):
    t = tire if sx == 1 else duplicate(tire, name='Rear_Tire_Left')
    t.location = (sx * REAR_X, RA_Y, RA_Z)
    t.rotation_euler = (0, math.pi / 2, 0)
    if sx == 1:
        t.name = 'Rear_Tire_Right'
    r = rim if sx == 1 else duplicate(rim, name='Rear_Wheel_Left')
    r.location = (sx * REAR_X, RA_Y, RA_Z)
    r.rotation_euler = (0, sx * math.pi / 2, 0)
    if sx == 1:
        r.name = 'Rear_Wheel_Right'


# ---------------------------------------------------------------- 앞 쌍바퀴
def front_tire():
    bm = bmesh.new()
    prof = [(0.215, 0.055), (0.25, 0.068), (0.29, 0.07), (0.318, 0.06), (0.333, 0.045), (0.343, 0.036),
            (0.345, 0.026), (0.336, 0.018), (0.336, 0.010), (0.345, 0.004), (0.345, -0.004), (0.336, -0.010),
            (0.336, -0.018), (0.345, -0.026), (0.343, -0.036), (0.333, -0.045), (0.318, -0.06), (0.29, -0.07),
            (0.25, -0.068), (0.215, -0.055)]
    bm_lathe(bm, prof, 72)
    return obj_from_bm('Front_Tire_5.50-16', bm, RUBBER)


def front_rim():
    rimb = lathe('Front_Rim', [(0.225, 0.062), (0.205, 0.05), (0.205, -0.05), (0.225, -0.062),
                               (0.215, -0.066), (0.196, -0.05), (0.196, 0.05), (0.215, 0.066)], material=RED, seg=48)
    disc = lathe('Front_Wheel_Disc', [(0.2, 0.0), (0.12, 0.03), (0.07, 0.03), (0.07, 0.018),
                                      (0.12, 0.018), (0.2, -0.012)], material=RED, seg=40)
    hub = lathe('Front_Hub_Cap', [(0.07, 0.0), (0.07, 0.06), (0.05, 0.085), (0.0, 0.09)], material=RED, seg=32)
    return join([rimb, disc, hub], 'Front_Wheel_Rim')


ft = front_tire()
fr = front_rim()
for sx in (1, -1):
    nm = 'Right' if sx == 1 else 'Left'
    t = ft if sx == 1 else duplicate(ft)
    t.name = f'Front_Tire_{nm}'
    t.location = (sx * FRONT_X, FA_Y, FA_Z)
    t.rotation_euler = (0, math.pi / 2, 0)
    w = fr if sx == 1 else duplicate(fr)
    w.name = f'Front_Wheel_{nm}'
    w.location = (sx * FRONT_X, FA_Y, FA_Z)
    w.rotation_euler = (0, sx * math.pi / 2, 0)

# 앞 받침대(페데스털)와 짧은 앞차축
rod('Front_Axle', (-0.13, FA_Y, FA_Z), (0.13, FA_Y, FA_Z), 0.04, IRON)
fork = box('Front_Fork_Yoke', (0.2, 0.13, 0.16), loc=(0, FA_Y, FA_Z + 0.03), material=RED, base=True)
bevel(fork, 0.02, 2)
rod('Front_Steering_Spindle', (0, FA_Y + 0.02, FA_Z + 0.17), (0, -1.27, 0.76), 0.062, RED)
ped = extrude_profile('Front_Bolster_Pedestal', [(-0.08, 0.50), (0.08, 0.50), (0.17, 0.74), (-0.17, 0.74)], 0.2,
                      loc=(0, -1.28, 0), material=RED)
bevel(ped, 0.02, 2)

# ---------------------------------------------------------------- 엔진·차대
rail_l = box('Frame_Rail_Left', (0.07, 1.62, 0.13), loc=(-0.19, -0.42, 0.57), material=RED, base=True)
rail_r = duplicate(rail_l, loc=(0.19, -0.42, 0.57), name='Frame_Rail_Right')
bolster = box('Front_Bolster', (0.46, 0.2, 0.12), loc=(0, -1.25, 0.62), material=RED, base=True)
bevel(bolster, 0.015, 2)
pan = box('Engine_Oil_Pan', (0.26, 0.95, 0.12), loc=(0, -0.5, 0.56), material=RED, base=True)
bevel(pan, 0.03, 2)
block = box('Engine_Block_C152', (0.27, 1.02, 0.42), loc=(0, -0.53, 0.68), material=RED, base=True)
bevel(block, 0.02, 2)
head = box('Cylinder_Head', (0.25, 0.98, 0.07), loc=(0, -0.53, 1.10), material=RED, base=True)
bevel(head, 0.012, 2)
# 측면 상세: 흡배기 매니폴드, 기화기, 마그네토, 시동모터
man = rod('Exhaust_Manifold', (0.17, -0.95, 1.0), (0.17, -0.1, 1.0), 0.04, EXHAUST)
for i, y in enumerate((-0.86, -0.64, -0.42, -0.2)):
    rod('Manifold_Port', (0.14, y, 1.0), (0.18, y, 1.0), 0.032, EXHAUST, seg=12)
carb = box('Carburetor', (0.09, 0.12, 0.14), loc=(0.22, -0.52, 0.86), material=IRON, base=True)
bevel(carb, 0.01, 1)
rod('Intake_Pipe', (0.17, -0.52, 0.99), (0.24, -0.52, 0.99), 0.03, IRON)
mag = rod('Magneto', (-0.17, -0.70, 0.88), (-0.26, -0.70, 0.88), 0.06, BLACK, seg=20)
rod('Starter_Motor', (-0.17, -0.02, 0.78), (-0.17, -0.32, 0.78), 0.06, BLACK, seg=20)
oilf = cylinder('Oil_Filter', r=0.055, h=0.17, loc=(-0.2, -0.35, 0.84), material=RED, seg=20)
for i in range(4):
    y = -0.86 + i * 0.22
    rod(f'Spark_Plug_Wire_{i + 1}', (-0.13, y, 1.12), (-0.2, -0.70, 0.94), 0.007, BLACK, seg=6)
    cylinder(f'Spark_Plug_{i + 1}', r=0.012, h=0.05, loc=(-0.13, y, 1.08), material=CREAM, seg=8)
# 블록 측면: 냉각수 플러그, 밸브 덮개(왼쪽), 냉각 호스, 팬
for i in range(3):
    for sx in (1, -1):
        cylinder('Core_Plug', r=0.025, h=0.012, loc=(sx * 0.135, -0.86 + i * 0.3, 0.9),
                 rot=(0, sx * math.pi / 2, 0), material=STEEL, seg=12, base=False)
vc = box('Valve_Chamber_Cover', (0.02, 0.62, 0.12), loc=(-0.14, -0.5, 0.94), material=RED)
bevel(vc, 0.006, 1)
for i in range(4):
    cylinder('Cover_Bolt', r=0.01, h=0.012, loc=(-0.152, -0.74 + i * 0.16, 0.94),
             rot=(0, -math.pi / 2, 0), material=STEEL, seg=8, base=False)
rod('Upper_Radiator_Hose', (0, -1.04, 1.12), (0, -1.16, 1.18), 0.035, BLACK, seg=12)
lathe('Water_Outlet', [(0.0, 0.0), (0.05, 0.0), (0.05, 0.08), (0.0, 0.08)], loc=(0, -0.98, 1.10),
      rot=(math.pi / 2, 0, 0), material=RED, seg=16)
rod('Lower_Radiator_Hose', (0.1, -1.04, 0.76), (0.1, -1.16, 0.76), 0.03, BLACK, seg=12)
rod('Fan_Shaft', (0, -1.04, 0.96), (0, -1.13, 0.96), 0.03, IRON, seg=12)
for i in range(4):
    a = math.pi / 4 + i * math.pi / 2
    box('Fan_Blade', (0.18, 0.012, 0.07), loc=(0.11 * math.cos(a), -1.115, 0.96 + 0.11 * math.sin(a)),
        rot=(math.radians(25), a, 0), material=IRON)
cylinder('Governor_Housing', r=0.06, h=0.08, loc=(0.0, -1.05, 0.70), rot=(math.pi / 2, 0, 0), material=RED,
         seg=20, base=False)
rod('Generator', (0.2, -0.98, 0.84), (0.2, -0.72, 0.84), 0.065, BLACK, seg=20)
rod('Generator_Pulley', (0.2, -1.02, 0.84), (0.2, -0.98, 0.84), 0.045, IRON, seg=16)
rod('Fuel_Line', (0.06, 0.05, 1.11), (0.22, -0.46, 0.99), 0.008, STEEL, seg=6)
# 라디에이터
rad = box('Radiator_Core', (0.48, 0.12, 0.62), loc=(0, -1.2, 0.72), material=IRON, base=True)
# 클러치 하우징 (엔진 → 변속기)
lathe('Clutch_Housing', [(0.0, 0.0), (0.2, 0.0), (0.26, 0.2), (0.0, 0.2)], loc=(0, -0.05, 0.86),
      rot=(-math.pi / 2, 0, 0), material=RED, seg=40)
# 벨트 풀리 (오른쪽)
lathe('Belt_Pulley', [(0.0, 0.0), (0.15, 0.0), (0.16, 0.01), (0.16, 0.15), (0.15, 0.16), (0.0, 0.16)],
      loc=(0.27, 0.07, 0.86), rot=(0, math.pi / 2, 0), material=RED, seg=48)
rod('Pulley_Shaft', (0.18, 0.07, 0.86), (0.28, 0.07, 0.86), 0.05, IRON)
# 변속기·차동장치 케이스
trans = box('Transmission_Case', (0.58, 0.78, 0.56), loc=(0, 0.6, 0.52), material=RED, base=True)
bevel(trans, 0.05, 3)
for sx in (1, -1):
    rod('Rear_Axle_Housing', (sx * 0.28, RA_Y, RA_Z), (sx * 0.74, RA_Y, RA_Z), 0.09, RED, seg=24)
    rod('Final_Drive_Flange', (sx * 0.28, RA_Y, RA_Z), (sx * 0.33, RA_Y, RA_Z), 0.15, RED, seg=32)
    db = box('Brake_Housing', (0.1, 0.3, 0.3), loc=(sx * 0.34, 0.55, 0.78), material=RED)
    bevel(db, 0.04, 3)
    # 발판
    fb = box('Foot_Platform', (0.34, 0.5, 0.025), loc=(sx * 0.48, 0.42, 0.66), material=STEEL, base=True)
    for k in range(5):
        box('Platform_Tread_Bar', (0.3, 0.02, 0.012), loc=(sx * 0.48, 0.22 + k * 0.1, 0.684),
            material=STEEL, base=True)
    rod('Platform_Bracket', (sx * 0.3, 0.6, 0.67), (sx * 0.3, 0.6, 0.6), 0.02, IRON)
for i, sx in enumerate((0.07, -0.07)):
    rod('Brake_Pedal_Arm', (0.3 + 0.03 * i, 0.5, 0.78), (0.4 + 0.03 * i, 0.32, 0.86), 0.015, IRON, seg=8)
    box('Brake_Pedal_Pad', (0.07, 0.04, 0.09), loc=(0.4 + 0.03 * i, 0.32, 0.86), material=IRON)
rod('Clutch_Pedal_Arm', (-0.3, 0.45, 0.78), (-0.42, 0.3, 0.86), 0.015, IRON, seg=8)
box('Clutch_Pedal_Pad', (0.07, 0.04, 0.09), loc=(-0.42, 0.3, 0.86), material=IRON)

# 견인봉(드로바)
db = box('Drawbar', (0.62, 0.08, 0.04), loc=(0, 1.32, 0.42), material=IRON)
bevel(db, 0.01, 1)
for sx in (1, -1):
    rod('Drawbar_Strut', (sx * 0.28, 0.85, 0.55), (sx * 0.28, 1.32, 0.43), 0.025, IRON, seg=10)
for k in range(5):
    cylinder('Drawbar_Hole_Pin', r=0.012, h=0.05, loc=(-0.2 + k * 0.1, 1.32, 0.415), material=STEEL, seg=8)
rc = box('Rear_Cover_Plate', (0.44, 0.02, 0.4), loc=(0, 0.995, 0.58), material=RED, base=True)
bevel(rc, 0.008, 1)
for i in range(6):
    for zz in (0.62, 0.95):
        cylinder('Rear_Cover_Bolt', r=0.012, h=0.015, loc=(-0.19 + i * 0.076, 1.005, zz),
                 rot=(-math.pi / 2, 0, 0), material=STEEL, seg=8, base=False)
box('PTO_Shield', (0.18, 0.12, 0.1), loc=(0, 1.02, 0.6), material=RED)
rod('PTO_Shaft', (0, 0.98, 0.6), (0, 1.12, 0.6), 0.025, STEEL, seg=12)

# ---------------------------------------------------------------- 보닛·라디에이터 덮개
HOOD_Y0, HOOD_Y1 = -1.16, 0.30
hood = extrude_profile('Hood', rounded_profile(0.5, 1.12, 1.42, 0.16), HOOD_Y1 - HOOD_Y0,
                       loc=(0, (HOOD_Y0 + HOOD_Y1) / 2, 0), material=RED)
smooth(hood)
bevel(hood, 0.01, 2, angle=50)
shell = extrude_profile('Radiator_Shell', rounded_profile(0.58, 0.72, 1.45, 0.2, 10), 0.16,
                        loc=(0, -1.25, 0), material=RED)
smooth(shell)
bevel(shell, 0.012, 2, angle=50)
# 그릴 — 짙은 바탕판 위 가로 살
box('Grille_Backing', (0.46, 0.012, 0.6), loc=(0, -1.334, 0.79), material=IRON, base=True)
for k in range(12):
    z = 0.82 + k * 0.047
    gb = box('Grille_Bar', (0.44, 0.02, 0.02), loc=(0, -1.345, z), material=RED)
box('Grille_Center_Bar', (0.035, 0.024, 0.58), loc=(0, -1.347, 0.80), material=RED, base=True)
# 보닛 옆 상표 띠 (크림색 줄)
for sx in (1, -1):
    box('Hood_Stripe', (0.004, 1.2, 0.012), loc=(sx * 0.2505, -0.42, 1.2), material=CREAM)
    t = text3d('Hood_Lettering', 'FARMALL', size=0.085, depth=0.003,
               loc=(sx * 0.252, -0.55, 1.23), rot=(math.pi / 2, 0, sx * math.pi / 2), material=CREAM)
    if sx < 0:
        t.rotation_euler = (math.pi / 2, 0, -math.pi / 2)
# 계기판 / 연료탱크 마감
dash = extrude_profile('Dash_Panel', rounded_profile(0.5, 1.02, 1.42, 0.16), 0.03,
                       loc=(0, HOOD_Y1 + 0.015, 0), material=RED)
smooth(dash)
cylinder('Fuel_Cap', r=0.05, h=0.05, loc=(0, -0.05, 1.41), material=STEEL, seg=20)
cylinder('Radiator_Cap', r=0.04, h=0.05, loc=(0.08, -1.25, 1.44), material=STEEL, seg=20)
gauge = cylinder('Ammeter_Gauge', r=0.035, h=0.02, loc=(0.12, HOOD_Y1 + 0.03, 1.3),
                 rot=(-math.pi / 2, 0, 0), material=CREAM, seg=20)

# 배기관(오른쪽)과 흡기 프리클리너(가운데)
cylinder('Muffler', r=0.06, h=0.42, loc=(0.12, -0.55, 1.38), material=EXHAUST, seg=24)
cylinder('Exhaust_Stack', r=0.035, h=0.38, loc=(0.12, -0.55, 1.79), material=EXHAUST, seg=16)
cap = cylinder('Exhaust_Rain_Cap', r=0.05, h=0.012, loc=(0.135, -0.55, 2.17), rot=(0, math.radians(-25), 0),
               material=EXHAUST, seg=20)
cylinder('Air_Intake_Pipe', r=0.045, h=0.25, loc=(0.0, -0.95, 1.38), material=RED, seg=20)
cylinder('Precleaner_Base', r=0.07, h=0.03, loc=(0.0, -0.95, 1.63), material=STEEL, seg=24)
cylinder('Precleaner_Bowl', r=0.065, h=0.11, loc=(0.0, -0.95, 1.66), material=GLASSB, seg=24)
lathe('Precleaner_Cap', [(0.0, 0.0), (0.075, 0.0), (0.075, 0.02), (0.05, 0.05), (0.0, 0.055)],
      loc=(0, -0.95, 1.77), material=STEEL, seg=24)

# 전조등 (라디에이터 덮개 옆)
for sx in (1, -1):
    rod('Headlight_Bracket', (sx * 0.29, -1.25, 1.2), (sx * 0.37, -1.25, 1.2), 0.012, IRON, seg=8)
    lathe('Headlight_Body', [(0.0, 0.0), (0.05, 0.0), (0.075, 0.05), (0.08, 0.1), (0.0, 0.1)],
          loc=(sx * 0.4, -1.2, 1.2), rot=(math.pi / 2, 0, 0), material=RED, seg=24)
    cylinder('Headlight_Lens', r=0.07, h=0.01, loc=(sx * 0.4, -1.3, 1.2), rot=(math.pi / 2, 0, 0),
             material=LENS, seg=24)
    torus('Headlight_Bezel', 0.075, 0.008, loc=(sx * 0.4, -1.302, 1.2), rot=(math.pi / 2, 0, 0),
          material=CHROME, seg=24, rseg=6)

# ---------------------------------------------------------------- 조향 장치
WHEEL_C = Vector((0.0, 0.86, 1.70))
UJ = Vector((-0.13, 0.32, 1.50))
col_dir = (WHEEL_C - UJ).normalized()
rod('Steering_Column', UJ, WHEEL_C, 0.022, IRON)
rod('Steering_Shaft', (-0.13, -1.22, 1.50), UJ, 0.02, IRON)
sphere('Steering_U_Joint', 0.035, loc=UJ, material=IRON, seg=16, rings=8)
gb = box('Steering_Gear_Box', (0.12, 0.14, 0.12), loc=(-0.13, -1.25, 1.42), material=RED, base=True)
bevel(gb, 0.02, 2)
for y in (-0.55, 0.05):
    rod('Shaft_Support', (-0.13, y, 1.40), (-0.13, y, 1.49), 0.012, IRON, seg=8)
q = col_dir.to_track_quat('Z', 'Y')
sw_rim = torus('Steering_Wheel_Rim', 0.21, 0.016, material=BLACK, seg=48, rseg=10)
sw_hub = cylinder('Steering_Wheel_Hub', r=0.04, h=0.06, loc=(0, 0, -0.03), material=BLACK, seg=16)
spokes = [sw_rim, sw_hub]
for i in range(3):
    a = 2 * math.pi * i / 3 + math.pi / 2
    s = rod('Steering_Spoke', (0, 0, 0), (0.2 * math.cos(a), 0.2 * math.sin(a), -0.012), 0.011, STEEL, seg=8)
    spokes.append(s)
sw = join(spokes, 'Steering_Wheel')
sw.location = WHEEL_C
sw.rotation_euler = q.to_euler()

# ---------------------------------------------------------------- 좌석
leaf = rod('Seat_Spring_Bar', (0, 0.80, 1.07), (0, 1.36, 1.15), 0.03, IRON)
seat = lathe('Pan_Seat', [(0.0, 0.0), (0.16, 0.005), (0.2, 0.03), (0.225, 0.09), (0.215, 0.095),
                          (0.19, 0.04), (0.155, 0.018), (0.0, 0.013)], loc=(0, 1.38, 1.16), material=STEEL, seg=48)
seat.scale = (1.0, 0.9, 1.0)
seat.rotation_euler = (math.radians(-8), 0, 0)
rod('Seat_Post', (0, 1.36, 1.10), (0, 1.38, 1.18), 0.04, IRON)

# ---------------------------------------------------------------- 펜더
def fender(name, sx):
    bm = bmesh.new()
    a0, a1, n = math.radians(12), math.radians(150), 40
    R = 0.87
    xs = [(0.70, 0.0), (0.98, 0.0), (1.02, -0.045)]
    rows = []
    for i in range(n + 1):
        a = a0 + (a1 - a0) * i / n
        row = []
        for x, dr in xs:
            rr = R + dr
            row.append(bm.verts.new((sx * x, RA_Y + rr * math.cos(a), RA_Z + rr * math.sin(a))))
        rows.append(row)
    for i in range(n):
        for j in range(len(xs) - 1):
            bm.faces.new((rows[i][j], rows[i][j + 1], rows[i + 1][j + 1], rows[i + 1][j]))
    ob = obj_from_bm(name, bm, RED)
    solidify(ob, 0.008)
    return ob


for sx in (1, -1):
    fender('Rear_Fender', sx)
    # 펜더 지지대: 차축 하우징에서 펜더 안쪽으로
    for a in (60, 120):
        ar = math.radians(a)
        rod('Fender_Brace', (sx * 0.66, RA_Y + 0.12 * math.cos(ar), RA_Z + 0.12 * math.sin(ar)),
            (sx * 0.72, RA_Y + 0.865 * math.cos(ar), RA_Z + 0.865 * math.sin(ar)), 0.018, RED, seg=8)
