Загрузка данных


"""
HDMI (Type A) и DisplayPort - процедурные 3D-модели штекеров для Blender.
Запуск: Blender -> Scripting -> New -> вставить код -> Run Script (Alt+P).
Единицы: 1 единица Blender = 1 мм. Размеры приблизительные, по стандартным спецификациям.
"""
import bpy
import bmesh
from math import pi

# ---------- Настройки ----------
WALL = 0.3          # толщина металлического корпуса, мм
CABLE_R = 2.5       # радиус кабеля, мм
CABLE_LEN = 40.0    # длина кабеля, мм


# ---------- Материалы ----------
def make_material(name, color, metallic=0.0, roughness=0.5):
    mat = bpy.data.materials.get(name) or bpy.data.materials.new(name)
    mat.use_nodes = True
    bsdf = mat.node_tree.nodes.get("Principled BSDF")
    bsdf.inputs["Base Color"].default_value = color
    bsdf.inputs["Metallic"].default_value = metallic
    bsdf.inputs["Roughness"].default_value = roughness
    return mat


MAT_METAL = make_material("Shell_Metal", (0.75, 0.77, 0.80, 1), 1.0, 0.25)
MAT_PLASTIC = make_material("Plastic_Black", (0.02, 0.02, 0.02, 1), 0.0, 0.45)
MAT_GOLD = make_material("Contact_Gold", (1.0, 0.77, 0.25, 1), 1.0, 0.2)
MAT_CABLE = make_material("Cable_Rubber", (0.01, 0.01, 0.01, 1), 0.0, 0.7)


def assign(obj, mat):
    obj.data.materials.clear()
    obj.data.materials.append(mat)


# ---------- Профили (сечение штекера, плоскость X-Z) ----------
def hdmi_profile(w, h, z0=0.0, c=1.0):
    """Трапеция HDMI Type A: сверху ровно, снизу скошенные углы."""
    return [
        (-w / 2, z0 + h),
        (w / 2, z0 + h),
        (w / 2, z0 + h * 0.5),
        (w / 2 - c, z0),
        (-w / 2 + c, z0),
        (-w / 2, z0 + h * 0.5),
    ]


def dp_profile(w, h, z0=0.0, c=1.5):
    """DisplayPort: прямоугольник со срезанным верхним правым углом."""
    return [
        (-w / 2, z0),
        (w / 2, z0),
        (w / 2, z0 + h - c),
        (w / 2 - c, z0 + h),
        (-w / 2, z0 + h),
    ]


# ---------- Геометрические помощники ----------
def new_object(name, verts, faces, loc=(0, 0, 0)):
    mesh = bpy.data.meshes.new(name)
    mesh.from_pydata(verts, [], faces)
    mesh.update()
    obj = bpy.data.objects.new(name, mesh)
    bpy.context.collection.objects.link(obj)
    obj.location = loc
    bm = bmesh.new()
    bm.from_mesh(mesh)
    bmesh.ops.recalc_face_normals(bm, faces=bm.faces)
    bm.to_mesh(mesh)
    bm.free()
    return obj


def hollow_tube(name, outer, inner, depth, loc):
    """Полая труба вдоль оси Y по двум профилям с одинаковым числом точек."""
    n = len(outer)
    v = []
    for (x, z) in outer:
        v.append((x, 0, z))        # 0..n-1
    for (x, z) in outer:
        v.append((x, depth, z))    # n..2n-1
    for (x, z) in inner:
        v.append((x, 0, z))        # 2n..3n-1
    for (x, z) in inner:
        v.append((x, depth, z))    # 3n..4n-1
    f = []
    for i in range(n):
        j = (i + 1) % n
        f.append((i, j, n + j, n + i))                    # наружная стенка
        f.append((2 * n + i, 3 * n + i, 3 * n + j, 2 * n + j))  # внутренняя
        f.append((i, 2 * n + i, 2 * n + j, j))            # передний торец
        f.append((n + i, n + j, 3 * n + j, 3 * n + i))    # задний торец
    return new_object(name, v, f, loc)


