1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Pythonプログラミングで始める形状作成の練習帳です。PythonSCADにコピペして、確認していきましょう。きっと、世界は広いと感じられます。ありがとうございます。

PythonSCAD

PythonSCADコミュニティ・開発者に感謝申し上げます。ありがとうございます。

01. Google検索のAIモードのサンプル例

検索「PythonSCAD」の簡単なサンプルコード:
原点に配置した10角柱(円柱)を、X軸方向に20移動させて黄色く塗るコード例です。

from pythonscad import *

# 形状の定義(半径5、高さ10、側面の細かさ10)
my_cylinder = cylinder(r=5, h=10, fn=10)

# X方向に20移動
moved_obj = translate(my_cylinder, [20, 0, 0])

# 黄色に着色して配置
final_obj = color(moved_obj, "yellow")

show(final_obj)

スクリーンショット 2026-08-17 113436.png

02. 色を変更。移動の方法を + [x, y, z]で記述

from pythonscad import *

# 形状の定義(半径5、高さ10、側面の細かさ10,10角形)
my_cylinder = cylinder(r=5, h=10, fn=10)

# X方向に20移動、+ [20, 0, 0]が使えます。
moved_obj = my_cylinder + [20, 0, 0]

# dodgerblueに着色
final_obj = color(moved_obj, "dodgerblue")

show(final_obj)

スクリーンショット 2026-08-17 113721.png

色の名前は以下を参考にしてください。

03. 寸法20×20×2mmのcubeに、以下の147色を割り当て、xy平面上に、等間隔で、12列で並べる

カラーは、アルファベット順

完成コード(PythonSCAD)

# ==========================================
# Color Tiles Grid (147 colors)
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
tile_x   = 20.0
tile_y   = 20.0
tile_z   = 2.0

cols     = 12          # 12列
gap      = 2.0         # タイル同士のすき間(等間隔)
pitch_x  = tile_x + gap
pitch_y  = tile_y + gap

# --- 色リスト(ユーザー指定) ---
colors = [
    "aliceblue",
    "antiquewhite",
    "aqua",
    "aquamarine",
    "azure",
    "beige",
    "bisque",
    "black",
    "blanchedalmond",
    "blue",
    "blueviolet",
    "brown",
    "burlywood",
    "cadetblue",
    "chartreuse",
    "chocolate",
    "coral",
    "cornflowerblue",
    "cornsilk",
    "crimson",
    "cyan",
    "darkblue",
    "darkcyan",
    "darkgoldenrod",
    "darkgray",
    "darkgreen",
    "darkgrey",
    "darkkhaki",
    "darkmagenta",
    "darkolivegreen",
    "darkorange",
    "darkorchid",
    "darkred",
    "darksalmon",
    "darkseagreen",
    "darkslateblue",
    "darkslategray",
    "darkslategrey",
    "darkturquoise",
    "darkviolet",
    "deeppink",
    "deepskyblue",
    "dimgray",
    "dimgrey",
    "dodgerblue",
    "firebrick",
    "floralwhite",
    "forestgreen",
    "fuchsia",
    "gainsboro",
    "ghostwhite",
    "gold",
    "goldenrod",
    "gray",
    "green",
    "greenyellow",
    "grey",
    "honeydew",
    "hotpink",
    "indianred",
    "indigo",
    "ivory",
    "khaki",
    "lavender",
    "lavenderblush",
    "lawngreen",
    "lemonchiffon",
    "lightblue",
    "lightcoral",
    "lightcyan",
    "lightgoldenrodyellow",
    "lightgray",
    "lightgreen",
    "lightgrey",
    "lightpink",
    "lightsalmon",
    "lightseagreen",
    "lightskyblue",
    "lightslategray",
    "lightslategrey",
    "lightsteelblue",
    "lightyellow",
    "lime",
    "limegreen",
    "linen",
    "magenta",
    "maroon",
    "mediumaquamarine",
    "mediumblue",
    "mediumorchid",
    "mediumpurple",
    "mediumseagreen",
    "mediumslateblue",
    "mediumspringgreen",
    "mediumturquoise",
    "mediumvioletred",
    "midnightblue",
    "mintcream",
    "mistyrose",
    "moccasin",
    "navajowhite",
    "navy",
    "oldlace",
    "olive",
    "olivedrab",
    "orange",
    "orangered",
    "orchid",
    "palegoldenrod",
    "palegreen",
    "paleturquoise",
    "palevioletred",
    "papayawhip",
    "peachpuff",
    "peru",
    "pink",
    "plum",
    "powderblue",
    "purple",
    "red",
    "rosybrown",
    "royalblue",
    "saddlebrown",
    "salmon",
    "sandybrown",
    "seagreen",
    "seashell",
    "sienna",
    "silver",
    "skyblue",
    "slateblue",
    "slategray",
    "slategrey",
    "snow",
    "springgreen",
    "steelblue",
    "tan",
    "teal",
    "thistle",
    "tomato",
    "turquoise",
    "violet",
    "wheat",
    "white",
    "whitesmoke",
    "yellow",
    "yellowgreen",
]

