66651
๐Ÿ‘€
66651
  • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (57)
    • note (28)
    • log (13)
    • error (9)
    • etc (7)

๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

  • ํ™ˆ
  • ํƒœ๊ทธ
  • ๋ฐฉ๋ช…๋ก

ํ‹ฐ์Šคํ† ๋ฆฌ

hELLO ยท Designed By ์ •์ƒ์šฐ.
66651

๐Ÿ‘€

[JS] ํž˜ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ 2 | ์นธ ์•„์นด๋ฐ๋ฏธ
note

[JS] ํž˜ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ 2 | ์นธ ์•„์นด๋ฐ๋ฏธ

https://ko.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-forces/a/modeling-gravity-and-friction

 

 

 

์ค‘๋ ฅ ๋ฐ ๋งˆ์ฐฐ๋ ฅ ๋ชจ๋ธ๋ง

์ง€๋‚œ ์˜ˆ์ œ์—์„œ ๋งŒ๋“  ์ค‘๋ ฅ์„ ๊ฐœ์„ ํ•˜๋ฉด์„œ ์—ฌ๊ธฐ์— ๋งˆ์ฐฐ๋ ฅ์„ ์ถ”๊ฐ€์‹œ์ผœ ๋ณด์ž.

 

์ง€๊ตฌ์˜ ์ค‘๋ ฅ

๋‰ดํ„ด์˜ ์ œ2๋ฒ•์น™์— ๋”ฐ๋ผ ์งˆ๋Ÿ‰์ด ์ž‘์œผ๋ฉด ๊ฐ€์†๋„๊ฐ€ ํฌ๋‹ค๊ณ  ์„ค์ •ํ–ˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค.

 

์ค‘๋ ฅ์€ ๋ฌผ์ฒด์˜ ์งˆ๋Ÿ‰์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค. ๋ฌผ์ฒด๊ฐ€ ๋ฌด๊ฑฐ์šธ์ˆ˜๋ก ํž˜์ด ๋” ํฌ๊ธฐ ๋•Œ๋ฌธ์— ํž˜์ด ์งˆ๋Ÿ‰์— ๋”ฐ๋ฅธ๋‹ค๋ฉด, ํž˜์„ ์งˆ๋Ÿ‰์œผ๋กœ ๋‚˜๋ˆ„์—ˆ์„ ๋•Œ ์งˆ๋Ÿ‰์€ ์‚ฌ๋ผ์ง„๋‹ค. ํ”„๋กœ๊ทธ๋žจ์— ๋งŒ๋“ค์—ˆ๋˜ ์งˆ๋Ÿ‰์— ๋”ฐ๋ฅธ ๊ฐ€์†๋„(์ค‘๋ ฅ)์— ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
    const canvas = document.getElementById('myCanvas');
    const context = canvas.getContext('2d');

    const PVector = function(x, y) {
        this.x = x;
        this.y = y;
    };

    PVector.prototype.set = function(x, y) {
        this.x = x;
        this.y = y;
    };

    PVector.prototype.add = function(v) {
        this.y = this.y + v.y;
        this.x = this.x + v.x;
    };

    PVector.prototype.mult = function(n) {
        this.x = this.x * n;
        this.y = this.y * n;
    };

    // ๋ฒกํ„ฐ๋ฅผ ๋ณต์‚ฌํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์†Œ๋“œ
    PVector.prototype.copy = function() {
        return new PVector(this.x, this.y);
    };

    PVector.div = function(v1, n) {
        const v3 = new PVector(v1.x / n, v1.y / n);
        return v3;
    };

    // ๋‹ค์–‘ํ•œ ์งˆ๋Ÿ‰๊ณผ ์œ„์น˜๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด ์ƒ์„ฑ์ž
    const Mover = function(m, x, y) {
        this.mass = m;
        this.position = new PVector(x, y);
        this.velocity = new PVector(0, 0);
        this.acceleration = new PVector(0, 0);
    };

    Mover.prototype.update = function() {
        this.velocity.add(this.acceleration);
        this.position.add(this.velocity);
        // ๊ฐ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๊ฐ€์†๋„ ์ œ๊ฑฐ
        this.acceleration.mult(0);
    };

    Mover.prototype.display = function() {
        context.beginPath();
        // ์งˆ๋Ÿ‰์˜ ์‹œ๊ฐํ™” (์งˆ๋Ÿ‰์— ๋”ฐ๋ผ ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐ)
        context.ellipse(this.position.x, this.position.y, this.mass * 20, this.mass * 20, 0, 0, Math.PI * 2, false);
        context.lineWidth = 5;
        context.strokeStyle = '#333333';
        context.stroke();
        context.fillStyle = '#ffffff';
        context.fill();
    };

    // ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ ๊ฐ์ฒด๋ฅผ ํŠ•๊ฒจ๋‚ด๋Š” ๋ฉ”์†Œ๋“œ
    Mover.prototype.checkEdges = function() {
        if (this.position.x > canvas.width) {
            this.position.x = canvas.width;
            this.velocity.x *= -1;
        } else if (this.position.x < 0) {
            this.velocity.x *= -1;
            this.position.x = 0;
        }
        if (this.position.y > canvas.height) {
            this.velocity.y *= -1;
            this.position.y = canvas.height;
        }
    };

    // ๋‰ดํ„ด์˜ ์ œ 2๋ฒ•์น™ (ํž˜์„ ๋ฐ›๊ณ , ์งˆ๋Ÿ‰์œผ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ฐ€์†๋„์— ๋”ํ•จ)
    Mover.prototype.applyForce = function(force) {
        const f = PVector.div(force, this.mass);
        this.acceleration.add(f);
    };

    const movers = [];

    for (let i = 0; i < 20; i++) {
        // (0, 0) ์œ„์น˜์—์„œ ์‹œ์ž‘ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์งˆ๋Ÿ‰์˜ ๊ฐ์ฒด๋ฅผ 20๊ฐœ ์ƒ์„ฑ
        movers[i] = new Mover(Math.random(0.1, 5), 0, 0);
    }

    const update = () => {
        context.clearRect(0, 0, canvas.width, canvas.height);

        for (let i = 0; i < movers.length; i++) {
            const wind = new PVector(0.01, 0);
            // ์ค‘๋ ฅ์€ ๋ฌผ์ฒด์˜ ์งˆ๋Ÿ‰์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.
            const gravity = new PVector(0, 0.1 * movers[i].mass);
            movers[i].applyForce(wind);
            movers[i].applyForce(gravity);

            movers[i].update();
            movers[i].display();
            movers[i].checkEdges();
        }

        requestAnimationFrame(update);
    };

    update();