def box(name, size, center, parent=None):
    bpy.ops.mesh.primitive_cube_add(size=1, location=center)
    o = bpy.context.active_object
    o.name = name
    o.dimensions = size
    bpy.ops.object.transform_apply(scale=True)
    if parent:
        o.parent = parent
    return o


def rounded_box(name, size, center, bevel=1.2, parent=None):
    o = box(name, size, center, parent)
    mod = o.modifiers.new("Bevel", "BEVEL")
    mod.width = bevel
    mod.segments = 4
    bpy.ops.object.shade_smooth()
    return o


# ---------- Сборка штекера ----------
def build_plug(kind, origin_x):
    if kind == "HDMI":
        w, h, depth = 13.9, 4.45, 10.0
        prof = hdmi_profile
        pins_top, pins_bot = 10, 9
        pitch = 1.0
    else:  # DisplayPort
        w, h, depth = 16.1, 4.76, 10.0
        prof = dp_profile
        pins_top, pins_bot = 10, 10
        pitch = 1.0

    root = bpy.data.objects.new(f"{kind}_Plug", None)
    bpy.context.collection.objects.link(root)
    root.location = (origin_x, 0, 0)

    # Металлический корпус
    outer = prof(w, h, 0.0)
    inner = prof(w - 2 * WALL, h - 2 * WALL, WALL, c=(1.0 if kind == "HDMI" else 1.5) - WALL)
    shell = hollow_tube(f"{kind}_Shell", outer, inner, depth, (0, 0, 0))
    shell.parent = root
    assign(shell, MAT_METAL)

    # Пластиковый язычок внутри
    tongue_t = 1.0
    tongue_w = w - 2 * WALL - 1.0
    tongue = box(f"{kind}_Tongue", (tongue_w, depth - 1.0, tongue_t),
                 (0, depth / 2 + 0.5 - 0.5 + 0.0, h / 2), root)
    tongue.location.y = (depth - 1.0) / 2 + 1.0
    assign(tongue, MAT_PLASTIC)

    # Контакты (два ряда)
    cw, cl, ct = 0.5, depth - 2.5, 0.12
    z_top = h / 2 + tongue_t / 2 + ct / 2
    z_bot = h / 2 - tongue_t / 2 - ct / 2
    for row, count, z in (("T", pins_top, z_top), ("B", pins_bot, z_bot)):
        start = -(count - 1) * pitch / 2
        for i in range(count):
            c = box(f"{kind}_Pin_{row}{i + 1}", (cw, cl, ct),
                    (start + i * pitch, 1.5 + cl / 2, z), root)
            assign(c, MAT_GOLD)

    # Пластиковая «лодочка» (корпус разъёма)
    boot = rounded_box(f"{kind}_Boot", (w + 4.0, 14.0, h + 4.0),
                       (0, depth + 7.0, h / 2), bevel=1.2, parent=root)
    assign(boot, MAT_PLASTIC)

    # Кабель
    bpy.ops.mesh.primitive_cylinder_add(
        radius=CABLE_R, depth=CABLE_LEN,
        location=(0, depth + 14.0 + CABLE_LEN / 2, h / 2),
        rotation=(pi / 2, 0, 0), vertices=32)
    cable = bpy.context.active_object
    cable.name = f"{kind}_Cable"
    cable.parent = root
    bpy.ops.object.shade_smooth()
    assign(cable, MAT_CABLE)
    return root


# ---------- Запуск ----------
def main():
    # очистка старых объектов с такими именами
    for ob in list(bpy.data.objects):
        if ob.name.startswith(("HDMI_", "DisplayPort_", "DP_")):
            bpy.data.objects.remove(ob, do_unlink=True)

    build_plug("HDMI", -15.0)
    build_plug("DisplayPort", 15.0)

    # Мягкий свет и камера для быстрого рендера
    if "Key_Light" not in bpy.data.objects:
        bpy.ops.object.light_add(type="AREA", location=(0, -60, 50))
        light = bpy.context.active_object
        light.name = "Key_Light"
        light.data.energy = 8000
        light.data.size = 40
        light.rotation_euler = (pi / 3.2, 0, 0)

    print("Готово: HDMI слева, DisplayPort справа.")


main()