# --- メイン処理 ---
n = len(colors)
rows = (n + cols - 1) // cols  # 必要な行数(切り上げ)

# 1枚のタイル(XY中心、Zは厚みの半分だけ上げて「xy平面上」に乗せる)
base_tile = translate(
    cube([tile_x, tile_y, tile_z], center=True),
    [0, 0, tile_z / 2.0]
)

# グリッドを原点中心に配置するためのオフセット
x0 = -((cols - 1) * pitch_x) / 2.0
y0 =  ((rows - 1) * pitch_y) / 2.0

result = None

for i, name in enumerate(colors):
    c = i % cols
    r = i // cols

    x = x0 + c * pitch_x
    y = y0 - r * pitch_y

    one = translate(color(base_tile, name), [x, y, 0])

    result = one if result is None else (result + one)

show(result)

※もし「すき間なし(ぴったり隣接)」が良ければ、gap = 0.0 にしてください。

左上から順に並んでいます。

スクリーンショット 2026-08-17 120504.png

スクリーンショット 2026-08-17 121119.png

04. 高さ30, 幅120, 奥行55、各辺は、面取りC5のブロック

完成コード(PythonSCAD)

# ==========================================
# C5 面取りブロック
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
width    = 120.0   # 幅(X方向)
depth    = 55.0    # 奥行き(Y方向)
height   = 30.0    # 高さ(Z方向)
chamfer  = 5.0     # 面取り C寸法(C5)

# --- 関数(モジュール)定義 ---

def octahedron(c):
    """
    面取り用の「八面体」(L1ボール)。
    minkowski(直方体, 八面体) にすると、全エッジが45°で均一に面取りされます。
    """
    pts = [
        [ c, 0, 0],  # 0
        [-c, 0, 0],  # 1
        [0,  c, 0],  # 2
        [0, -c, 0],  # 3
        [0, 0,  c],  # 4
        [0, 0, -c],  # 5
    ]
    faces = [
        [4, 0, 2],
        [4, 2, 1],
        [4, 1, 3],
        [4, 3, 0],
        [5, 2, 0],
        [5, 1, 2],
        [5, 3, 1],
        [5, 0, 3],
    ]
    return polyhedron(points=pts, faces=faces)

def chamfered_block(w, d, h, c):
    """
    目標外形 (w,d,h) のC面取りブロックを作る。
    外形を狙い通りにするため、芯の直方体は各方向 2*c 小さくしてから minkowski します。
    """
    core_w = w - 2.0 * c
    core_d = d - 2.0 * c
    core_h = h - 2.0 * c

    if core_w <= 0 or core_d <= 0 or core_h <= 0:
        raise ValueError("chamfer が大きすぎます。w,d,h に対して 2*chamfer 未満にしてください。")

    core = cube([core_w, core_d, core_h], center=True)
    bevel_tool = octahedron(c)

    return minkowski(core, bevel_tool)

# --- メイン処理 ---

result = chamfered_block(width, depth, height, chamfer)

show(result)

(直方体を八面体でMinkowski)だと全エッジが均一に45°面取りになり、角(頂点)も自然に面取り面が付きます。
スクリーンショット 2026-08-17 122053.png

05. 「角(頂点)は面取りしない」版