</script>

 

 

๋งˆ์ฐฐ๋ ฅ

๋งˆ์ฐฐ๋ ฅ์€ ์‚ฐ์ผ์ ์ธ ํž˜์ด๋‹ค. ์‚ฐ์ผ์ ์ธ ํž˜์ด๋ž€ ์–ด๋–ค ๋ฌผ์ฒด๊ฐ€ ์šด๋™ํ•  ๋•Œ ์ด ์—๋„ˆ์ง€๊ฐ€ ์ ์  ์ค„์–ด๋“œ๋Š” ํž˜์„ ์ผ์ปซ๋Š” ๊ฒƒ์ด๋‹ค. ๋งˆ์ฐฐ๋ ฅ์—๋Š” ์ •์ง€๋งˆ์ฐฐ๋ ฅ (์ •์ง€ ์ค‘์ธ ๋ฌผ์ฒด์™€ ๋ฉด)๊ณผ ์šด๋™๋งˆ์ฐฐ๋ ฅ (์ด๋™ ์ค‘์ธ ๋ฌผ์ฒด์™€ ๋ฉด) ๋‘ ์ข…๋ฅ˜๊ฐ€ ์žˆ์œผ๋‚˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชฉ์ ์— ๋งž๋Š” ์šด๋™๋งˆ์ฐฐ๋ ฅ๋งŒ ๋ฐฐ์›Œ๋ณด์ž.

 

 

์œ„ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ๋งˆ์ฐฐ๋ ฅ์€ ์†๋„์™€ ๋ฐฉํ–ฅ์ด ๋ฐ˜๋Œ€๋‹ค. ๊ณต์‹ ์ค‘ -1 * v^๋ถ€๋ถ„ ์ฆ‰, -1 ๊ณฑํ•˜๊ธฐ ์†๋„์˜ ๋‹จ์œ„ ๋ฒกํ„ฐ๋Š” ๋งˆ์ฐฐ๋ ฅ์ด ์†๋„์™€ ๋ฐฉํ–ฅ์ด ๋ฐ˜๋Œ€๋ผ๋Š” ๊ฒƒ์„ ๋งํ•ด์ค€๋‹ค. ProcessingJS์—์„œ ์ด๊ฒƒ์€ ์†๋„ ๋ฒกํ„ฐ๋ฅผ ์ •๊ทœํ™” ์‹œํ‚จ ๋‹ค์Œ -1๋กœ ๊ณฑํ•ด์ฃผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

