vec2pt / all_projects / tags / contact

Chladni Patterns ()

Chladni Patterns

[sketch]

import numpy as np
from PIL import Image


def chladni_func(
    x: np.ndarray,
    y: np.ndarray,
    m: float = 1,
    n: float = 1,
    a: float = 1,
    b: float = 1,
) -> np.ndarray:
    """Chladni function

    Args:
        x (np.ndarray): X array
        y (np.ndarray): Y array
        m (float, optional): m parameter. Defaults to 1.
        n (float, optional): n parameter. Defaults to 1.
        a (float, optional): a parameter. Defaults to 1.
        b (float, optional): b parameter. Defaults to 1.

    Returns:
        np.ndarray: Output array
    """
    return abs(
        a * np.sin(np.pi * n * x) * np.sin(np.pi * m * y)
        + b * np.sin(np.pi * m * x) * np.sin(np.pi * n * y)
    )


def min_max_scaling(array: np.ndarray) -> np.ndarray:
    """Min-Max scaling

    Args:
        array (np.ndarray): Array

    Returns:
        np.ndarray: Min-Max Scaled array
    """
    min_val = np.min(array)
    max_val = np.max(array)
    return (array - min_val) / (max_val - min_val)


def chladni(
    width: int = 512,
    height: int = 512,
    m: float = 1,
    n: float = 1,
    a: float = 1,
    b: float = 1,
) -> np.ndarray:
    """Chladni

    Args:
        width (int, optional): Img width. Defaults to 512.
        height (int, optional): Img height. Defaults to 512.
        m (float, optional): m parameter. Defaults to 1.
        n (float, optional): n parameter. Defaults to 1.
        a (float, optional): a parameter. Defaults to 1.
        b (float, optional): b parameter. Defaults to 1.

    Returns:
        np.ndarray: Output array
    """
    shape = (height, width)
    x, y = np.meshgrid(
        np.linspace(-1.0, 1.0, shape[1]),
        np.linspace(-1.0, 1.0, shape[0]),
    )
    results = chladni_func(x, y, m=m, n=n, a=a, b=b)
    results = abs(min_max_scaling(results) - 1)  # Inversion
    results = results**7  # Contrast

    # Simple sand effect
    random_array = np.random.random(shape)
    results = np.where(random_array > 0.65, random_array, 0) * results

    return (min_max_scaling(results) * 255).astype(np.uint8)


if __name__ == "__main__":
    output = chladni(m=3, n=2, a=-1.29, b=2.26)
    Image.fromarray(output, "L").save("chladni.png")

sketch-chladni-01-animation.gif