完成コード(PythonSCAD)

# ==========================================
# 角(頂点)を残す「止まり面取り(C面取り)」ブロック
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
width    = 120.0   # 幅(X方向)
depth    = 55.0    # 奥行き(Y方向)
height   = 30.0    # 高さ(Z方向)

chamfer  = 4.0     # 面取り C寸法(C5)
setback  = 0     # 頂点から面取りを止める距離(これを >0 にすると「頂点は面取りしない」になる)

eps      = 0.05    # チラつき防止&貫通保証の微小値

# --- 関数(モジュール)定義 ---

def tri_prism_along_axis(c, L, axis, d1, d2):
    """
    45°面取り用の「直角三角形プリズム」カッターを作る(中心配置)。
    axis: 'x'/'y'/'z' ・・・プリズムの長手方向
    d1, d2: 面取りが伸びる向き(内側方向)を決める符号付き量(例: -c, +c)
            axisが'z'のとき d1=X方向, d2=Y方向
            axisが'x'のとき d1=Y方向, d2=Z方向
            axisが'y'のとき d1=X方向, d2=Z方向
    """
    # 長手方向は中心基準
    a0 = -L / 2.0
    a1 =  L / 2.0

    if axis == "z":
        # 右角頂点を(0,0)に置き、XとYへ d1, d2 伸ばす。Zに沿って伸ばす。
        pts = [
            [0,   0,   a0],
            [d1,  0,   a0],
            [0,   d2,  a0],
            [0,   0,   a1],
            [d1,  0,   a1],
            [0,   d2,  a1],
        ]
    elif axis == "x":
        # Xに沿って伸ばし、断面はYZ
        pts = [
            [a0,  0,   0],
            [a0,  d1,  0],
            [a0,  0,   d2],
            [a1,  0,   0],
            [a1,  d1,  0],
            [a1,  0,   d2],
        ]
    elif axis == "y":
        # Yに沿って伸ばし、断面はXZ
        pts = [
            [0,   a0,  0],
            [d1,  a0,  0],
            [0,   a0,  d2],
            [0,   a1,  0],
            [d1,  a1,  0],
            [0,   a1,  d2],
        ]
    else:
        raise ValueError("axis must be 'x', 'y', or 'z'")

    # 三角形プリズム(側面の四角は三角2枚に分割して安全に)
    faces = [
        # bottom / top
        [0, 1, 2],
        [3, 5, 4],

        # side faces (triangulated)
        [0, 1, 4], [0, 4, 3],
        [1, 2, 5], [1, 5, 4],
        [2, 0, 3], [2, 3, 5],
    ]
    return polyhedron(points=pts, faces=faces)