๊ณต์‹์—์„œ ๋งˆ์ฐฐ๋ ฅ์˜ ํฌ๊ธฐ๋Š” μ * N ์œผ๋กœ ์ •์˜๋œ๋‹ค. ์•ž์— ์žˆ๋Š” ๊ทธ๋ฆฌ์Šค ๋ฌธ์ž ๋ฎค๋Š” ๋งˆ์ฐฐ๊ณ„์ˆ˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋งˆ์ฐฐ๊ณ„์ˆ˜๋Š” ์–ด๋–ค ๋ฉด ๊ณ ์œ ์˜ ๋งˆ์ฐฐ๋ ฅ์œผ๋กœ ๊ทธ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ˆ˜์น˜๊ฐ€ ๋‹ค๋ฅธ ๊ฐ’์ด๋‹ค. ๊ฐ’์ด ํด์ˆ˜๋ก ๋งˆ์ฐฐ๋ ฅ์ด ๋” ๊ฐ•ํ•ด์ง€๊ณ  ๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ๋งˆ์ฐฐ๋ ฅ์€ ๋” ์•ฝํ•ด์ง„๋‹ค.

 

N์€ ์ˆ˜์งํ•ญ๋ ฅ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋ฌผ์ฒด๊ฐ€ ์„œ๋กœ์— ๋‹ฟ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ ‘์ด‰๋ ฅ์ด๋‹ค. (๋‰ดํ„ด์˜ ์ œ 3๋ฒ•์น™) ์ข€ ๋” ์ •ํ™•ํžˆ N์€ ์ ‘์ด‰๋ ฅ์˜ ์ˆ˜์ง ์š”์†Œ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋ฌผ์ฒด๊ฐ€ ์ˆ˜ํ‰์œผ๋กœ ์›€์ง์ผ ๋•Œ ์ˆ˜์งํ•ญ๋ ฅ์€ ์งˆ๋Ÿ‰์— ์ค‘๋ ฅ์„ ๊ณฑํ•œ ๊ฐ’์œผ๋กœ Fn = mg๋ผ๊ณ  ์“ด๋‹ค. ์ด ๋ง์€ ๊ฐ€๋ฒผ์šด ์Šคํฌ์ธ ์นด๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ์ž‘์€ ์ˆ˜์งํ•ญ๋ ฅ์„ ๊ฐ€ํ•˜๊ณ , ๋”ฐ๋ผ์„œ ํฐ ํŠธ๋ ˆ์ผ๋Ÿฌ๋ณด๋‹ค ์ž‘์€ ๋งˆ์ฐฐ๋ ฅ์„ ๊ฒฝํ—˜ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์œ„ ๊ทธ๋ฆผ์—์„œ ๋ฌผ์ฒด๋Š” ์ผ์ • ๊ฐ๋„๋กœ ํ‘œ๋ฉด์„ ๋”ฐ๋ผ ์›€์ง์ด๊ณ  ์žˆ๊ณ , ๊ทธ๋Ÿฌ๋ฉด ์ˆ˜์ง ํ•ญ๋ ฅ์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์ด ์•ฝ๊ฐ„ ๋” ๋ณต์žกํ•˜๋‹ค. ์ค‘๋ ฅ๊ณผ ๋ฐฉํ–ฅ์ด ๋™์ผํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ๋•Œ๋ฌธ์— ๊ฐ๋„์™€ ์‚ผ๊ฐ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•œ๋‹ค. 

 

// ๋งˆ์ฐฐ๊ณ„์ˆ˜ : 0.001์ด๋ผ๊ณ  ์„ค์ •
const c = 0.01;

// ์ˆ˜์งํ•ญ๋ ฅ : ํ•ญ์ƒ 1์ด๋ผ๊ณ  ์„ค์ •
const normal = 1;

// ๋งˆ์ฐฐ๋ ฅ์˜ ํฌ๊ธฐ
const frictionMag = c * normal;

// ์†๋„ ๋ฒกํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ด
const friction = movers[i].velocity.get();

// ๋งˆ์ฐฐ๋ ฅ์˜ ๋ฐฉํ–ฅ : ์†๋„์™€ ๋ฐ˜๋Œ€์ด๋ฏ€๋กœ ๊ณฑํ•˜๊ธฐ -1
friction.mult(-1);

// ๋งˆ์ฐฐ๋ ฅ์˜ ๋ฐฉํ–ฅ : ์†๋„ ๋ฒกํ„ฐ ์ •๊ทœํ™” (๋งˆ์ฐฐ๋ ฅ์˜ ํฌ๊ธฐ๋Š” ์ด๋™์†๋„์™€๋Š” ๊ด€๊ณ„๊ฐ€ ์—†์Œ)
friction.normalize();

