3
4

パーティクルが色の類似性によってクラスタリングされ、似ている色のパーティクルが引き合うようすをながめる。

Last updated at Posted at 2024-09-26

パーティクルが色の類似性によってクラスタリングされ、似ている色のパーティクルが引き合う。

スクリーンショット 2024-09-27 045610.png

image.png

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>カラー パーティクル クラスタリング</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
    <script>
        let particles = [];
        const numParticles = 100; // パーティクルの数

        function setup() {
            createCanvas(windowWidth, windowHeight);
            noStroke(); // パーティクルに輪郭をつけない
            for (let i = 0; i < numParticles; i++) {
                // ランダムな位置にパーティクルを生成
                particles.push(new Particle(random(width), random(height)));
            }
        }

        function draw() {
            background(255); // 背景を白にリセット
            for (let p of particles) {
                p.move(); // パーティクルを移動させる
                p.display(); // パーティクルを表示する
                p.interact(particles); // 他のパーティクルとの相互作用
            }
        }

        class Particle {
            constructor(x, y) {
                this.pos = createVector(x, y); // パーティクルの位置
                this.vel = createVector(random(-1, 1), random(-1, 1)); // ランダムな速度
                this.acc = createVector(0, 0); // 初期加速度は0
                this.radius = random(10, 20); // ランダムなサイズ
                this.color = [random(255), random(255), random(255)]; // ランダムなカラー
            }

            move() {
                this.vel.add(this.acc); // 加速度を速度に加算
                this.pos.add(this.vel); // 速度を位置に加算
                this.acc.mult(0); // 加速度をリセット
                this.vel.limit(2); // 最大速度を2に制限

                // 画面の端に達したら反射
                if (this.pos.x < 0 || this.pos.x > width) this.vel.x *= -1;
                if (this.pos.y < 0 || this.pos.y > height) this.vel.y *= -1;
            }

            display() {
                // パーティクルを描画 (カラーとサイズ)
                fill(this.color[0], this.color[1], this.color[2]);
                ellipse(this.pos.x, this.pos.y, this.radius * 2);
            }

            interact(particles) {
                for (let other of particles) {
                    if (other !== this) {
                        // 他のパーティクルとの距離を計算
                        let distVect = p5.Vector.sub(other.pos, this.pos);
                        let distance = distVect.mag(); // 距離の大きさ
                        let minDist = this.radius + other.radius; // パーティクル同士の最小距離

                        // 距離が最小距離より小さい場合
                        if (distance < minDist) {
                            let force = distVect.copy().normalize(); // 力の方向ベクトルを正規化
                            let colorSimilarity = this.getColorSimilarity(other); // 色の類似度を計算

                            // 色が似ている場合、引力を強める
                            if (colorSimilarity > 0.75) {
                                force.mult(0.5); // 引力を強化
                                this.acc.add(force);
                            }
                            // 色があまり似ていない場合、反発力を弱める
                            else if (colorSimilarity < 0.5) {
                                force.mult(-0.02); // 反発力を弱める
                                this.acc.add(force);
                            }
                        }
                    }
                }
            }

            // 2つのパーティクルのカラーの類似度を計算する関数
            getColorSimilarity(other) {
                let color1 = this.color;
                let color2 = other.color;

                // ベクトルのコサイン類似度を計算
                let dotProduct = color1[0] * color2[0] + color1[1] * color2[1] + color1[2] * color2[2];
                let mag1 = sqrt(color1[0] ** 2 + color1[1] ** 2 + color1[2] ** 2);
                let mag2 = sqrt(color2[0] ** 2 + color2[1] ** 2 + color2[2] ** 2);

                return dotProduct / (mag1 * mag2); // 類似度(1に近いほど類似)
            }
        }

        // ウィンドウサイズが変更されたときにキャンバスをリサイズ
        function windowResized() {
            resizeCanvas(windowWidth, windowHeight);
        }
    </script>
</body>
</html>

説明:
全体のパーティクルシステム:

particles 配列でパーティクルのインスタンスを管理。
各パーティクルは move 関数で動かされ、interact 関数で他のパーティクルとの相互作用を計算。
パーティクルの動作:

パーティクルはランダムな初期位置と速度で生成され、画面内をランダムに移動。
画面の端に達すると、速度の符号を反転させて画面内に留まるようにしている(壁にぶつかると跳ね返る)。
パーティクル間の相互作用:

interact 関数では、色の類似度に基づいて引力や反発力を計算。
似た色のパーティクルは引き合い、色が異なる場合は反発する。
類似度の計算には「コサイン類似度」を使用し、ベクトルとして色の値を扱う。

3
4
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
3
4