def edge_chamfer_cutter(w, d, h, c, sb, eps_val):
    """
    12本のエッジに対して「止まり面取り」カッターを全部まとめて返す。
    sb(setback) > 0 にすると、頂点(8箇所)の直近は削られず、角が尖ったまま残る。
    """
    # 有効寸法(少しだけ大きくして、面の一致によるチラつきを避ける)
    c_eff = c + eps_val

    Lx = w - 2.0 * sb
    Ly = d - 2.0 * sb
    Lz = h - 2.0 * sb
    if Lx <= 0 or Ly <= 0 or Lz <= 0:
        raise ValueError("setback が大きすぎます。各寸法に対して setback < 対応辺長/2 にしてください。")

    # 端面まで確実に削るため、長さだけ少しだけ延ばす(setbackは eps より十分大きい想定)
    Lx_cut = Lx + 2.0 * eps_val
    Ly_cut = Ly + 2.0 * eps_val
    Lz_cut = Lz + 2.0 * eps_val

    cutter = None

    # ---- Z方向のエッジ(4本):(x=±w/2, y=±d/2) ----
    for sx in [-1.0, 1.0]:
        for sy in [-1.0, 1.0]:
            # 内側方向は -sx, -sy
            d1 = (-sx) * c_eff  # X方向
            d2 = (-sy) * c_eff  # Y方向
            wedge = tri_prism_along_axis(c_eff, Lz_cut, "z", d1, d2)

            # 右角頂点を「少し外側」にずらして、面一致を避ける
            px = sx * (w / 2.0 + eps_val)
            py = sy * (d / 2.0 + eps_val)
            wedge = translate(wedge, [px, py, 0])

            cutter = wedge if cutter is None else (cutter + wedge)

    # ---- X方向のエッジ(4本):(y=±d/2, z=±h/2) ----
    for sy in [-1.0, 1.0]:
        for sz in [-1.0, 1.0]:
            # 内側方向は -sy, -sz
            d1 = (-sy) * c_eff  # Y方向
            d2 = (-sz) * c_eff  # Z方向
            wedge = tri_prism_along_axis(c_eff, Lx_cut, "x", d1, d2)

            py = sy * (d / 2.0 + eps_val)
            pz = sz * (h / 2.0 + eps_val)
            wedge = translate(wedge, [0, py, pz])

            cutter = cutter + wedge

    # ---- Y方向のエッジ(4本):(x=±w/2, z=±h/2) ----
    for sx in [-1.0, 1.0]:
        for sz in [-1.0, 1.0]:
            # 内側方向は -sx, -sz
            d1 = (-sx) * c_eff  # X方向
            d2 = (-sz) * c_eff  # Z方向
            wedge = tri_prism_along_axis(c_eff, Ly_cut, "y", d1, d2)

            px = sx * (w / 2.0 + eps_val)
            pz = sz * (h / 2.0 + eps_val)
            wedge = translate(wedge, [px, 0, pz])

            cutter = cutter + wedge

    return cutter

def make_block_with_edge_chamfer_only(w, d, h, c, sb, eps_val):
    """
    「エッジはC面取りするが、頂点は残す」ブロック。
    ※ これは“面取りが頂点まで到達しない(止まり面取り)”という意味での「頂点を面取りしない」です。
    """
    body = cube([w, d, h], center=True)
    cutter = edge_chamfer_cutter(w, d, h, c, sb, eps_val)
    return body - cutter

# --- メイン処理 ---

result = make_block_with_edge_chamfer_only(width, depth, height, chamfer, setback, eps)

show(result)

注意(設計として大事)
「エッジだけ面取りして、頂点は“完全にそのまま”」は、面取りが頂点まで届く限り幾何的に不可能です。なのでここでは機械加工でもよくある “止まり面取り(頂点の手前で面取りを止める)” として実装しています。
「setback を何mmにすべきか」を調整してみてください。

スクリーンショット 2026-08-17 123434.png

06. 高さ30, 幅120, 奥行55、上面、底面のコーナー面取りC6、他の辺は面取りC3のブロック

完成コード(PythonSCAD)

# ==========================================
# 2段面取りブロック(上面・底面C6、その他C3)
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
width   = 120.0   # X方向
depth   = 55.0    # Y方向
height  = 30.0    # Z方向

c_top   = 6.0     # 上面・底面の外周エッジ面取り(C6)
c_side  = 3.0     # 縦エッジ等の面取り(C3)

eps     = 0.15    # チラつき防止用マージン
fn_val  = 60      # 今回は円弧なし(将来拡張用)

# --- 関数(モジュール)定義 ---

def sgn(v):
    """符号(+1 / -1)"""
    return 1.0 if v >= 0 else -1.0

def wedge_along_x(leg, length_x, y_dir, z_dir):
    """
    X方向エッジ用の面取りカッター(45°)
    corner(原点)から (Y方向:y_dir) と (Z方向:z_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [-z_dir * leg, 0.0],   # 回転後 Z = -x になるので、x側でZ方向を制御
        [0.0, y_dir * leg],    # yは回転後もY方向
    ])
    prism = linear_extrude(tri, height=length_x)
    prism = translate(prism, [0.0, 0.0, -length_x / 2.0])  # 押し出し軸(元Z)でセンター化
    prism = rotate(prism, [0.0, 90.0, 0.0])                # 押し出し軸をXへ
    return prism

def wedge_along_y(leg, length_y, x_dir, z_dir):
    """
    Y方向エッジ用の面取りカッター(45°)
    corner(原点)から (X方向:x_dir) と (Z方向:z_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [x_dir * leg, 0.0],    # 回転後もX
        [0.0, z_dir * leg],    # 回転後 Z = y
    ])
    prism = linear_extrude(tri, height=length_y)
    prism = translate(prism, [0.0, 0.0, -length_y / 2.0])
    prism = rotate(prism, [90.0, 0.0, 0.0])                # 押し出し軸をYへ(符号は対称なのでOK)
    return prism