// ๋งˆ์ฐฐ๋ ฅ์˜ ํฌ๊ธฐ์™€ ๋ฐฉํ–ฅ์„ ํ•ฉ์ณ์คŒ
friction.mult(frictionMag);

 

์ด์ „์— ๋งŒ๋“  ํž˜ ์˜ˆ์ œ์— ๋งˆ์ฐฐ๋ ฅ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๊ฐ€ ๋™์‹œ์— ๋ฐ”๋žŒ, ์ค‘๋ ฅ, ๋งˆ์ฐฐ๋ ฅ์— ์˜ํ–ฅ์„ ๋ฐ›๋„๋ก ๋งŒ๋“ค์–ด๋ณด์ž. ์ด ํ”„๋กœ๊ทธ๋žจ์„ ๊ณ„์† ๊ด€์ฐฐํ•˜๋‹ค ๋ณด๋ฉด ์›๋“ค์ด ์„œ์„œํžˆ ๋А๋ ค์ง€๋ฉด์„œ ์–ด๋А ์ˆœ๊ฐ„ ๋ชจ๋‘ ์ œ์ž๋ฆฌ์— ๋ฉˆ์ถฐ์žˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋งˆ์ฐฐ๋ ฅ์€ ์›๋“ค์ด ์›€์ง์ด๋Š” ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์œผ๋กœ ํž˜์„ ๊ฐ€ํ•ด์„œ ์†๋„๋ฅผ ์„œ์„œํžˆ ๋Šฆ์ถ”๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

 

 

<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
    const canvas = document.getElementById('myCanvas');
    const context = canvas.getContext('2d');

    const PVector = function(x, y) {
        this.x = x;
        this.y = y;
    };

    PVector.prototype.add = function(v) {
        this.y = this.y + v.y;
        this.x = this.x + v.x;
    };

    PVector.prototype.mult = function(n) {
        this.x = this.x * n;
        this.y = this.y * n;
    };

    PVector.prototype.div = function(n) {
        this.x = this.x / n;
        this.y = this.y / n;
    };

    PVector.prototype.mag = function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    };

    PVector.prototype.normalize = function() {
        const m = this.mag();
        if (m > 0) {
            this.div(m);
        }
    };

    // ๋ฒกํ„ฐ๋ฅผ ๋ณต์‚ฌํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์†Œ๋“œ
    PVector.prototype.copy = function() {
        return new PVector(this.x, this.y);
    };

    PVector.div = function(v1, n) {
        const v3 = new PVector(v1.x / n, v1.y / n);
        return v3;
    };

    // ๋‹ค์–‘ํ•œ ์งˆ๋Ÿ‰๊ณผ ์œ„์น˜๋ฅผ ๊ฐ–๋Š” ๊ฐ์ฒด ์ƒ์„ฑ์ž
    const Mover = function(m, x, y) {
        this.mass = m;
        this.position = new PVector(x, y);
        this.velocity = new PVector(0, 0);
        this.acceleration = new PVector(0, 0);
    };

    Mover.prototype.update = function() {
        this.velocity.add(this.acceleration);
        this.position.add(this.velocity);
        // ๊ฐ ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๊ฐ€์†๋„ ์ œ๊ฑฐ
        this.acceleration.mult(0);
    };

    Mover.prototype.display = function() {
        context.beginPath();
        // ์งˆ๋Ÿ‰์˜ ์‹œ๊ฐํ™” (์งˆ๋Ÿ‰์— ๋”ฐ๋ผ ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐ)
        context.ellipse(this.position.x, this.position.y, this.mass * 20, this.mass * 20, 0, 0, Math.PI * 2, false);
        context.lineWidth = 5;
        context.strokeStyle = '#333333';
        context.stroke();
        context.fillStyle = '#ffffff';
        context.fill();
    };

    // ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ ๊ฐ์ฒด๋ฅผ ํŠ•๊ฒจ๋‚ด๋Š” ๋ฉ”์†Œ๋“œ
    Mover.prototype.checkEdges = function() {
        if (this.position.x > canvas.width) {
            this.position.x = canvas.width;
            this.velocity.x *= -1;
        } else if (this.position.x < 0) {
            this.velocity.x *= -1;
            this.position.x = 0;
        }
        if (this.position.y > canvas.height) {
            this.velocity.y *= -1;
            this.position.y = canvas.height;
        }
    };

    // ๋‰ดํ„ด์˜ ์ œ 2๋ฒ•์น™ (ํž˜์„ ๋ฐ›๊ณ , ์งˆ๋Ÿ‰์œผ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ฐ€์†๋„์— ๋”ํ•จ)
    Mover.prototype.applyForce = function(force) {
        const f = PVector.div(force, this.mass);
        this.acceleration.add(f);
    };

    const movers = [];

    for (let i = 0; i < 20; i++) {
        // (0, 0) ์œ„์น˜์—์„œ ์‹œ์ž‘ํ•˜๋Š” ๋‹ค์–‘ํ•œ ์งˆ๋Ÿ‰์˜ ๊ฐ์ฒด๋ฅผ 20๊ฐœ ์ƒ์„ฑ
        movers[i] = new Mover(Math.random(0.1, 5), 0, 0);
    }

    const update = () => {
        context.clearRect(0, 0, canvas.width, canvas.height);

        for (let i = 0; i < movers.length; i++) {
            const wind = new PVector(0.01, 0);
            // ์ค‘๋ ฅ์€ ๋ฌผ์ฒด์˜ ์งˆ๋Ÿ‰์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.
            const gravity = new PVector(0, 0.1 * movers[i].mass);

            const c = 0.01;
            const normal = 1;
            const frictionMag = c * normal;
            const friction = movers[i].velocity.copy();
            friction.mult(-1);
            friction.normalize();
            friction.mult(frictionMag);

            // mover์— ๋งˆ์ฐฐ๋ ฅ ์ ์šฉ
            movers[i].applyForce(friction);
            movers[i].applyForce(wind);
            movers[i].applyForce(gravity);
            movers[i].update();
            movers[i].display();
            movers[i].checkEdges();
        }

        requestAnimationFrame(update);
    };

    update();
