์ค๋ ฅ ๋ฐ ๋ง์ฐฐ๋ ฅ ๋ชจ๋ธ๋ง
์ง๋ ์์ ์์ ๋ง๋ ์ค๋ ฅ์ ๊ฐ์ ํ๋ฉด์ ์ฌ๊ธฐ์ ๋ง์ฐฐ๋ ฅ์ ์ถ๊ฐ์์ผ ๋ณด์.
์ง๊ตฌ์ ์ค๋ ฅ
๋ดํด์ ์ 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์ด๋ผ๊ณ ๊ฐ์ ํ๋ค.
- ๋ ๋ฌผ์ฒด๊ฐ ์์ง์ด๋ ์๋ ฅ์ ๋ํ๋ธ๋ค. ๋ฌผ์ฒด์ ์๋ ฅ์ ์๋ ๋ฒกํฐ์ ํฌ๊ธฐ velocity.mag()์ ๊ฐ๋ค.
- ๋ ์ก์ฒด๋ ๊ธฐ์ฒด๋ฅผ ํต๊ณผํ๋ ๋ฌผ์ฒด์ ์๋ฉด ๋ฉด์ ์ ์๋ฏธํ๋ค. ์๋ฅผ ๋ค์ด ๊ณต๊ธฐ ์ญํ์ ์ผ๋ก ์ค๊ณ๋ ๋๋ณด๋ฅด๊ธฐ๋๋ ๋ฐ์ค ๋ชจ์์ ๋ณผ๋ณด๋ณด๋ค ์ ์ ๊ณต๊ธฐ ์ ํญ์ ๊ฒฝํํ๋ค. ๊ฐ๋จํ ์๋ฎฌ๋ ์ด์ ์์๋ ๋ฌผ์ฒด๊ฐ ๊ณต ๋ชจ์์ด๋ผ๊ณ ๊ฐ์ ํ๊ณ ์ด๊ฒ์ ๋ฌด์ํ ์ ์๋ค.
- 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>
์ฐธ์กฐ ์ฌ์ดํธ
์์ฑ์ ํจ์์ ์ํ ๊ฐ์ฒด ์์ฑ