def wedge_along_z(leg, length_z, x_dir, y_dir):
    """
    Z方向(縦エッジ)用の面取りカッター(45°)
    corner(原点)から (X方向:x_dir) と (Y方向:y_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [x_dir * leg, 0.0],
        [0.0, y_dir * leg],
    ])
    prism = linear_extrude(tri, height=length_z)
    prism = translate(prism, [0.0, 0.0, -length_z / 2.0])
    return prism

def create_body(w, d, h):
    """本体(足す形状)"""
    return cube([w, d, h], center=True)

def create_cutter(w, d, h, c6, c3, eps):
    """面取り用カッター(引く形状)を + で1つにまとめる"""
    cutter = None

    # エッジ長(カッターは少し長めにして確実に貫通させる)
    len_x = w + 2.0 * eps
    len_y = d + 2.0 * eps

    # --- 上面(Z=+h/2)外周エッジ:C6 ---
    z_top = +h / 2.0
    z_dir_top = -1.0  # 上面から内側へは -Z

    # X方向エッジ(Y=±d/2, Z=+h/2)
    for y_edge in [+d / 2.0, -d / 2.0]:
        y_dir = -sgn(y_edge)  # 外側から内側へ
        wdg = wedge_along_x(c6, len_x, y_dir=y_dir, z_dir=z_dir_top)
        wdg = translate(wdg, [0.0, y_edge, z_top])
        cutter = wdg if cutter is None else (cutter + wdg)

    # Y方向エッジ(X=±w/2, Z=+h/2)
    for x_edge in [+w / 2.0, -w / 2.0]:
        x_dir = -sgn(x_edge)
        wdg = wedge_along_y(c6, len_y, x_dir=x_dir, z_dir=z_dir_top)
        wdg = translate(wdg, [x_edge, 0.0, z_top])
        cutter = cutter + wdg

    # --- 底面(Z=-h/2)外周エッジ:C6 ---
    z_bot = -h / 2.0
    z_dir_bot = +1.0  # 底面から内側へは +Z

    # X方向エッジ(Y=±d/2, Z=-h/2)
    for y_edge in [+d / 2.0, -d / 2.0]:
        y_dir = -sgn(y_edge)
        wdg = wedge_along_x(c6, len_x, y_dir=y_dir, z_dir=z_dir_bot)
        wdg = translate(wdg, [0.0, y_edge, z_bot])
        cutter = cutter + wdg

    # Y方向エッジ(X=±w/2, Z=-h/2)
    for x_edge in [+w / 2.0, -w / 2.0]:
        x_dir = -sgn(x_edge)
        wdg = wedge_along_y(c6, len_y, x_dir=x_dir, z_dir=z_dir_bot)
        wdg = translate(wdg, [x_edge, 0.0, z_bot])
        cutter = cutter + wdg

    # --- 縦エッジ(Z方向の4本):C3 ---
    # 上下面のC6領域に食い込みすぎないよう、縦方向のカッター長を「中央の帯」に制限
    mid_len_z = (h - 2.0 * c6)
    if mid_len_z <= 0:
        # 高さが低すぎて「上下面C6」が干渉する場合は、安全側で縦面取りを無効化
        mid_len_z = 0.0

    len_z = mid_len_z + 2.0 * eps  # わずかに余裕
    if len_z > 0:
        for x_edge in [+w / 2.0, -w / 2.0]:
            for y_edge in [+d / 2.0, -d / 2.0]:
                x_dir = -sgn(x_edge)
                y_dir = -sgn(y_edge)
                wdg = wedge_along_z(c3, len_z, x_dir=x_dir, y_dir=y_dir)
                wdg = translate(wdg, [x_edge, y_edge, 0.0])  # 中央帯なのでZ=0中心
                cutter = cutter + wdg

    return cutter

# --- メイン処理 ---

body = create_body(width, depth, height)
cutter = create_cutter(width, depth, height, c_top, c_side, eps)

result = body - cutter

show(result)

スクリーンショット 2026-08-17 124129.png

07. previewすると、上面と底面にかぶせた感じで、各コーナーは庇のように、直角に出ています。各コーナーの面取りに合わせて、出っ張りをカットする

完成コード(PythonSCAD)※コーナーの「庇」をカットする版

上面/底面のC6エッジ面取りだけだと、**角(頂点)が未加工で直角の出っ張り(庇)**が残ります。
なので **上下面の4隅(計8頂点)に「頂点用の面取りカッター(四面体)」**を追加して、角もC6に揃えてカットします。

# ==========================================
# 2段面取りブロック(上面・底面C6、他エッジC3)
# コーナー(頂点)の庇を頂点カッターで除去
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
width   = 120.0   # X方向
depth   = 55.0    # Y方向
height  = 30.0    # Z方向

c_top   = 6.0     # 上面・底面のエッジ面取り(C6)
c_side  = 3.0     # その他(主に縦エッジ)の面取り(C3)

eps     = 0.15    # チラつき防止用マージン(カッターを少しはみ出させる)

# --- 関数(モジュール)定義 ---

def sgn(v):
    """符号(+1 / -1)"""
    return 1.0 if v >= 0 else -1.0


def wedge_along_x(leg, length_x, y_dir, z_dir):
    """
    X方向エッジ用の面取りカッター(45°)
    corner(原点)から (Y方向:y_dir) と (Z方向:z_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [-z_dir * leg, 0.0],   # 回転後 Z = -x になるので、x側でZ方向を制御
        [0.0, y_dir * leg],
    ])
    prism = linear_extrude(tri, height=length_x)
    prism = translate(prism, [0.0, 0.0, -length_x / 2.0])
    prism = rotate(prism, [0.0, 90.0, 0.0])  # 押し出し軸をXへ
    return prism