</script>

 

 

 

๊ณต๊ธฐ ๋ฐ ์œ ์ฒด ์ €ํ•ญ

๋ฌผ์ฒด๊ฐ€ ์•ก์ฒด๋‚˜ ๊ธฐ์ฒด๋ฅผ ํ†ต๊ณผํ•  ๋•Œ ์—ญ์‹œ ๋งˆ์ฐฐ์ด ๋ฐœ์ƒํ•œ๋‹ค.

๋‹ค์Œ ๊ณต์‹์„ ์‚ดํŽด๋ณด์ž.

 

 

 

 

 

  • Fd ๋Š” ํ•ญ๋ ฅ์„ ๋œปํ•œ๋‹ค. ๊ถ๊ทน์ ์œผ๋กœ ๊ณ„์‚ฐํ•ด์„œ applyForce() ํ•จ์ˆ˜์— ์ „๋‹ฌํ•  ๋ฒกํ„ฐ๋‹ค.
  • -1/2 ๋Š” ์ƒ์ˆ˜๋‹ค. ์ด ๋ถ€๋ถ„์€ ์Œ์ˆ˜๋ผ๋Š” ์‚ฌ์‹ค์ด ์ค‘์š”ํ•˜๋‹ค. ๋งˆ์ฐฐ๋ ฅ์ฒ˜๋Ÿผ ํž˜์ด ์†๋„์™€ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ์•Œ๋ ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ρ ๋Š” ๊ทธ๋ฆฌ์Šค ๋ฌธ์ž '๋กœ'๋ผ๊ณ  ํ•˜๊ณ  ์•ก์ฒด์˜ ๋ฐ€๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ๋Š” ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋ฌธ์ œ๋ฅผ ๊ฐ„๋‹จํžˆ ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฅผ ์ƒ์ˆ˜์˜ ๊ฐ’ 1์ด๋ผ๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.
  • v ๋Š” ๋ฌผ์ฒด๊ฐ€ ์›€์ง์ด๋Š” ์†๋ ฅ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋ฌผ์ฒด์˜ ์†๋ ฅ์€ ์†๋„ ๋ฒกํ„ฐ์˜ ํฌ๊ธฐ velocity.mag()์™€ ๊ฐ™๋‹ค.
  • A ๋Š” ์•ก์ฒด๋‚˜ ๊ธฐ์ฒด๋ฅผ ํ†ต๊ณผํ•˜๋Š” ๋ฌผ์ฒด์˜ ์•ž๋ฉด ๋ฉด์ ์„ ์˜๋ฏธํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ณต๊ธฐ ์—ญํ•™์ ์œผ๋กœ ์„ค๊ณ„๋œ ๋žŒ๋ณด๋ฅด๊ธฐ๋‹ˆ๋Š” ๋ฐ•์Šค ๋ชจ์–‘์˜ ๋ณผ๋ณด๋ณด๋‹ค ์ ์€ ๊ณต๊ธฐ ์ €ํ•ญ์„ ๊ฒฝํ—˜ํ•œ๋‹ค. ๊ฐ„๋‹จํ•œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์—์„œ๋Š” ๋ฌผ์ฒด๊ฐ€ ๊ณต ๋ชจ์–‘์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์ด๊ฒƒ์„ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Cd ๋Š” ํ•ญ๋ ฅ๊ณ„์ˆ˜์ด๊ณ , ๋งˆ์ฐฐ๋ ฅ ๊ณ„์ˆ˜ μ์™€ ๊ฐ™๋‹ค. ์ด ์ƒ์ˆ˜๋กœ ํ•ญ๋ ฅ์ด ์„ผ์ง€ ์•ฝํ•œ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•ญ๋ ฅ๊ณ„์ˆ˜๋Š” ๋ฌผ์ฒด๊ฐ€ ํ†ต๊ณผํ•˜๊ธฐ๊ฐ€ ์‰ฝ๊ฑฐ๋‚˜ ํ†ต๊ณผํ•˜๊ธฐ ์–ด๋ ค์šด ์ •๋„๋ฅผ ๋งํ•œ๋‹ค.
  • v^ ๋Š” ์†๋„์˜ ๋‹จ์œ„ ๋ฒกํ„ฐ velocity.normalize()๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋งˆ์ฐฐ๋ ฅ์ฒ˜๋Ÿผ ํ•ญ๋ ฅ๋„ ์†๋„์˜ ๋ฐ˜๋Œ€ ๋ฐฉํ–ฅ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

 

