今回は色々な図形を組み合わせて
デジタルアートを作ってみました


解説動画はこちら


 
はじめに

今回はPythonで作図できる図形を組み合わせて
デジタルアートを作るものです

今回紹介する図形は以下です。

リサージュ図形
ローズ曲線
スピログラフ
ヒルベルト曲線
ドラゴンカーブ
ペンローズタイル(没)
トロコイド曲線
曼荼羅

早速図形を描いてみましょう。


ライブラリのインストール 

今回はGoogle Colabを用いて作図します。
日本語は表示できないので
日本語表示のライブラリをインストールしておいてください。
pip install japanize_matplotlib







リサージュ図形

二つの正弦波を組み合わせてできる曲線です
周波数や位相の違いで様々なパターンが生成されます
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib

# パラメータ設定
A = 1
B = 1
a = 5
b = 4
delta = np.pi / 2

# tの範囲
t = np.linspace(0, 2 * np.pi, 1000)

# x, y の計算
x = A * np.sin(a * t + delta)
y = B * np.sin(b * t)

# 描画
plt.figure(figsize=(6, 6))
plt.plot(x, y)
plt.axis('off')
plt.show()
download


import numpy as np
import matplotlib.pyplot as plt

# 図形の数と色の設定
num_shapes = 10
colors = plt.cm.viridis(np.linspace(0, 1, num_shapes))

# 描画設定
plt.figure(figsize=(8, 8))

# 各リサージュ図形の描画
for i in range(num_shapes):
    A = 1
    B = 1
    a = np.random.randint(1, 10)
    b = np.random.randint(1, 10)
    delta = np.random.uniform(0, 2 * np.pi)
    
    t = np.linspace(0, 2 * np.pi, 1000)
    x = A * np.sin(a * t + delta)
    y = B * np.sin(b * t)
    
    plt.plot(x, y, color=colors[i], alpha=0.7)

# グラフの設定
plt.title("複雑なリサージュアート")
plt.axis('equal')
plt.axis('off')
plt.show()
download-1


ローズ曲線

極座標を使って描く花のような形の曲線です
import numpy as np
import matplotlib.pyplot as plt

# パラメータ設定
k = 5  # kは整数または分数
a = 1  # 振幅

# tの範囲
t = np.linspace(0, 2 * np.pi, 1000)

# x, y の計算
x = a * np.cos(k * t) * np.cos(t)
y = a * np.cos(k * t) * np.sin(t)

# 描画
plt.figure(figsize=(6, 6))
plt.plot(x, y, color='magenta')
plt.title("ローズ曲線")
plt.axis('equal')
plt.axis('off')
plt.show()
download-2


import numpy as np
import matplotlib.pyplot as plt

# パラメータのリスト
k_values = np.arange(1, 6)
a_values = np.linspace(0.5, 2.5, 5)

# プロット設定
fig, axes = plt.subplots(5, 5, figsize=(6,6))
fig.suptitle("ローズ曲線のグリッド", fontsize=16)

# 各プロットの描画
for i, a in enumerate(a_values):
    for j, k in enumerate(k_values):
        t = np.linspace(0, 2 * np.pi, 1000)
        x = a * np.cos(k * t) * np.cos(t)
        y = a * np.cos(k * t) * np.sin(t)
        
        ax = axes[i, j]
        ax.plot(x, y, color='magenta')
        ax.set_title(f'k={k}, a={a:.1f}')
        ax.axis('equal')
        ax.set_xlim(-2.5, 2.5)
        ax.set_ylim(-2.5, 2.5)
        ax.axis('off')  # 軸を非表示

plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()
download-3



スピログラフ

歯車を使って描くような複雑な曲線です
パラメータを変えることで多様な形ができます

小学生の時とかに作図キットを持っている人もいたかも
あれをPythonで再現します
import numpy as np
import matplotlib.pyplot as plt

# スピログラフのパラメータ
R = 100  # 大きい円の半径
r = 30   # 小さい円の半径
d = 160   # 描画する点の距離

