old method for calculating ball rotation velocity... "use strict"; // see // http://farside.ph.utexas.edu/teaching/301/301.pdf // for a good introduction to Classical Mechanics // in particular the section on torque class Ship { constructor(initX, initY) { // position this.x = initX; this.y = initY; // rotation in radians, 0 is along the +ve X axis, +ve Y axis goes down the screen this.rot = 270 * Math.PI / 180; // velocity this.velX = 0; this.velY = 0; // scale of the ship this.size = 10; // total mass of ship and balls // mass of ship is 1 => mass of balls is defined relative to the ship this.mass = 1; // no balls yet this.balls = []; } // add a new ball addBall(mass) { // new rod ends up hanging downwards this.balls.push(new Ball(mass)); // increase total mass of ship and balls this.mass += mass; console.log('addBall: mass=' + mass + ' total=' + this.mass); } draw(ctx) { // helper that translates and rotates all the coords for us var path = new PathBuilder(ctx, this.x, this.y, this.rot); // draw the ship ctx.beginPath(); // top triangle path.moveTo(this.size, this.size); path.lineTo(2 * this.size, 0); path.lineTo(this.size, -this.size); // right side of the square path.lineTo(-this.size, -this.size); path.lineTo(-this.size, 0); // engine triangle path.lineTo(-(2 * this.size), -this.size); path.lineTo(-(2 * this.size), this.size); path.lineTo(-this.size, 0); // left side of the square path.lineTo(-this.size, this.size); // back to the start ctx.closePath(); // draw it //ctx.fillStyle = '#ff0000'; //ctx.fill(); ctx.strokeStyle = '#ff0000'; ctx.lineWidth = 2; ctx.stroke(); // the balls ctx.beginPath(); ctx.strokeStyle = '#ff00ff'; ctx.lineWidth = 2; // first ball rotates about the ship var bx = this.x; var by = this.y; // Array.forEach is really slow apparently // https://coderwall.com/p/kvzbpa/don-t-use-array-foreach-use-for-instead for (var i = 0; i < this.balls.length; i++) { var ball = this.balls[i]; // rod from previous ball/ship to this one ctx.moveTo(bx, by); // update bx,by to be the position of this ball // next ball rotates about this one bx += ball.rodLen * Math.cos(ball.rot); by += ball.rodLen * Math.sin(ball.rot); ctx.lineTo(bx, by); ctx.moveTo(bx + ball.size, by); ctx.ellipse(bx, by, ball.size, ball.size, 0, 0, 2 * Math.PI); ctx.stroke(); } } // +ve angles are clockwise rotate(degs) { // convert to radians var rads = degs * Math.PI / 180; this.rot += rads; // TODO: clamp to 0->360 } // increase velocity in direction of rotation // or in the given direction if specified thrust(delta, angle = this.rot) { // accelerate the ship this.velX += delta * Math.cos(angle) / this.mass; this.velY += delta * Math.sin(angle) / this.mass; // apply torque to the balls // Array.forEach is really slow apparently // https://coderwall.com/p/kvzbpa/don-t-use-array-foreach-use-for-instead for (var i = 0; i < this.balls.length; i++) { var ball = this.balls[i]; // angle between this balls rotation about the previous ball/ship // and the direction of thrust // only need the sin (ie tangenial velocity), cos is absorbed by the rod // TODO: do we need to take account of lever length too, ie how far the ship is from the ball? // for gravity the lever length is distance from ball to centre of mass // TODO: does force along the rod cause previous ball/ship to move? // => 3+ body problem to deal with var sint = Math.sin(ball.rot - angle); // increase angular velocity ball.vel += delta * sint / (ball.rodLen * ball.mass); } } // move the ship and balls based on current position and velocity timeStep() { this.x += this.velX; this.y += this.velY; // wrap around the display if (this.x < 0) this.x += DBGame.displayWidth; if (this.x > DBGame.displayWidth) this.x -= DBGame.displayWidth; if (this.y < 0) this.y += DBGame.displayHeight; if (this.y > DBGame.displayHeight) this.y -= DBGame.displayHeight; // any drag to slow us down? // just do velX *= drag; velY *= drag? or do you need to work out |vel| * drag then assign it back to X,Y again? // balls are easy, vel *= drag // 0,0 is top left, so gravity is equivalent to thrust along the +ve Y axis // TODO // gravity acts at centre of mass of the system as a whole // => moves the ship - this also moves the whole frame of reference as the balls are at fixed distances defined by the rods // does it mean torque will act on the balls, lever length is distance from COM to each ball? // or does gravity act equally everywhere and so there is no torque? // does gravity tend to stop balls rotating, by pulling them downwards if they are trying to rotate upwards? //this.thrust(DBGame.gravity * this.mass, 90 * Math.PI / 180); // rotate each ball // Array.forEach is really slow apparently // https://coderwall.com/p/kvzbpa/don-t-use-array-foreach-use-for-instead for (var i = 0; i < this.balls.length; i++) { var ball = this.balls[i]; ball.rot += ball.vel; // TODO: should clamp this to 0->360 => make it a method in Ball and do the clamping there } } } // helper to build paths at the given angle, centered on the given origin // need to call ctx.beginPath() before using any of the PathBuilder functions class PathBuilder { constructor(ctx, oX, oY, rot) { this.ctx = ctx; this.oX = oX; this.oY = oY; // rotation constants this.rotc = Math.cos(rot); this.rots = Math.sin(rot); } moveTo(x, y) { var rotx = this.oX + (x * this.rotc - y * this.rots); var roty = this.oY + (x * this.rots + y * this.rotc); this.ctx.moveTo(rotx, roty); } lineTo(x, y) { var rotx = this.oX + (x * this.rotc - y * this.rots); var roty = this.oY + (x * this.rots + y * this.rotc); this.ctx.lineTo(rotx, roty); } }