๊ฐ ์š”์†Œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๊ฐ„๋‹จํ•œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์—์„œ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ๊ฒฐ์ •ํ–ˆ์œผ๋‹ˆ ์‹์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ„๋‹จํžˆ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

// ์‹์˜ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„ (ํฌ๊ธฐ): v^2 * Cd
const c = 0.1;
const speed = velocity.mag();
const dragMagnitude = c * speed * speed;

// ์‹์˜ ๋‘ ๋ฒˆ์งธ ๋ถ€๋ถ„ (๋ฐฉํ–ฅ): v unit vector * -1 
const drag = velocity.get();
drag.normalize();
drag.mult(-1);

// ํฌ๊ธฐ์™€ ๋ฐฉํ–ฅ์„ ํ•ฉ์นœ ์‹
drag.mult(dragMagnitude);

 

์ด์ œ Mover ๊ฐ์ฒด๊ฐ€ ํ—ค์—„์น  ์•ก์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜์ž. ํ•ญ๋ ฅ ๊ณ„์ˆ˜๋Š” (0.1) ์ •๋„๋กœ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•˜์—ฌ ์ฒœ์ฒœํžˆ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

 

// ์œ„์น˜, ๋„ˆ๋น„, ๋†’์ด, ํ•ญ๋ ฅ ๊ณ„์ˆ˜์— ๋Œ€ํ•œ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ–๋Š” ์ง์‚ฌ๊ฐํ˜• ์•ก์ฒด
const Liquid = function(x, y, w, h, c) {
  this.x = x;
  this.y = y;
  this.w = w;
  this.h = h;
  this.c = c;
};

// ์•ก์ฒด๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๋Š” ๋ฉ”์†Œ๋“œ
Liquid.prototype.display = function() {
  noStroke();
  fill(50);
  rect(this.x, this.y, this.w, this.h);
};

// Liquid ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ ์–ธํ•˜๊ณ  ์ดˆ๊ธฐํ™”
const liquid = new Liquid(0, height/2, width, height/2, 0.1);

 

Mover๊ฐ€ ์•ก์ฒด๋ฅผ ํ†ต๊ณผํ•  ๋•Œ ํ•ญ๋ ฅ์„ ๋ฐ›๋Š” ๋™์ž‘์„ ์ฝ”๋“œ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// ์œ„์น˜ ๋ฒกํ„ฐ๊ฐ€ Liquid ์ง์‚ฌ๊ฐํ˜• ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฉ”์†Œ๋“œ
Liquid.prototype.contains = function(m) {
    const p = m.position;
    return p.x > this.x && p.x < this.x + this.w && p.y > this.y && p.y < this.y + this.h;
};

// ๋งˆ์ฐฐํ•ญ๋ ฅ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์†Œ๋“œ (ํ•ญ๋ ฅ = ์†๋„์˜ ์ œ๊ณฑ * ํ•ญ๋ ฅ๊ณ„์ˆ˜ * ์†๋„ * -1)
Liquid.prototype.calculateDrag = function(m) {
    // ํฌ๊ธฐ๋Š” ๊ณ„์ˆ˜ * ์†๋„์˜ ์ œ๊ณฑ
    const speed = m.velocity.mag();
    const dragMagnitude = this.c * speed * speed;

    // ๋ฐฉํ–ฅ์€ ์†๋„์™€ ๋ฐ˜๋Œ€
    const dragForce = m.velocity.copy();
    dragForce.mult(-1);

    // ์•Œ๋งž์€ ํฌ๊ธฐ๋กœ ์กฐ์ • 
    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
};