# 最大公約数を使って完全なループを描画
gcd = np.gcd(R, r)
lcm = (R * r) // gcd
t = np.linspace(0, 2 * np.pi * (r // gcd), 1000 * (r // gcd))

# 描画設定
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-R - r - d, R + r + d)
ax.set_ylim(-R - r - d, R + r + d)
ax.set_aspect('equal')
ax.axis('off')

# スピログラフの描画
x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)
ax.plot(x, y, color='blue')
plt.show()
download-4


import numpy as np
import matplotlib.pyplot as plt

# スピログラフのパラメータ
r = 30  # 小さい円の半径

# 描画設定
fig, axs = plt.subplots(5, 5, figsize=(8,8))
fig.subplots_adjust(hspace=0.3, wspace=0.3)

# Rとdを変化させてプロット
R_values = np.linspace(50, 150, 5)
d_values = np.linspace(20, 80, 5)

for i, R in enumerate(R_values):
    for j, d in enumerate(d_values):
        ax = axs[i, j]
        gcd = np.gcd(int(R), r)
        t = np.linspace(0, 2 * np.pi * (r // gcd), 1000 * (r // gcd))
        
        x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
        y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)
        
        ax.plot(x, y, color='blue')
        ax.set_xlim(-R - r - d, R + r + d)
        ax.set_ylim(-R - r - d, R + r + d)
        ax.set_aspect('equal')
        ax.axis('off')
        ax.set_title(f"R={int(R)}, d={int(d)}", fontsize=8)

plt.show()
download-5

import numpy as np
import matplotlib.pyplot as plt

# スピログラフのパラメータ
R = 150  # 大きい円の半径
r1 = 30  # 中間の円の半径
r2 = 15  # 小さい円の半径
d = 50   # 描画する点の距離

# 描画設定
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-R - r1 - r2 - d, R + r1 + r2 + d)
ax.set_ylim(-R - r1 - r2 - d, R + r1 + r2 + d)
ax.set_aspect('equal')
ax.axis('off')

