|
#!/usr/bin/env python3 |
|
"""PROTOTYPE — X 形 diamond SWAP の人間確認用 GLB。本実装には使わない。""" |
|
|
|
import math |
|
import os |
|
import sys |
|
|
|
sys.path.insert(0, os.path.abspath("docs/poc")) |
|
|
|
import make_diamond_gate as gate |
|
from make_diamond_cnot import GltfBuilder |
|
|
|
ENDPOINT_SPAN = 1.5 |
|
LEFT_X = -ENDPOINT_SPAN / 2 |
|
RIGHT_X = ENDPOINT_SPAN / 2 |
|
ARM_LENGTH = 0.86 |
|
ARM_WIDTH = 0.16 |
|
ARM_BEVEL = 0.07 |
|
ARM_DEPTH = 0.20 |
|
BOND_RADIUS = 0.045 |
|
BOND_SIDES = 8 |
|
BOND_CENTER_Z = ARM_DEPTH * 0.78 |
|
|
|
|
|
def transform_point(point, angle, center_x, z_offset=0.0): |
|
x, y, z = point |
|
cosine = math.cos(angle) |
|
sine = math.sin(angle) |
|
return (x * cosine - y * sine + center_x, x * sine + y * cosine, z + z_offset) |
|
|
|
|
|
def arm_outline(scale=1.0): |
|
half_length = ARM_LENGTH * scale / 2 |
|
half_width = ARM_WIDTH * scale / 2 |
|
bevel = min(ARM_BEVEL * scale, half_length * 0.45, half_width * 0.8) |
|
return [ |
|
(-half_length + bevel, -half_width), |
|
(half_length - bevel, -half_width), |
|
(half_length, -half_width + bevel), |
|
(half_length, half_width - bevel), |
|
(half_length - bevel, half_width), |
|
(-half_length + bevel, half_width), |
|
(-half_length, half_width - bevel), |
|
(-half_length, -half_width + bevel), |
|
] |
|
|
|
|
|
def gem_arm_mesh(angle, center_x, z_offset): |
|
outer = arm_outline(1.0) |
|
inner = arm_outline(0.76) |
|
positions = [] |
|
normals = [] |
|
indices = [] |
|
|
|
def add_triangle(a, b, c): |
|
start = len(positions) |
|
world = [ |
|
transform_point(a, angle, center_x, z_offset), |
|
transform_point(b, angle, center_x, z_offset), |
|
transform_point(c, angle, center_x, z_offset), |
|
] |
|
normal = gate.face_normal(*world) |
|
positions.extend(world) |
|
normals.extend([normal] * 3) |
|
indices.extend([start, start + 1, start + 2]) |
|
|
|
outer_front_z = 0.02 |
|
inner_front_z = ARM_DEPTH / 2 |
|
crown_z = ARM_DEPTH * 0.78 |
|
back_z = -ARM_DEPTH / 2 |
|
|
|
# 正面中央を 8 枚の三角ファセットにする。 |
|
for index in range(8): |
|
next_index = (index + 1) % 8 |
|
add_triangle((0, 0, crown_z), (*inner[index], inner_front_z), (*inner[next_index], inner_front_z)) |
|
|
|
# 正面外周の面取り。 |
|
for index in range(8): |
|
next_index = (index + 1) % 8 |
|
add_triangle((*outer[index], outer_front_z), (*inner[index], inner_front_z), (*inner[next_index], inner_front_z)) |
|
add_triangle((*outer[index], outer_front_z), (*inner[next_index], inner_front_z), (*outer[next_index], outer_front_z)) |
|
|
|
# 側面と背面。 |
|
for index in range(8): |
|
next_index = (index + 1) % 8 |
|
add_triangle((*outer[index], back_z), (*outer[index], outer_front_z), (*outer[next_index], outer_front_z)) |
|
add_triangle((*outer[index], back_z), (*outer[next_index], outer_front_z), (*outer[next_index], back_z)) |
|
add_triangle((0, 0, back_z), (*outer[next_index], back_z), (*outer[index], back_z)) |
|
|
|
return positions, normals, indices |
|
|
|
|
|
def merge_meshes(meshes): |
|
positions = [] |
|
normals = [] |
|
indices = [] |
|
for mesh_positions, mesh_normals, mesh_indices in meshes: |
|
offset = len(positions) |
|
positions.extend(mesh_positions) |
|
normals.extend(mesh_normals) |
|
indices.extend(index + offset for index in mesh_indices) |
|
return positions, normals, indices |
|
|
|
|
|
def endpoint_mesh(center_x): |
|
return merge_meshes( |
|
[ |
|
gem_arm_mesh(math.radians(45), center_x, -0.025), |
|
gem_arm_mesh(math.radians(-45), center_x, 0.025), |
|
] |
|
) |
|
|
|
|
|
def faceted_bond_mesh(min_x, max_x, radius, sides, center_z): |
|
positions = [] |
|
normals = [] |
|
indices = [] |
|
for index in range(sides): |
|
angle_a = math.tau * index / sides |
|
angle_b = math.tau * (index + 1) / sides |
|
ya, za = math.cos(angle_a) * radius, center_z + math.sin(angle_a) * radius |
|
yb, zb = math.cos(angle_b) * radius, center_z + math.sin(angle_b) * radius |
|
start = len(positions) |
|
vertices = [(min_x, ya, za), (max_x, ya, za), (max_x, yb, zb), (min_x, yb, zb)] |
|
normal = gate.face_normal(vertices[0], vertices[1], vertices[2]) |
|
positions.extend(vertices) |
|
normals.extend([normal] * 4) |
|
indices.extend([start, start + 1, start + 2, start, start + 2, start + 3]) |
|
return positions, normals, indices |
|
|
|
|
|
def build(output_path): |
|
builder = GltfBuilder() |
|
left = endpoint_mesh(LEFT_X) |
|
right = endpoint_mesh(RIGHT_X) |
|
bond = faceted_bond_mesh(LEFT_X, RIGHT_X, BOND_RADIUS, BOND_SIDES, BOND_CENTER_Z) |
|
meshes = [ |
|
{"name": "SWAP_x0_diamond_mesh", "primitives": [builder.primitive(*left, 0)]}, |
|
{"name": "SWAP_x1_diamond_mesh", "primitives": [builder.primitive(*right, 0)]}, |
|
{"name": "SWAP_bond_bar_mesh", "primitives": [builder.primitive(*bond, 0)]}, |
|
] |
|
gltf = { |
|
"asset": {"version": "2.0", "generator": "PROTOTYPE prototype_diamond_swap.py"}, |
|
"scene": 0, |
|
"scenes": [{"nodes": [0, 1, 2]}], |
|
"nodes": [ |
|
{"mesh": 0, "name": "SWAP_x0_diamond"}, |
|
{"mesh": 1, "name": "SWAP_x1_diamond"}, |
|
{"mesh": 2, "name": "SWAP_bond_bar"}, |
|
], |
|
"meshes": meshes, |
|
"materials": [ |
|
{ |
|
"name": "prototype_diamond_SWAP", |
|
"alphaMode": "OPAQUE", |
|
"emissiveFactor": [1.0, 0.62, 0.18], |
|
"pbrMetallicRoughness": { |
|
"baseColorFactor": [1.0, 0.62, 0.18, 1.0], |
|
"metallicFactor": 0.7, |
|
"roughnessFactor": 0.28, |
|
}, |
|
} |
|
], |
|
"bufferViews": builder.buffer_views, |
|
"accessors": builder.accessors, |
|
"buffers": [{"byteLength": len(builder.bin_chunk)}], |
|
} |
|
gate.save_glb(output_path, 2, gltf, bytes(builder.bin_chunk)) |
|
|
|
|
|
if __name__ == "__main__": |
|
if len(sys.argv) != 2: |
|
raise SystemExit("usage: prototype_diamond_swap.py OUTPUT.glb") |
|
os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True) |
|
build(sys.argv[1]) |
|
print(sys.argv[1]) |