//Mover๊ฐ€ ์•ก์ฒด์— ์žˆ๋Š”๊ฐ€?
if (liquid.contains(movers[i])) {
    // ํ•ญ๋ ฅ์„ ๊ณ„์‚ฐ
    const dragForce = liquid.calculateDrag(movers[i]);
    // Mover์— ํ•ญ๋ ฅ์„ ์ ์šฉ
    movers[i].applyForce(dragForce);
}

 

Liquid ๊ฐ์ฒดํ˜•์— ์œ„์˜ ๋‘ ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค์–ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๋ฉด ๋ฌผ์†์œผ๋กœ ๊ณต์ด ๋–จ์–ด์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ™”๋ฉด ์•„๋ž˜ ํŒŒ๋ž€์ƒ‰ ์˜์—ญ(์•ก์ฒด)์„ ํ†ต๊ณผํ•ด์„œ ์ง€๋‚˜๊ฐ€๋ฉด ๋ฌผ์ฒด๊ฐ€ ๋” ๋А๋ ค์ง„๋‹ค. ๋˜ํ•œ, ์ž‘์€ ๋ฌผ์ฒด๋Š” ํฐ ๋ฌผ์ฒด๋ณด๋‹ค ๋” ๊ธ‰๊ฒฉํ•˜๊ฒŒ ๋А๋ ค์ง€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ๋‰ดํ„ด์˜ ์ œ 2๋ฒ•์น™ A = F / M์™€ ๊ฐ™์ด ๊ฐ€์†๋„๋Š” ํž˜์„ ์งˆ๋Ÿ‰์œผ๋กœ ๋‚˜๋ˆˆ ๊ฐ’๊ณผ ๊ฐ™๋‹ค. ๋ฌผ์ฒด์˜ ์งˆ๋Ÿ‰์ด ํด์ˆ˜๋ก ๊ฐ€์†๋„๊ฐ€ ์ž‘๋‹ค. ์ž‘์€ ๋ฌผ์ฒด๋Š” ๋นจ๋ฆฌ ๊ฐ€์†ํ•œ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์–ธ๊ธ‰ํ•˜๋Š” ๊ฐ€์†๋„๋Š” ํ•ญ๋ ฅ์œผ๋กœ ์ธํ•ด ์ ์  ๋А๋ ค์ง€๋Š” ๊ฐ€์†๋„๋‹ค. ์ž‘์€ ๋ฌผ์ฒด๋Š” ํฐ ๋ฌผ์ฒด๋ณด๋‹ค ํ›จ์”ฌ ๋” ๋†’์€ ๋น„์œจ๋กœ ๋А๋ ค์งˆ ๊ฒƒ์ด๋‹ค.

 

 