# スピログラフの描画
gcd = np.gcd(np.gcd(R, r1), r2)
t = np.linspace(0, 2 * np.pi * (r2 // gcd), 1000 * (r2 // gcd))

x = (R - r1) * np.cos(t) + d * np.cos((R - r1) / r1 * t) + r2 * np.cos((R - r1) / r2 * t)
y = (R - r1) * np.sin(t) - d * np.sin((R - r1) / r1 * t) - r2 * np.sin((R - r1) / r2 * t)

ax.plot(x, y, color='blue')
plt.show()
download-6

import numpy as np
import matplotlib.pyplot as plt

# パラメータ設定
R = 100  # 大きい円の半径
r = 30   # 小さい円の半径
d = 60   # 描画する点の距離
a = 5    # リサージュのx軸周波数
b = 4    # リサージュのy軸周波数
delta = np.pi / 2  # 位相差

# 描画設定
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_xlim(-R - r - d, R + r + d)
ax.set_ylim(-R - r - d, R + r + d)
ax.set_aspect('equal')
ax.axis('off')

# スピログラフの方程式
gcd = np.gcd(R, r)
t = np.linspace(0, 2 * np.pi * (r // gcd), 1000 * (r // gcd))

x_spiro = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y_spiro = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)

# リサージュの方程式
x_lissajous = np.sin(a * t + delta)
y_lissajous = np.sin(b * t)

# 合成
x = x_spiro + x_lissajous * 10
y = y_spiro + y_lissajous * 10

# 図形の描画
ax.plot(x, y, color='blue')
plt.show()
download-7




ヒルベルト曲線

空間を埋め尽くすフラクタルの一種で
曲線が空間を埋めるように描かれます
import matplotlib.pyplot as plt

def hilbert_curve(x0, y0, xi, xj, yi, yj, n):
    if n <= 0:
        x_mid = x0 + (xi + yi) // 2
        y_mid = y0 + (xj + yj) // 2
        points.append((x_mid, y_mid))
    else:
        hilbert_curve(x0, y0, yi // 2, yj // 2, xi // 2, xj // 2, n - 1)
        hilbert_curve(x0 + xi // 2, y0 + xj // 2, xi // 2, xj // 2, yi // 2, yj // 2, n - 1)
        hilbert_curve(x0 + xi // 2 + yi // 2, y0 + xj // 2 + yj // 2, xi // 2, xj // 2, yi // 2, yj // 2, n - 1)
        hilbert_curve(x0 + xi // 2 + yi, y0 + xj // 2 + yj, -yi // 2, -yj // 2, -xi // 2, -xj // 2, n - 1)

# 描画設定
order = 6  # ヒルベルト曲線の階数
points = []
hilbert_curve(0, 0, 2**order, 0, 0, 2**order, order)

# プロット
x_vals, y_vals = zip(*points)
plt.plot(x_vals, y_vals)
plt.title(f'Hilbert Curve of Order {order}')
plt.axis('equal')
plt.axis('off')
plt.show()
download-8





ドラゴンカーブ

繰り返し折り返すことで生成されるフラクタル曲線です
中華のどんぶりに描かれている模様っぽいものです
import matplotlib.pyplot as plt
from math import cos, sin, radians

def dragon_curve(order, step=1):
    def recursive_dragon(n, angle, sign):
        if n == 0:
            forward(step)
        else:
            recursive_dragon(n - 1, angle, 1)
            turn(sign * angle)
            recursive_dragon(n - 1, angle, -1)

    def forward(dist):
        nonlocal x, y
        x += dist * directions[0]
        y += dist * directions[1]
        points.append((x, y))

    def turn(angle):
        cos_theta = cos(angle)
        sin_theta = sin(angle)
        directions[0], directions[1] = (
            directions[0] * cos_theta - directions[1] * sin_theta,
            directions[0] * sin_theta + directions[1] * cos_theta
        )

    # 初期化
    x, y = 0, 0
    directions = [1, 0]  # 初期方向
    points = [(x, y)]

    # ドラゴンカーブの生成
    recursive_dragon(order, radians(90), 1)
    return points

# 描画
order = 10  # ドラゴンカーブの階数
points = dragon_curve(order)
x_vals, y_vals = zip(*points)

plt.plot(x_vals, y_vals)
plt.title(f'Dragon Curve of Order {order}')
plt.axis('equal')
plt.axis('off')
plt.show()
download-9





ペンローズタイル(没)

本来は非周期的に平面を埋め尽くすタイルのパターンですが
これはちょいとちがっちゃってます
import matplotlib.pyplot as plt
import numpy as np

def draw_kite(ax, x, y, angle, size):
    # カイトの4つの頂点を計算
    points = np.array([
        [0, 0],
        [np.cos(angle), np.sin(angle)],
        [np.cos(angle + np.pi / 5), np.sin(angle + np.pi / 5)],
        [np.cos(angle - np.pi / 5), np.sin(angle - np.pi / 5)]
    ]) * size
    points += [x, y]
    ax.fill(points[:, 0], points[:, 1], edgecolor='black', fill=False)

def draw_dart(ax, x, y, angle, size):
    # ダートの4つの頂点を計算
    points = np.array([
        [0, 0],
        [np.cos(angle), np.sin(angle)],
        [np.cos(angle + np.pi / 5) / 2, np.sin(angle + np.pi / 5) / 2],
        [np.cos(angle - np.pi / 5) / 2, np.sin(angle - np.pi / 5) / 2]
    ]) * size
    points += [x, y]
    ax.fill(points[:, 0], points[:, 1], edgecolor='black', fill=False)

def draw_penrose_tiling(order, size):
    fig, ax = plt.subplots()
    ax.set_aspect('equal')
    ax.axis('off')

    for i in range(order):
        angle = i * 2 * np.pi / order
        draw_kite(ax, 0, 0, angle, size)
        draw_dart(ax, 0, 0, angle, size)

    plt.show()

# 描画
order = 30
size = 1000
draw_penrose_tiling(order, size)
download-10


import matplotlib.pyplot as plt
import numpy as np

def draw_penrose(ax, x, y, angle, size, depth):
    if depth == 0:
        return
    
    golden_ratio = (1 + np.sqrt(5)) / 2
    new_size = size / golden_ratio

    # パチ形の描画
    points = np.array([
        [0, 0],
        [np.cos(angle) * size, np.sin(angle) * size],
        [np.cos(angle + 2 * np.pi / 5) * size, np.sin(angle + 2 * np.pi / 5) * size],
        [np.cos(angle - 2 * np.pi / 5) * new_size, np.sin(angle - 2 * np.pi / 5) * new_size]
    ])
    points += np.array([x, y])
    ax.fill(points[:, 0], points[:, 1], edgecolor='black', fill=False)

    # 再帰的に描画
    for i in range(5):
        new_angle = angle + i * 2 * np.pi / 5
        draw_penrose(ax, points[i % 4, 0], points[i % 4, 1], new_angle, new_size, depth - 1)

def plot_penrose_tiling(size, depth):
    fig, ax = plt.subplots()
    ax.set_aspect('equal')
    ax.axis('off')
    draw_penrose(ax, 0, 0, 0, size, depth)
    plt.show()

# 描画
size = 300
depth = 2
plot_penrose_tiling(size, depth)
download-11


トロコイド曲線

円が他の円の内側や外側を転がることで描かれる曲線です
ほぼほぼスピログラフですね
import matplotlib.pyplot as plt
import numpy as np

# トロコイド曲線のパラメータ
R = 5  # 大きな円の半径
r = 3  # 小さな円の半径
d = 5  # 描画する点の距離

# tの範囲
t = np.linspace(0, 10 * np.pi, 1000)

# トロコイド曲線の方程式
x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)

# 描画
plt.plot(x, y)
plt.title('Trochoid Curve')
plt.axis('equal')
plt.axis('off')
plt.show()
download-12

import matplotlib.pyplot as plt
import numpy as np

# 固定パラメータ
R = 6  # 大きな円の半径

# tの範囲
t = np.linspace(0, 10 * np.pi, 1000)

# プロットの準備
fig, axes = plt.subplots(5, 5, figsize=(8,8))
fig.suptitle('Trochoid Curves')

# rとdを変化させてプロット
r_values = np.linspace(1, 5, 5)
d_values = np.linspace(1, 5, 5)

for i, r in enumerate(r_values):
    for j, d in enumerate(d_values):
        x = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
        y = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)
        
        ax = axes[i, j]
        ax.plot(x, y)
        ax.set_title(f'r={r:.1f}, d={d:.1f}')
        ax.set_aspect('equal')
        ax.axis('off')

plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
download-13
import matplotlib.pyplot as plt
import numpy as np

# パラメータ設定
R = 5  # スピログラフ用の大きな円の半径
r = 3  # スピログラフ用の小さな円の半径
d = 5  # トロコイド用の点の距離

A = 5  # リサージュ曲線の振幅
B = 4
a = 3  # リサージュ曲線のx軸方向の周波数
b = 2  # リサージュ曲線のy軸方向の周波数
delta = np.pi / 2  # リサージュ曲線の位相差

# tの範囲
t = np.linspace(0, 10 * np.pi, 1000)

# スピログラフ(トロコイド)曲線
x_spiro = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y_spiro = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)

# リサージュ曲線
x_lissajous = A * np.sin(a * t + delta)
y_lissajous = B * np.sin(b * t)

# 組み合わせた曲線
x_combined = x_spiro + x_lissajous
y_combined = y_spiro + y_lissajous

# 描画
plt.figure(figsize=(8, 8))
plt.plot(x_combined, y_combined, label='Combined Curve')
plt.title('Combined Curve of Spirograph, Lissajous, and Trochoid')
plt.axis('equal')
plt.axis('off')
plt.show()
download-14

import matplotlib.pyplot as plt
import numpy as np

# パラメータ設定
R = 5  # スピログラフ用の大きな円の半径
r = 3  # スピログラフ用の小さな円の半径
d = 5  # トロコイド用の点の距離

A = 5  # リサージュ曲線の振幅
B = 4
a = 3  # リサージュ曲線のx軸方向の周波数
b = 2  # リサージュ曲線のy軸方向の周波数
delta = np.pi / 2  # リサージュ曲線の位相差

n = 36 # 円周上の個数

# tの範囲
t = np.linspace(0, 10 * np.pi, 1000)

# スピログラフ(トロコイド)曲線
x_spiro = (R - r) * np.cos(t) + d * np.cos((R - r) / r * t)
y_spiro = (R - r) * np.sin(t) - d * np.sin((R - r) / r * t)

# リサージュ曲線
x_lissajous = A * np.sin(a * t + delta)
y_lissajous = B * np.sin(b * t)

# 組み合わせた曲線
x_combined = x_spiro + x_lissajous
y_combined = y_spiro + y_lissajous

# 円周上に配置するための角度
angles = np.linspace(0, 2 * np.pi, n, endpoint=False)

# 描画
plt.figure(figsize=(8, 8))

for angle in angles:
    # 回転行列を適用
    x_rotated = x_combined * np.cos(angle) - y_combined * np.sin(angle)
    y_rotated = x_combined * np.sin(angle) + y_combined * np.cos(angle)
    
    # 配置する中心を決定
    center_x = 15 * np.cos(angle)
    center_y = 15 * np.sin(angle)
    plt.plot(x_rotated + center_x, y_rotated + center_y, label=f'Angle {angle:.2f} rad')

plt.title(f'{n} Combined Curves on Circle')
plt.axis('equal')
plt.axis('off')
plt.show()
download-15




曼荼羅

同心円状や同心方形状に
円や方形の図形を配置した図形のことです
import matplotlib.pyplot as plt
import numpy as np

# 円の数とそれぞれの半径
num_circles = 12
radii = np.linspace(0.1, 5, num_circles)

# 角度の設定
angles = np.linspace(0, 2 * np.pi, 100)

# 描画
plt.figure(figsize=(8, 8))

for radius in radii:
    for angle in np.linspace(0, 2 * np.pi, num_circles, endpoint=False):
        # 各円の中心
        center_x = radius * np.cos(angle)
        center_y = radius * np.sin(angle)
        
        # 円を描画
        x = center_x + radius * np.cos(angles)
        y = center_y + radius * np.sin(angles)
        plt.plot(x, y, color='purple')

plt.title('Mandala Pattern')
plt.axis('equal')
plt.axis('off')
plt.show()
download-16


import matplotlib.pyplot as plt
import numpy as np

# 円の数とそれぞれの半径
num_layers = 36
num_circles_per_layer = 12
radii = np.linspace(0.5, 5, num_layers)

# 使用する色
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']

# 描画
plt.figure(figsize=(8, 8))

for i, radius in enumerate(radii):
    for angle in np.linspace(0, 2 * np.pi, num_circles_per_layer, endpoint=False):
        # 各円の中心
        center_x = radius * np.cos(angle)
        center_y = radius * np.sin(angle)
        
        # 円を描画
        angles_circle = np.linspace(0, 2 * np.pi, 100)
        x = center_x + radius * 0.3 * np.cos(angles_circle)
        y = center_y + radius * 0.3 * np.sin(angles_circle)
        plt.plot(x, y, color=colors[i % len(colors)])

plt.title('Complex Mandala Pattern')
plt.axis('equal')
plt.axis('off')
plt.show()

download-17


import matplotlib.pyplot as plt
import numpy as np

# 円の数とそれぞれの半径
num_layers = 24
num_circles_per_layer = 24
radii = np.linspace(0.5, 5, num_layers)

# 使用する色
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']

# 描画
plt.figure(figsize=(8, 8))

for i, radius in enumerate(radii):
    for angle in np.linspace(0, 2 * np.pi, num_circles_per_layer, endpoint=False):
        # 各円の中心
        center_x = radius * np.cos(angle)
        center_y = radius * np.sin(angle)
        
        # 円を描画
        angles_circle = np.linspace(0, 2 * np.pi, 100)
        size_variation = 0.2 + 0.1 * np.sin(angle * num_layers)  # サイズに変化をつける
        x = center_x + radius * size_variation * np.cos(angles_circle)
        y = center_y + radius * size_variation * np.sin(angles_circle)
        
        # ランダムな線幅
        # linewidth = np.random.uniform(0.5, 2.5)
        linewidth = 1
        plt.plot(x, y, color=colors[i % len(colors)], linewidth=linewidth)

plt.title('Complex Mandala Pattern with Variations')
plt.axis('equal')
plt.axis('off')
plt.show()
download-18


こんな感じで、図形を組み合わせることで
様々な紋様を描く事ができます。

複雑な図形を組み合わせれば
それなりにアートっぽく
なって行くんではないでしょうか

気になった方はコピペして
色々パラメータ変えて
試してみてください

それでは。