์ค๋ ฅ ์ธ๋ ฅ
์ง๋์ ๊ฐ์ง ๋ชจ๋ ๋ฌผ์ฒด๋ ๋ชจ๋ ๋ค๋ฅธ ๋ฌผ์ฒด์ ์ค๋ ฅ์ ๊ฐํ๋ค. ์๋์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ค๋ ฅ์ ์ธ๊ธฐ๋ฅผ ๊ณ์ฐํ๋ ์์ด ์๋ค.
- F ๋ ์ค๋ ฅ์ ๋ํ๋ด๋ ๊ฐ์ผ๋ก, ์ต์ข ์ ์ผ๋ก ๊ณ์ฐํ์ฌ applyForce() ํจ์์ ์ ๋ฌํ๋ ค๋ ๋ฒกํฐ๋ค.
- G ๋ ๋ง์ ์ธ๋ ฅ์ ์์๋ก 6.67428 x 10^-11 ์ด(s) ์ ๊ณฑ ๋ถ์ ํฌ๋ก๊ทธ๋จ(kg) ๋ถ์ ๋ฏธํฐ(m) ์ธ์ ๊ณฑ์ด๋ค. ์ด๊ฒ์ ํ์ ๋ณด๋ค ์ฝํ๊ฒ ๋ง๋ค๊ฑฐ๋ ๋ณด๋ค ๊ฐํ๊ฒ ๋ง๋๋ ์์๋ค. ์ด ๊ฐ์ 1๋ก ๋๊ณ ๋ฌด์ํด๋ ํฐ์ผ๋์ง ์๋๋ค.
- mโโ ๊ณผ mโ ๋ ๊ฐ์ฒด 1 ๊ณผ 2 ์ ์ง๋์ด๋ค. ๋ดํด์ ์ 2 ๋ฒ์น(F =MA)์์ ๋ฐฐ์ด๊ฒ๊ณผ ๊ฐ์ด ์ง๋์ ๊ณ ๋ คํ์ง ์์๋ ๋๋ค. ์ด์ฐจํผ ํ๋ฉด์ ๋ฌผ์ฒด์ ๋ฌผ๋ฆฌ์ ์ง๋์ ์ค์ ๋ก ์กด์ฌํ์ง ์๋๋ค. ๊ทธ๋ฌ๋ ์ด ๊ฐ์ ์ ์งํ๋ ๊ฒฝ์ฐ ๋ณด๋ค ํฐ ๋ฌผ์ฒด๊ฐ ์์ ๋ฌผ์ฒด๋ณด๋ค ํจ์ฌ ๋ ๊ฐํ ์ค๋ ฅ์ ๋ฐํํ๋ ์ฌ๋ฏธ์๋ ์๋ฎฌ๋ ์ด์ ์ ๋ง๋ค ์ ์๋ค.
- r^์ ๊ฐ์ฒด 1 ์์ ๊ฐ์ฒด 2 ๋ฅผ ๊ฐ๋ฆฌํค๋ ๋จ์ ๋ฒกํฐ๋ค. ํ ๊ฐ์ฒด์์ ๋ค๋ฅธ ๊ฐ์ฒด์ ์์น๋ฅผ ๋นผ์ ์ด ๋ฐฉํฅ ๋ฒกํฐ๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
- r ² ๋ ๋ ๋ฌผ์ฒด ์ฌ์ด์ ๊ฑฐ๋ฆฌ๋ฅผ ์ ๊ณฑํ ๊ฒ์ด๋ค. ํ์ ๊ฐ๋๋ ๊ฑฐ๋ฆฌ ์ ๊ณฑ์ ๋ฐ๋น๋กํ๋ค. ๋ฌผ์ฒด๊ฐ ๋ฉ๋ฆฌ ์์์๋ก ํ์ ๋ ์ฝํด์ง๊ณ ๊ฐ๊น์ธ์๋ก ๋ ๊ฐํด์ง๋ค.
๋ค์์ ๊ฐ์ ํด ๋ณด์.
- ๊ฐ ๊ฐ์ฒด์ PVector ์์น๋ location1๊ณผ location2์ด๋ค.
- ๊ฐ ๊ฐ์ฒด์ ์ง๋์ mass1๊ณผ mass2์ด๋ค.
- ๋ง์ ์ธ๋ ฅ ์์๋ ์ซ์ ๋ณ์ G๋ค.
์์ ๊ฐ์ ๊ฐ์ ํ์์ ์ค๋ ฅ์ ํ PVector force๋ฅผ ๊ณ์ฐํ๊ณ ์ ํ๋ค. ๋ ๋ถ๋ถ์ผ๋ก ๋๋์ด์ ๊ณ์ฐํด ๋ณด์. ๋จผ์ ์์ ์์์ ํ์ ๋ฐฉํฅ r ² ์ ๊ณ์ฐํ๋ค. ๋ ๋ฒ์งธ๋ก ์ง๋๊ณผ ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ํ์ ์ธ๊ธฐ๋ฅผ ๊ณ์ฐํ๋ค.
๋ฒกํฐ(ํ์ ๋ฐฉํฅ)๋ ๋ ์ ์ฌ์ด์ ๊ฑฐ๋ฆฌ๋ค. ํ ์ ์์ ๋ค๋ฅธ ํ ์ ์ผ๋ก ๊ฐ๋ฆฌํค๋ ๋ฒกํฐ๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋จ์ํ ๋ค๋ฅธ ์ ์์ ํ ์ ์ ๋นผ๋ฉด ๋๋ค. ๋ฐฉํฅ๋ง์ ์๋ ค ์ฃผ๋ ๋จ์ ๋ฒกํฐ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ์์น๋ฅผ ๋บ ํ์ ๋ฒกํฐ๋ฅผ ์ ๊ทํํด์ผ ํ๋ค๋ ์ ์ ์์ง ๋ง์.
// ์ธ๋ ฅ์ ๋ฐฉํฅ
const dir = PVector.sub(location1, location2);
dir.normalize();
ํ์ ๋ฐฉํฅ์ ์์๋ค. ์ด์ ๋ฒกํฐ์ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐํ๊ณ ๊ทธ์ ๋ง์ถ์ด ์กฐ์ ํ๋ฉด ๋๋ค.
// ์ธ๋ ฅ์ ์ธ๊ธฐ
const m = (G * mass1 * mass2) / (distance * distance);
dir.mult(m);
์์ ์ฝ๋๊ฐ ๋์ํ๊ธฐ ์ํด ๊ฑฐ๋ฆฌ๊ฐ ํ์ํ๋ค. ๊ทธ๋ฐ๋ฐ ์ฐ๋ฆฌ๋ ๋ฐฉ๊ธ ํ ์์น์์ ๋ ๋ค๋ฅธ ์์น ๋๊น์ง ๊ฐ๋ฆฌํค๋ ๋ฒกํฐ๋ฅผ ๋ง๋ค์๋ค. ์ฌ๊ธฐ์ ๋ฒกํฐ์ ๊ธธ์ด๊ฐ ๋ ๊ฐ์ฒด ์ฌ์ด์ ๊ฑฐ๋ฆฌ๊ฐ ๋๋ค. ์ฝ๋ ํ ์ค์ ์ถ๊ฐํด์ ๋ฒกํฐ๋ฅผ ์ ๊ทํํ๊ธฐ ์ ์ ๋ฒกํฐ์ ํฌ๊ธฐ๋ฅผ ์ ์ ์๋ค๋ฉด ๊ฑฐ๋ฆฌ๋ ์ ์ ์๋ค.
// ํ ๊ฐ์ฒด์์ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค๋ ๋ฒกํฐ
const force = PVector.sub(location1, location2);
// ํด๋น ๋ฒกํฐ์ ๊ธธ์ด(ํฌ๊ธฐ)๋ ๋ ๊ฐ์ฒด๋ค ์ฌ์ด์ ๊ฑฐ๋ฆฌ
const distance = force.magnitude();
// ์ค๋ ฅ์ ๋ํ ๊ณต์์ ์ด์ฉํ์ฌ ํ์ ํฌ๊ธฐ๋ฅผ ๊ณ์ฐ
const strength = (G * mass1 * mass2) / (distance * distance);
// ํ ๋ฒกํฐ๋ฅผ ์ ๊ทํํ๊ณ ์ ์ ํ ํฌ๊ธฐ๋ก ์กฐ์
force.normalize();
force.mult(strength);
์ด์ ๊ณ ์ ๋ ์์น๋ฅผ ๊ฐ๋ ์๋ก์ด Attractor ๊ฐ์ฒด๋ฅผ ํ๋ก๊ทธ๋จ์ ์ถ๊ฐํด๋ณด์. Mover object๊ฐ Attractor object์ชฝ์ผ๋ก ๋๋ฆฌ๋ ์ค๋ ฅ์ ๊ฒฝํํ ์ ์๋๋ก ๋ง๋ค์ด๋ณด์.
// ์ดํธ๋ํฐ ์์ฑ์ ํจ์
const Attractor = function() {
// ์์น
this.position = new PVector(canvas.width / 2, canvas.height / 2);
// ์ง๋
this.mass = 20;
// ๋ง์ ์ธ๋ ฅ
this.G = 1;
};
// ์ดํธ๋ํฐ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฉ์๋
Attractor.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, this.mass * 2, this.mass * 2, 0, 0, Math.PI * 2, false);
context.fillStyle = '#b1c1d1';
context.fill();
context.closePath();
};
์ ์ํ ํ์ Attractor ๊ฐ์ฒด ํํ์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค ์ ์๋ค.
const mover = new Mover();
const attractor = new Attractor();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
attractor.display();
mover.update();
mover.display();
requestAnimationFrame(update);
};
์ด ํ๋ก๊ทธ๋จ์ ํผ์ฆ ๋ง์ง๋ง ์กฐ๊ฐ์ ํ ๋ฌผ์ฒด๊ฐ ๋ค๋ฅธ ๋ฌผ์ฒด๋ฅผ ๋์ด๋น๊ธฐ๋ ๋์์ ๊ตฌํํ๋ ๊ฒ์ด๋ค. ๋ ๋ฌผ์ฒด๋ฅผ ์ด๋ป๊ฒ ํด์ผ ์๋ก ๊ต๊ฐํ ์ ์์๊น?
์ด๋ ๊ฒ ๋์ํ ์ ์๋๋ก ํ๋ก๊ทธ๋จ์ ์ค๊ณํ๋ ๋ฐฉ๋ฒ์ ๋ง์ด ์๋ค.
๋ฐฉ๋ฒ | ํจ์ | |
1 | ||
2 | ||
3 | ||
4 | Mover๋ฅผ ์ ๋ฌ๋ฐ์ ํ ์ธ๋ ฅ์ธ PVector๋ฅผ ๋ฐํํ๋ Attractor ๊ฐ์ฒด ๋ด์ ๋ฉ์๋. (์ดํ ์ธ๋ ฅ์ Mover์ applyForce() ๋ฉ์๋๋ก ์ ๋ฌ๋จ) |
`var f = a.calculateAttraction(m); |
ํ์ฌ๊น์ง์ ๊ณผ์ ์ผ๋ก๋ ๋ฌผ์ฒด๊ฐ ์๋ก ์ํธ ์์ฉํ๊ฒ ๋ง๋๋ ์ฝ๋ ์ค ๋ค ๋ฒ์งธ ์ ํ์ด ๊ฐ์ฅ ์ ์ ํ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
์ด์ update() ํจ์๋ฅผ ์๋์ ๊ฐ์ด ์ ๋ฆฌํ ์ ์๋ค.
const mover = new Mover();
const attractor = new Attractor();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
// ๋ object๋ค ์ฌ์ด์ ์ธ๋ ฅ์ ๊ณ์ฐํด์ ์ ์ฉ
const f = attractor.calculateAttraction(mover);
mover.applyForce(f);
attractor.display();
mover.update();
mover.display();
requestAnimationFrame(update);
};
์ธ๋ ฅ์ ๊ณ์ฐํ๋ calculateAttraction() ๋ฉ์๋๋ฅผ ์์ฑํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
* ์ค๋ ฅ์ ๊ณ์ฐํ๋ ์์ ์ ์ฉํ๋ฉด์ ๋์์ ํด๊ฒฐํด์ผ ํ ์์ ๋ฌธ์ ๊ฐ ์๋ค. ProcessingJS ์ธ๊ณ์์ mover๋ ๊ฒฐ๊ตญ attractor์ ๋งค์ฐ ๊ทผ์ ํ๊ฒ ๋๋ฉด ํ์ด ๋๋ฌด ๊ฐํด์ mover๊ฐ ํ๋ฉด ๋ฐ์ผ๋ก ํ๊ฒจ ๋๊ฐ ์๋ ์๋ค. Mover ๊ฐ ์ค์ ๋ก ์๋ ์์น์ ์๊ด์์ด attractor ๋ก๋ถํฐ 5 ํฝ์ ๋ณด๋ค ์๊ฑฐ๋ 25 ํฝ์ ์ด์ ๊ฑฐ๋ฆฌ๋ฅผ ๋์ง ์๋๋ก ๋ง๋ค์ด ์ฃผ์ด์ผ ํ๋ค.
์ต์ ๊ฑฐ๋ฆฌ๋ฅผ ์ ํํ๋ ์ด์ ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ต๋ ๊ฑฐ๋ฆฌ๋ฅผ ์ ํํ๋ ๊ฒ๋ ์ข์ ๊ฒ์ด๋ค. ์ฆ, mover๊ฐ attractor๋ก๋ถํฐ 500 ํฝ์ ์ ์๋ค๋ฉด(์ง๋์น๊ฒ ๋ฉ์ง๋ ์์ ๊ฑฐ๋ฆฌ) ํ์ 250,000์ผ๋ก ๋๋๋ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ ํ๋ฉด ํ์ ์ ํ ์ ์ฉํ์ง ์์ ์ ๋๋ก ๋๋ฌด ์ฝํด์ง ์ ์๋ค.
// ์ดํธ๋ํฐ์ mover ์ฌ์ด์ ์ธ๋ ฅ์ ๊ณ์ฐํ๋ ๋ฉ์๋
Attractor.prototype.calculateAttraction = function(mover) {
// ํ์ ๋ฐฉํฅ ๊ณ์ฐ
const force = PVector.sub(this.position, mover.position);
// ๊ฐ์ฒด ์ฌ์ด์ ๊ฑฐ๋ฆฌ
let distance = force.mag();
// ๊ฑฐ๋ฆฌ ๋ฒ์ ์ ํ (๋๋ฌด ๊ฐ๊น๊ฑฐ๋ ์์ฃผ ๋จผ ๊ฐ์ฒด์ ๊ฒฝ์ฐ)
distance = constrain(distance, 5, 25);
// ์ ๊ทํ
force.normalize();
// ์ค๋ ฅ์ ํฌ๊ธฐ ๊ณ์ฐ
const strength = (this.G * this.mass * mover.mass) / (distance * distance);
// ํ ๋ฒกํฐ = ํฌ๊ธฐ * ๋ฐฉํฅ
force.mult(strength);
// ํ์ ๋ฐํ
return force;
};
์ด์ ๋ชจ๋ ๊ฒ์ ํ๋ก๊ทธ๋จ์ ํตํฉํด๋ณด์. Mover ์ ๊ฐ์ฒดํ์ ์ ํ ๋ฐ๋์ง ์์์ง๋ง, ์ด์ ํ๋ก๊ทธ๋จ์ Attractor ๊ฐ์ฒด์ ๊ทธ ๋์ ํจ๊ป ๋ฌถ๋ ์ฝ๋๊ฐ ๋ค์ด๊ฐ๋ค. ๋ attractor๋ฅผ ๋ง์ฐ์ค๋ก ์ ์ดํ๊ธฐ ์ํด ์ฝ๋๋ฅผ ํ๋ก๊ทธ๋จ์ ์ถ๊ฐํด์ ํจ๊ณผ๋ฅผ ๊ด์ฐฐํ๊ธฐ ๋ ์ฝ๊ฒ ๋์๋ค.
<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 positionX = canvas.offsetLeft;
const positionY = canvas.offsetTop;
let mouseX = 0;
let mouseY = 0;
const mouseMoved = function(e) {
mouseX = e.clientX - positionX;
mouseY = e.clientY - positionY;
attractor.handleHover(mouseX, mouseY);
};
const mousePressed = function(e) {
e.preventDefault();
attractor.handlePress(mouseX, mouseY);
};
const mouseDragged = function() {
attractor.handleHover(mouseX, mouseY);
attractor.handleDrag(mouseX, mouseY);
};
const mouseReleased = function() {
attractor.stopDragging();
canvas.removeEventListener('mousemove', mouseDragged);
canvas.removeEventListener('mousemove', mouseReleased);
};
canvas.addEventListener('mousemove', mouseMoved);
canvas.addEventListener('mousedown', function(e) {
mousePressed(e);
canvas.addEventListener('mousemove', mouseDragged);
canvas.addEventListener('mouseup', mouseReleased);
});
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.sub = function(v) {
this.x = this.x - v.x;
this.y = this.y - v.y;
};
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.sub = function(v1, v2) {
const v3 = new PVector(v1.x - v2.x, v1.y - v2.y);
return v3;
};
PVector.div = function(v1, n) {
const v3 = new PVector(v1.x / n, v1.y / n);
return v3;
};
PVector.constrain = function(n, low, high) {
return Math.max(Math.min(n, high), low);
};
PVector.dist = function(...args) {
if (args.length === 4) {
//2D
return Math.hypot(args[2] - args[0], args[3] - args[1]);
} else if (args.length === 6) {
//3D
return Math.hypot(args[3] - args[0], args[4] - args[1], args[5] - args[2]);
}
};
const Mover = function() {
this.position = new PVector(400, 50);
this.velocity = new PVector(1, 0);
this.acceleration = new PVector(0, 0);
this.mass = 1;
};
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 * 16, this.mass * 16, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#333333';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
context.closePath();
};
Mover.prototype.applyForce = function(force) {
const f = PVector.div(force, this.mass);
this.acceleration.add(f);
};
// ์ดํธ๋ํฐ ์์ฑ์ ํจ์
const Attractor = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.mass = 20;
this.G = 1;
// ๋ง์ฐ์ค ์ํธ ์์ฉ
this.dragOffset = new PVector(0, 0);
this.dragging = false;
this.rollover = false;
};
// ์ดํธ๋ํฐ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฉ์๋
Attractor.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, this.mass, this.mass, 0, 0, Math.PI * 2, false);
if (this.dragging) {
context.fillStyle = '#86888b';
} else if (this.rollover) {
context.fillStyle = '#a3abb3';
} else {
context.fillStyle = '#b1c1d1';
}
context.fill();
context.closePath();
};
// ์ดํธ๋ํฐ์ mover ์ฌ์ด์ ์ธ๋ ฅ์ ๊ณ์ฐํ๋ ๋ฉ์๋
Attractor.prototype.calculateAttraction = function(mover) {
const force = PVector.sub(this.position, mover.position);
let distance = force.mag();
distance = PVector.constrain(distance, 5, 25);
force.normalize();
const strength = (this.G * this.mass * mover.mass) / (distance * distance);
force.mult(strength);
return force;
};
// ๋ง์ฐ์ค ์ํธ ์์ฉ ๋ฉ์๋
Attractor.prototype.handleHover = function(mx, my) {
const d = PVector.dist(mx, my, this.position.x, this.position.y);
if (d < this.mass) {
this.rollover = true;
} else {
this.rollover = false;
}
};
Attractor.prototype.handlePress = function(mx, my) {
var d = PVector.dist(mx, my, this.position.x, this.position.y);
if (d < this.mass) {
console.log('setting dragging to true');
this.dragging = true;
this.dragOffset.x = this.position.x - mx;
this.dragOffset.y = this.position.y - my;
}
};
Attractor.prototype.handleDrag = function(mx, my) {
console.log('should we be dragging?' + this.dragging);
if (this.dragging) {
this.position.x = mx + this.dragOffset.x;
this.position.y = my + this.dragOffset.y;
}
};
Attractor.prototype.stopDragging = function() {
console.log('setting dragging to false');
this.dragging = false;
};
const mover = new Mover();
const attractor = new Attractor();
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
// ์ธ๋ ฅ ์ ์ฉ
const force = attractor.calculateAttraction(mover);
mover.applyForce(force);
mover.update();
attractor.display();
mover.display();
requestAnimationFrame(update);
};
update();
</script>
์ ์ ๋ง์ฐฐ๊ณผ ํญ๋ ฅ์ ์ด์ฉํ๋ ๊ฒ์ฒ๋ผ, ๋ง์ Mover ๊ฐ์ฒด๋ฅผ ํฌํจํ๊ธฐ ์ํด ๋ฐฐ์ด์ ์จ์ ์ด ์์ ๋ฅผ ํ์ฅํ ์๋ ์๋ค.
<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 positionX = canvas.offsetLeft;
const positionY = canvas.offsetTop;
let mouseX = 0;
let mouseY = 0;
const mouseMoved = function(e) {
mouseX = e.clientX - positionX;
mouseY = e.clientY - positionY;
attractor.handleHover(mouseX, mouseY);
};
const mousePressed = function(e) {
e.preventDefault();
attractor.handlePress(mouseX, mouseY);
};
const mouseDragged = function() {
attractor.handleHover(mouseX, mouseY);
attractor.handleDrag(mouseX, mouseY);
};
const mouseReleased = function() {
attractor.stopDragging();
canvas.removeEventListener('mousemove', mouseDragged);
canvas.removeEventListener('mousemove', mouseReleased);
};
canvas.addEventListener('mousemove', mouseMoved);
canvas.addEventListener('mousedown', function(e) {
mousePressed(e);
canvas.addEventListener('mousemove', mouseDragged);
canvas.addEventListener('mouseup', mouseReleased);
});
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.sub = function(v) {
this.x = this.x - v.x;
this.y = this.y - v.y;
};
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.sub = function(v1, v2) {
const v3 = new PVector(v1.x - v2.x, v1.y - v2.y);
return v3;
};
PVector.div = function(v1, n) {
const v3 = new PVector(v1.x / n, v1.y / n);
return v3;
};
PVector.constrain = function(n, low, high) {
return Math.max(Math.min(n, high), low);
};
PVector.dist = function(...args) {
if (args.length === 4) {
//2D
return Math.hypot(args[2] - args[0], args[3] - args[1]);
} else if (args.length === 6) {
//3D
return Math.hypot(args[3] - args[0], args[4] - args[1], args[5] - args[2]);
}
};
// ์ง๋, x, y ๊ฐ์ ๋ฐ์์ค๋ Mover ๊ฐ์ฒด
const Mover = function(mass, x, y) {
this.position = new PVector(x, y);
this.velocity = new PVector(1, 0);
this.acceleration = new PVector(0, 0);
this.mass = mass;
};
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 * 16, this.mass * 16, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#333333';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
context.closePath();
};
Mover.prototype.applyForce = function(force) {
const f = PVector.div(force, this.mass);
this.acceleration.add(f);
};
// ์ดํธ๋ํฐ ์์ฑ์ ํจ์
const Attractor = function() {
this.position = new PVector(canvas.width / 2, canvas.height / 2);
this.mass = 20;
this.G = 1;
this.dragOffset = new PVector(0, 0);
this.dragging = false;
this.rollover = false;
};
// ์ดํธ๋ํฐ๋ฅผ ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฉ์๋
Attractor.prototype.display = function() {
context.beginPath();
context.ellipse(this.position.x, this.position.y, this.mass, this.mass, 0, 0, Math.PI * 2, false);
if (this.dragging) {
context.fillStyle = '#86888b';
} else if (this.rollover) {
context.fillStyle = '#a3abb3';
} else {
context.fillStyle = '#b1c1d1';
}
context.fill();
context.closePath();
};
// ์ดํธ๋ํฐ์ mover ์ฌ์ด์ ์ธ๋ ฅ์ ๊ณ์ฐํ๋ ๋ฉ์๋
Attractor.prototype.calculateAttraction = function(mover) {
const force = PVector.sub(this.position, mover.position);
let distance = force.mag();
distance = PVector.constrain(distance, 5, 25);
force.normalize();
const strength = (this.G * this.mass * mover.mass) / (distance * distance);
force.mult(strength);
return force;
};
// ๋ง์ฐ์ค ์ํธ ์์ฉ ๋ฉ์๋
Attractor.prototype.handleHover = function(mx, my) {
const d = PVector.dist(mx, my, this.position.x, this.position.y);
if (d < this.mass) {
this.rollover = true;
} else {
this.rollover = false;
}
};
Attractor.prototype.handlePress = function(mx, my) {
var d = PVector.dist(mx, my, this.position.x, this.position.y);
if (d < this.mass) {
console.log('setting dragging to true');
this.dragging = true;
this.dragOffset.x = this.position.x - mx;
this.dragOffset.y = this.position.y - my;
}
};
Attractor.prototype.handleDrag = function(mx, my) {
console.log('should we be dragging?' + this.dragging);
if (this.dragging) {
this.position.x = mx + this.dragOffset.x;
this.position.y = my + this.dragOffset.y;
}
};
Attractor.prototype.stopDragging = function() {
console.log('setting dragging to false');
this.dragging = false;
};
const movers = [];
const attractor = new Attractor();
for (let i = 0; i < 10; i++) {
movers[i] = new Mover(Math.random(0.1, 2), Math.random() * canvas.width,Math.random() * canvas.height);
};
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
attractor.display();
for (let i = 0; i < movers.length; i++) {
const force = attractor.calculateAttraction(movers[i]);
movers[i].applyForce(force);
movers[i].update();
movers[i].display();
}
requestAnimationFrame(update);
};
update();
</script>
์ํธ ์ธ๋ ฅ
์ด์ ๋ ์ฌ๋ฌ ๋ฌผ์ฒด๊ฐ ์๋ก ๋์ด๋น๊ธฐ๋ ์กฐ๊ธ ๋ ๋ณต์กํ ํ๋ก๊ทธ๋จ์ ์ง๋ณด์. ์์คํ ๋ด๋ถ์ ๋ชจ๋ ๋ฌผ์ฒด๊ฐ ์๊ธฐ ์์ ์ ์ ์ธํ ๋ค๋ฅธ ๋ชจ๋ ๋ฌผ์ฒด๋ฅผ ๋์ด๋น๊ธฐ๋ ์ํฉ์ ๊ตฌํํด์ผ ํ๋ค.
ํ์ฌ ์ ํ๋ก๊ทธ๋จ์ ๋ชจ๋ Mover i์ ๋ํด update์ display๋ฅผ ์ฌ์ฉํ๋๋ก ์ง์ฌ์๋ค. ์ด์ ์ด ์ฝ๋๋ฅผ ๋ชจ๋ Mover i๊ฐ ๋ํด ๋ค๋ฅธ ๋ชจ๋ Mover j๋ฅผ ๋์ด๋น๊ธฐ๋ฉด์ update์ display๋ฅผ ํธ์ถํ๋๋ก ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ค.
// Mover์ update๋ฅผ ๋ถ๋ฅด๊ธฐ ์ ์ ๋ชจ๋ ์ธ๋ ฅ์ ๊ณ์ฐ
for (let i = 0; i < movers.length; i++) {
// ๋ชจ๋ mover์ ๋ํด ์ธ๋ ฅ์ ๊ณ์ฐ
for (let j = 0; j < movers.length; j++) {
const force = movers[j].calculateAttraction(movers[i]);
movers[i].applyForce(force);
}
}
// ๋ชจ๋ ์ธ๋ ฅ์ ๊ณ์ฐ ํ update
for (let i = 0; i < movers.length; i++) {
movers[i].update();
movers[i].display();
}
์์ง Mover์๊ฒ ์ฌ์ฉํ calculateAttraction() ๋ฉ์๋๊ฐ ์ ํด์ง์ง ์์ ์ฝ๋๊ฐ ์๋ํ์ง ์๋๋ค. ์ด์ ์์ ์์๋ Attractor ๊ฐ์ฒด์ calculateAttraction() ๋ฉ์๋๋ฅผ ๋ค๋ค๋ค. ์ด์ Mover๊ฐ ๋ค๋ฅธ Mover๋ฅผ ๋์ด๋น๊ธฐ๋ฏ๋ก ์ด ๋ฉ์๋๋ฅผ ๋ณต์ฌํด์ Mover ๊ฐ์ฒด๋ก ์ฎ๊ฒจ์ฃผ์.
Mover.prototype.calculateAttraction = function(mover) {
const force = PVector.sub(this.position, mover.position);
let distance = force.mag();
distance = PVector.constrain(distance, 5, 25);
force.normalize();
const strength = (this.G * this.mass * mover.mass) / (distance * distance);
force.mult(strength);
return force;
};
์ฌ๊ธฐ์๋ ์์ ๋ฌธ์ ๊ฐ ํ๋ ์๋ค. ๋ชจ๋ Mover i์ Mover j์ ๋ํด i ๊ฐ j ์ผ ๊ฒฝ์ฐ๋ ๊ด์ฐฎ์๊น?
์๋ฅผ ๋ค์ด, 3๋ฒ Mover๊ฐ 3๋ฒ Mover๋ฅผ ๋์ด๋น๊ธธ ์ ์์๊น? ๋ฌผ๋ก ์ด๋ฐ ์ผ์ ์ผ์ด๋ ์ ์๋ค. ๋ฌผ์ฒด๊ฐ 5๊ฐ ์๋ค๋ฉด 3๋ฒ Mover๋ ์๊ธฐ ์์ ์ ์ ์ธํ 0, 1, 2, 4 ๋ฒ Mover๋ฅผ ๋์ด๋น๊ฒจ์ผ ํ๋ค. ๊ทธ๋ ์ง๋ง 3๋ฒ๊ณผ 1๋ฒ Mover ๊ฐ์ ํ๊ณผ ๋ฐ๋๋ก 1๋ฒ๊ณผ 3๋ฒ Mover ๊ฐ์ ๋ชจ๋ ํ์ ๊ณ์ฐํ๊ณ ์ ์ฉํด์ผ ํ ํ์๋ ์๋ค. ์ด๋ ๊ฒ ๊ณ์ฐํ ํ์ ๊ฐ์ง๋ง, ์ด์ ๋ฐ๋ฅธ ๊ฐ์๋๋ ๊ฐ ๋ฌผ์ฒด์ ์ง๋์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ๊ฒ์ด๋ค.
์๋ ๋์์๋ ํ๋ฅผ ์ดํด๋ณด์.
0 โข 1, 2, 3, 4
1 โข 0, 2, 3, 4
2 โข 0, 1, 3, 4
3 โข 0, 1, 2, 4
๋ฐ๋ผ์, for๋ฌธ์ ์์ ํ์ฌ ๋ด๋ถ์ ๋ฐ๋ณต๋ฌธ์ผ๋ก ์ค์ค๋ก ๋์ด๋น๊ธฐ๋ Mover๋ฅผ ํผํ๋๋ก ๋ง๋ค๊ณ ์์ ๋ฅผ ๋ง๋ฌด๋ฆฌํ์.
<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 G = 1;
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.sub = function(v1, v2) {
const v3 = new PVector(v1.x - v2.x, v1.y - v2.y);
return v3;
};
PVector.div = function(v1, n) {
const v3 = new PVector(v1.x / n, v1.y / n);
return v3;
};
PVector.constrain = function(n, low, high) {
return Math.max(Math.min(n, high), low);
};
PVector.dist = function(...args) {
if (args.length === 4) {
return Math.hypot(args[2] - args[0], args[3] - args[1]);
} else if (args.length === 6) {
return Math.hypot(args[3] - args[0], args[4] - args[1], args[5] - args[2]);
}
};
const Mover = function(mass, x, y) {
this.position = new PVector(x, y);
this.velocity = new PVector(1, 0);
this.acceleration = new PVector(0, 0);
this.mass = mass;
};
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 * 16, this.mass * 16, 0, 0, Math.PI * 2, false);
context.lineWidth = 5;
context.strokeStyle = '#333333';
context.stroke();
context.fillStyle = '#ffffff';
context.fill();
context.closePath();
};
Mover.prototype.applyForce = function(force) {
const f = PVector.div(force, this.mass);
this.acceleration.add(f);
};
// ์ธ๋ ฅ์ ๊ณ์ฐํ๋ ๋ฉ์๋
Mover.prototype.calculateAttraction = function(mover, i) {
const force = PVector.sub(this.position, mover.position);
let distance = force.mag();
distance = PVector.constrain(distance, 5, 25);
force.normalize();
const strength = (G * this.mass * mover.mass) / (distance * distance);
force.mult(strength);
return force;
};
const movers = [];
for (var i = 0; i < 5; i++) {
movers[i] = new Mover(Math.random(0.1, 2), Math.random() * canvas.width,Math.random() * canvas.height);
}
const update = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
// Mover์ update๋ฅผ ๋ถ๋ฅด๊ธฐ ์ ์ ๋ชจ๋ ์ธ๋ ฅ์ ๊ณ์ฐ
for (let i = 0; i < movers.length; i++) {
// ๋ชจ๋ mover์ ๋ํด ์ธ๋ ฅ์ ๊ณ์ฐ
for (let j = 0; j < movers.length; j++) {
if (i !== j) {
const force = movers[j].calculateAttraction(movers[i]);
movers[i].applyForce(force);
}
}
movers[i].update();
movers[i].display();
}
requestAnimationFrame(update);
};
update();
</script>