<canvas id="myCanvas" width="300" height="250" style="border: 1px solid #000;"></canvas>
<script>
    const canvas = document.getElementById('myCanvas');
    const context = canvas.getContext('2d');

    const PVector = function(x, y) {
        this.x = x;
        this.y = y;
    };

    PVector.prototype.add = function(v) {
        this.y = this.y + v.y;
        this.x = this.x + v.x;
    };

    PVector.prototype.mult = function(n) {
        this.x = this.x * n;
        this.y = this.y * n;
    };

    PVector.prototype.div = function(n) {
        this.x = this.x / n;
        this.y = this.y / n;
    };

    PVector.prototype.mag = function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    };

    PVector.prototype.normalize = function() {
        const m = this.mag();
        if (m > 0) {
            this.div(m);
        }
    };

    PVector.prototype.copy = function() {
        return new PVector(this.x, this.y);
    };

    PVector.div = function(v1, n) {
        const v3 = new PVector(v1.x / n, v1.y / n);
        return v3;
    };

    const Mover = function(m, x, y) {
        this.mass = m;
        this.position = new PVector(x, y);
        this.velocity = new PVector(0, 0);
        this.acceleration = new PVector(0, 0);
    };

    Mover.prototype.update = function() {
        this.velocity.add(this.acceleration);
        this.position.add(this.velocity);
        this.acceleration.mult(0);
    };

    Mover.prototype.display = function() {
        context.beginPath();
        context.ellipse(this.position.x, this.position.y, this.mass * 20, this.mass * 20, 0, 0, Math.PI * 2, false);
        context.lineWidth = 5;
        context.strokeStyle = '#333333';
        context.stroke();
        context.fillStyle = '#ffffff';
        context.fill();
        context.closePath();
    };

    Mover.prototype.checkEdges = function() {
        if (this.position.x > canvas.width) {
            this.position.x = canvas.width;
            this.velocity.x *= -1;
        } else if (this.position.x < 0) {
            this.velocity.x *= -1;
            this.position.x = 0;
        }
        if (this.position.y > canvas.height) {
            this.velocity.y *= -1;
            this.position.y = canvas.height;
        }
    };

    Mover.prototype.applyForce = function(force) {
        const f = PVector.div(force, this.mass);
        this.acceleration.add(f);
    };

    // Liquid ์ƒ์„ฑ์ž ํ•จ์ˆ˜
    const Liquid = function(x, y, w, h, c) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.c = c;
    };

    // Liquid ๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๋Š” ๋ฉ”์†Œ๋“œ
    Liquid.prototype.display = function() {
        context.beginPath();
        context.rect(this.x, this.y, this.w, this.h);
        context.fillStyle = 'skyblue';
        context.fill();
        context.closePath();
    };

    // ์œ„์น˜ ๋ฒกํ„ฐ๊ฐ€ Liquid ์ง์‚ฌ๊ฐํ˜• ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฉ”์†Œ๋“œ
    Liquid.prototype.contains = function(m) {
        const p = m.position;
        return p.x > this.x && p.x < this.x + this.w && p.y > this.y && p.y < this.y + this.h;
    };

    // ๋งˆ์ฐฐํ•ญ๋ ฅ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์†Œ๋“œ (ํ•ญ๋ ฅ = ์†๋„์˜ ์ œ๊ณฑ * ํ•ญ๋ ฅ๊ณ„์ˆ˜ * ์†๋„ * -1)
    Liquid.prototype.calculateDrag = function(m) {
        const speed = m.velocity.mag();
        const dragMagnitude = this.c * speed * speed;
        const dragForce = m.velocity.copy();
        dragForce.mult(-1);
        dragForce.normalize();
        dragForce.mult(dragMagnitude);
        return dragForce;
    };

    const movers = [];

    // liquid ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    const liquid = new Liquid(0, canvas.height / 2, canvas.width, canvas.height / 2, 0.1);

    const update = () => {
        context.clearRect(0, 0, canvas.width, canvas.height);

        // ์•ก์ฒด๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅ
        liquid.display();

        for (let i = 0; i < movers.length; i++) {

            if (liquid.contains(movers[i])) {
                const dragForce = liquid.calculateDrag(movers[i]);
                // mover์— ๋งˆ์ฐฐ ํ•ญ๋ ฅ์„ ์ ์šฉ
                movers[i].applyForce(dragForce);
            }

            const gravity = new PVector(0, 0.1 * movers[i].mass);
            movers[i].applyForce(gravity);
            movers[i].update();
            movers[i].display();
            movers[i].checkEdges();
        }

        context.fillStyle = '#333333';
        context.fillText('click mouse to reset', 10, 30);

        requestAnimationFrame(update);
    };

    // Mover ์ธ์Šคํ„ด์Šค ๋ฌด์ž‘์œ„ ์ƒ์„ฑ
    const resetMovers = function() {
        for (let i = 0; i < 9; i++) {
            movers[i] = new Mover(Math.random(0.5, 3), 20 + i * canvas.width / 9, 0);
        }
    }

    // ์บ”๋ฒ„์Šค ํด๋ฆญ ์‹œ ์žฌ์‹œ์ž‘
    canvas.addEventListener('click', function() {
        resetMovers();
    });

    resetMovers();
    update();
</script>

 

 

 

 

์ฐธ์กฐ ์‚ฌ์ดํŠธ

 

์ƒ์„ฑ์ž ํ•จ์ˆ˜์— ์˜ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ

https://velog.io/@ursr0706/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1

์ €์ž‘์žํ‘œ์‹œ (์ƒˆ์ฐฝ์—ด๋ฆผ)
    'note' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
    • [Three.js] GIS Developer ๋‹˜์˜ Three.js ๊ฐ•์ขŒ - 1
    • [JS] ํž˜ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ 3 | ์นธ ์•„์นด๋ฐ๋ฏธ
    • [JS] ํž˜ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ 1 | ์นธ ์•„์นด๋ฐ๋ฏธ
    • [JS] ๋ฒกํ„ฐ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ 3 | ์นธ ์•„์นด๋ฐ๋ฏธ
    66651
    66651
    always awake

    ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”