def wedge_along_y(leg, length_y, x_dir, z_dir):
    """
    Y方向エッジ用の面取りカッター(45°)
    corner(原点)から (X方向:x_dir) と (Z方向:z_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [x_dir * leg, 0.0],
        [0.0, z_dir * leg],
    ])
    prism = linear_extrude(tri, height=length_y)
    prism = translate(prism, [0.0, 0.0, -length_y / 2.0])
    prism = rotate(prism, [90.0, 0.0, 0.0])  # 押し出し軸をYへ
    return prism


def wedge_along_z(leg, length_z, x_dir, y_dir):
    """
    Z方向(縦エッジ)用の面取りカッター(45°)
    corner(原点)から (X方向:x_dir) と (Y方向:y_dir) に leg だけ食い込む三角柱
    """
    tri = polygon([
        [0.0, 0.0],
        [x_dir * leg, 0.0],
        [0.0, y_dir * leg],
    ])
    prism = linear_extrude(tri, height=length_z)
    prism = translate(prism, [0.0, 0.0, -length_z / 2.0])
    return prism


def corner_tetra(leg, x_in, y_in, z_in):
    """
    頂点(コーナー)用の面取りカッター:四面体(テトラ)
    原点(頂点)から、内側方向 x_in/y_in/z_in に leg 進んだ3点で作る
    """
    pts = [
        [0.0, 0.0, 0.0],
        [x_in * leg, 0.0, 0.0],
        [0.0, y_in * leg, 0.0],
        [0.0, 0.0, z_in * leg],
    ]
    faces = [
        [0, 2, 1],
        [0, 1, 3],
        [0, 3, 2],
        [1, 2, 3],
    ]
    return polyhedron(points=pts, faces=faces)


def create_body(w, d, h):
    """本体(足す形状)"""
    return cube([w, d, h], center=True)


def create_cutter(w, d, h, c6, c3, eps):
    """面取り用カッター(引く形状)を + で1つにまとめる"""
    cutter = None

    # エッジ長(カッターは少し長めにして確実に貫通)
    len_x = w + 2.0 * eps
    len_y = d + 2.0 * eps
    len_z = h + 2.0 * eps

    # --- 上面(Z=+h/2)外周エッジ:C6 ---
    z_top = +h / 2.0
    z_dir_top = -1.0  # 上面から内側へは -Z

    # X方向エッジ(Y=±d/2, Z=+h/2)
    for y_edge in [+d / 2.0, -d / 2.0]:
        y_in = -sgn(y_edge)
        wdg = wedge_along_x(c6, len_x, y_dir=y_in, z_dir=z_dir_top)
        wdg = translate(wdg, [0.0, y_edge, z_top])
        cutter = wdg if cutter is None else (cutter + wdg)

    # Y方向エッジ(X=±w/2, Z=+h/2)
    for x_edge in [+w / 2.0, -w / 2.0]:
        x_in = -sgn(x_edge)
        wdg = wedge_along_y(c6, len_y, x_dir=x_in, z_dir=z_dir_top)
        wdg = translate(wdg, [x_edge, 0.0, z_top])
        cutter = cutter + wdg

    # --- 底面(Z=-h/2)外周エッジ:C6 ---
    z_bot = -h / 2.0
    z_dir_bot = +1.0  # 底面から内側へは +Z

    # X方向エッジ(Y=±d/2, Z=-h/2)
    for y_edge in [+d / 2.0, -d / 2.0]:
        y_in = -sgn(y_edge)
        wdg = wedge_along_x(c6, len_x, y_dir=y_in, z_dir=z_dir_bot)
        wdg = translate(wdg, [0.0, y_edge, z_bot])
        cutter = cutter + wdg

    # Y方向エッジ(X=±w/2, Z=-h/2)
    for x_edge in [+w / 2.0, -w / 2.0]:
        x_in = -sgn(x_edge)
        wdg = wedge_along_y(c6, len_y, x_dir=x_in, z_dir=z_dir_bot)
        wdg = translate(wdg, [x_edge, 0.0, z_bot])
        cutter = cutter + wdg

    # --- 縦エッジ(Z方向の4本):C3(高さ方向に全部)---
    # 「他の辺はC3」なので、縦エッジは全高に適用しておく方が自然です
    for x_edge in [+w / 2.0, -w / 2.0]:
        for y_edge in [+d / 2.0, -d / 2.0]:
            x_in = -sgn(x_edge)
            y_in = -sgn(y_edge)
            wdg = wedge_along_z(c3, len_z, x_dir=x_in, y_dir=y_in)
            wdg = translate(wdg, [x_edge, y_edge, 0.0])
            cutter = cutter + wdg

    # --- ★追加:上下面の「頂点(コーナー)」用C6カッター(庇を消す本命)---
    # 角の一点が残ると、プレビューで「直角の出っ張り」に見えます
    for z_face, z_in in [(+h / 2.0, -1.0), (-h / 2.0, +1.0)]:
        for x_edge in [+w / 2.0, -w / 2.0]:
            for y_edge in [+d / 2.0, -d / 2.0]:
                x_in = -sgn(x_edge)
                y_in = -sgn(y_edge)

                tet = corner_tetra(c6, x_in=x_in, y_in=y_in, z_in=z_in)

                # カッターが「外側にも少し」出るように頂点をepsだけ外へ逃がす
                corner_pos = [
                    x_edge - x_in * eps,
                    y_edge - y_in * eps,
                    z_face - z_in * eps,
                ]
                tet = translate(tet, corner_pos)
                cutter = cutter + tet

    return cutter


# --- メイン処理 ---

body = create_body(width, depth, height)
cutter = create_cutter(width, depth, height, c_top, c_side, eps)

result = body - cutter

show(result)

スクリーンショット 2026-08-17 124542.png

2. 何が変わったか

  • 以前:上面/底面の「エッジ」だけC6 → 頂点(角の一点)が残って庇っぽい直角が出る

  • 今回:頂点用の四面体カッターも追加 → 上面/底面の角もスパッと落ちて、見た目が自然になります

  • カッター作成 : cutter = create_cutter(width, depth, height, c_top, c_side, eps)
    スクリーンショット 2026-08-17 125843.png

c_top   = 3.0     # 上面・底面のエッジ面取り(C3)
c_side  = 6.0     # その他(主に縦エッジ)の面取り(C6)

スクリーンショット 2026-08-17 124723.png

08. 例:カッター作成・確認する

from pythonscad import *
def octahedron(c):
    """
    面取り用の「八面体」(L1ボール)。
    minkowski(直方体, 八面体) にすると、全エッジが45°で均一に面取りされます。
    """
    pts = [
        [ c, 0, 0],  # 0
        [-c, 0, 0],  # 1
        [0,  c, 0],  # 2
        [0, -c, 0],  # 3
        [0, 0,  c],  # 4
        [0, 0, -c],  # 5
    ]
    faces = [
        [4, 0, 2],
        [4, 2, 1],
        [4, 1, 3],
        [4, 3, 0],
        [5, 2, 0],
        [5, 1, 2],
        [5, 3, 1],
        [5, 0, 3],
    ]
    return polyhedron(points=pts, faces=faces)

c=6
bevel_tool = octahedron(c)
show(bevel_tool)

image.png

09. cube(5)を間隔3で、xy方向にカラフルな色で、3 x 3で並べる

1. 完成コード(PythonSCAD)

# ==========================================
# 3x3 カラフルキューブ配列
# 作成日: 2026/08/17
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
cube_size = 5.0      # 1個の立方体の一辺
gap_xy    = 3.0      # 立方体どうしの「すき間」
step      = cube_size + gap_xy  # 中心間ピッチ
z0        = 0.0

colors_9 = [
    "tomato", "gold", "limegreen",
    "deepskyblue", "royalblue", "violet",
    "orange", "springgreen", "hotpink",
]

# --- メイン処理 ---
result = None
idx = 0

for ix in [-1, 0, 1]:
    for iy in [-1, 0, 1]:
        x = ix * step
        y = iy * step

        block = cube([cube_size, cube_size, cube_size], center=True)
        block = translate(block, [x, y, z0])
        block = color(block, colors_9[idx])

        result = block if result is None else (result + block)
        idx += 1

show(result)

スクリーンショット 2026-08-17 163916.png

2. 完成コード(PythonSCAD)

# ==========================================
# 3x3 カラフルキューブ配列(XY方向)
# 作成日: 2026/07/25
# ==========================================

from pythonscad import *

# --- パラメータ設定 (単位: mm) ---
cube_size = 5    # 立方体の一辺
gap       = 3    # 立方体どうしの「すき間」
n_x       = 3    # X方向の個数
n_y       = 3    # Y方向の個数

# 配置ピッチ(中心-中心の距離)
pitch = cube_size + gap

# カラーパレット(9色)
# ※ color(obj, [R,G,B]) は 0.0〜1.0 の範囲で指定します
palette = [
    [1.0, 0.2, 0.2],  # 赤
    [1.0, 0.6, 0.2],  # オレンジ
    [1.0, 1.0, 0.2],  # 黄
    [0.2, 1.0, 0.2],  # 緑
    [0.2, 1.0, 1.0],  # シアン
    [0.2, 0.6, 1.0],  # 水色
    [0.2, 0.2, 1.0],  # 青
    [0.7, 0.2, 1.0],  # 紫
    [1.0, 0.2, 0.7],  # ピンク
]

# --- メイン処理 ---
result = None
idx = 0

for ix in range(n_x):
    for iy in range(n_y):
        # 1) 立方体(center=True で原点中心の形状にする)
        one = cube([cube_size, cube_size, cube_size], center=True)

        # 2) 色を付ける(9個なので palette を順番に割り当て)
        one = color(one, palette[idx % len(palette)])

        # 3) XY方向へ等間隔に並べる(全体が原点中心になるようにオフセット)
        x = (ix - (n_x - 1) / 2) * pitch
        y = (iy - (n_y - 1) / 2) * pitch
        one = translate(one, [x, y, 0])

        # 4) 形状を足し合わせて集合にする(+ 演算子で結合)
        result = one if result is None else (result + one)

        idx += 1

# 3Dプレビュー表示
show(result)

必要なら、「色を規則的(X方向が赤→青のグラデーション等)」にするなど、考えてみましょう。
スクリーンショット 2026-08-17 164017.png

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?