var TOP = 1; var RIGHT = 2 var BOTTOM = 3; var LEFT = 4; var TOP_RIGHT = 5; var BOTTOM_RIGHT = 6; var BOTTOM_LEFT = 7; var TOP_LEFT = 8; var wWidth = window.innerWidth; var wHeight = window.innerHeight; var oldWHeight; var oldWWidth; function updateVars(){ oldWWidth = wWidth; oldWHeight = wHeight; wWidth = window.innerWidth; wHeight = window.innerHeight; } function collisionDetection(obj0, obj1){ var sp = strokePadding; if (sp == null) sp = 0; if (obj0.isEllipse && obj1.isRectangle){ //Ball var b = obj0; //Rectangle var r = obj1; for (var i = 0; i < TWO_PI; i += PI / 32){ /* Check every borderpoint of the ball beginning at the top in clock direction up to top again */ // Ball Center X var bcx = b.x; // Ball Center Y var bcy = b.y; // Ball Border X var bbx = b.x + sin(i) * b.r; // Ball Border Y inverted because Y = 0 is the TOP of the screen var bby = b.y - cos(i) * b.r; // Rectangle Width var rW = r.width + 2 * sp; // Rectangle Height var rH = r.height + 2 * sp; // Rectangle Border X var rX = r.x - sp; // Rectangle Border Y var rY = r.y - sp; // Objects touch if (bbx > rX && bbx < rX + rW && bby > rY && bby < rY + rH){ // STRAIGHT FACES // //Top/Bottom touch if (bcx > rX && bcx < rX + rW){ //Top touch if (b.v.y > 0) return {isTouching: true, location: TOP}; //Bottom touch if (b.v.y < 0) return {isTouching: true, location: BOTTOM}; } //Left/Right touch if (bcy > rY && bcy < rY + rH){ //Left touch if (b.v.x > 0) return {isTouching: true, location: LEFT}; //Right touch if (b.v.x < 0) return {isTouching: true, location: RIGHT}; } // CORNERS // // BOTTOM Left/Right if (i > 0 && i <= PI / 2) return {isTouching: true, location: BOTTOM_LEFT}; //LEFT Bottom/Top if (i > PI / 2 && i <= PI) return {isTouching: true, location: TOP_LEFT}; //TOP Left/Right if (i > PI && i <= PI + PI / 2) return {isTouching: true, location: TOP_RIGHT}; //RIGHT Bottom/Top if (i > PI + PI / 2 && i <= TWO_PI) return {isTouching: true, location: BOTTOM_RIGHT}; } } } if (obj0.isEllipse && obj1.isEllipse){ //Ball 1 var b1 = obj0; //Ball 2 var b2 = obj1; //Balls are close to each other if (b1.x + b1.r > b2.x - b2.r && b1.x - b1.r < b2.x + b2.r && b1.y + b1.r > b2.y - b2.r && b1.y - b1.r < b2.y + b2.r){ var distance = sqrt(pow(b1.x - b2.x, 2) + pow(b1.y - b2.y, 2)); if (distance < b1.r + b2.r) return {isTouching: true}; } } return {isTouching: false, location: 0}; } function performCollision(obj0, obj1, collision){ if (obj0.isEllipse){ var ball = obj0; //Ball collides with frameborder if (obj1.isFrameborder){ switch (collision.location){ case BOTTOM: ball.v.y *= -1; break; case LEFT: case RIGHT: ball.v.x *= -1; break; } if (testMode && collision.location == TOP) ball.v.y *= -1; ball.move(); } //Ball collides with any brick if (obj1.isBrick){ switch (collision.location){ case TOP: case BOTTOM: ball.v.y *= -1; ball.move(); return; case LEFT: case RIGHT: ball.v.x *= -1; ball.move(); return; case TOP_LEFT: var cornerX = obj1.x; var cornerY = obj1.y; break; case TOP_RIGHT: var cornerX = obj1.x + obj1.width; var cornerY = obj1.y; break; case BOTTOM_LEFT: var cornerX = obj1.x; var cornerY = obj1.y + obj1.height; break; case BOTTOM_RIGHT: var cornerX = obj1.x + obj1.width; var cornerY = obj1.y + obj1.height; break; } var nx = ball.x - cornerX; var ny = ball.y - cornerY; var length = sqrt(nx * nx + ny * ny); nx /= length; ny /= length; var projection = ball.v.x * nx + ball.v.y * ny; ball.v.x = ball.v.x - 2 * projection * nx; ball.v.y = ball.v.y - 2 * projection * ny; ball.move(); } //Ball collides with paddleboard if (obj1.isPaddle){ switch (collision.location){ case TOP: case TOP_LEFT: case TOP_RIGHT: ball.v.x = ball.calcVelocityX(obj1, ball.v.x); ball.v.y = -ball.calcVelocityY(); ball.move(); return; case LEFT: case RIGHT: ball.v.x *= -1; ball.move(); return; case BOTTOM_LEFT: var cornerX = obj1.x; var cornerY = obj1.y + obj1.height; break; case BOTTOM_RIGHT: var cornerX = obj1.x + obj1.width; var cornerY = obj1.y + obj1.height; break; } var nx = ball.x - cornerX; var ny = ball.y - cornerY; var length = sqrt(nx * nx + ny * ny); nx /= length; ny /= length; var projection = ball.v.x * nx + ball.v.y * ny; ball.v.x = ball.v.x - 2 * projection * nx; ball.v.y = ball.v.y - 2 * projection * ny; ball.move(); } //Ball collides with other ball if (obj1.isEllipse){ //Ball 1 var b1 = obj0; //Ball 2 var b2 = obj1; //Set mass equal to radius of each ball b1.mass = b1.r; b2.mass = b2.r; //Colliding angle of ball 1 to ball 2 using arc tan of both x and y differences var collisionAngle = atan2((b2.y - b1.y), (b2.x - b1.x)); //Converting directions of velocity vector of balls into angles var d1 = atan2(b1.v.y, b1.v.x); var d2 = atan2(b2.v.y, b2.v.x); //Ignoring mass effects new velocites are simply magnitude multiplied with value of angle differences var newXspeed1 = b1.v.mag * cos(d1 - collisionAngle); var newYspeed1 = b1.v.mag * sin(d1 - collisionAngle); var newXspeed2 = b2.v.mag * cos(d2 - collisionAngle); var newYspeed2 = b2.v.mag * sin(d2 - collisionAngle); //According to the principle of linear momentum, kinetic energy stays the same after collision, so velocities are now related to masses var finalXspeed1 = ((b1.mass - b2.mass) * newXspeed1 + b2.mass * 2 * newXspeed2) / (b1.mass + b2.mass); var finalYspeed1 = newYspeed1; var finalXspeed2 = (b1.mass * 2 * newXspeed1 + (b2.mass - b1.mass) * newXspeed2) / (b1.mass + b2.mass); var finalYspeed2 = newYspeed2; //Values of collisionAngle var cosAngle = cos(collisionAngle); var sinAngle = sin(collisionAngle); //To also keep velocites relative to pure collisionAngle, subtract sin*x from cos*x and add sin*y to cos*y because coordSystem has y = 0 on the top var u1x = cosAngle * finalXspeed1 - sinAngle * finalYspeed1; var u1y = sinAngle * finalXspeed1 + cosAngle * finalYspeed1; var u2x = cosAngle * finalXspeed2 - sinAngle * finalYspeed2; var u2y = sinAngle * finalXspeed2 + cosAngle * finalYspeed2; //Set new velocities to both balls b1.v.x = u1x; b1.v.y = u1y; b2.v.x = u2x; b2.v.y = u2y; //Update magnitude b1.v.mag = sqrt(pow(b1.v.x, 2) + pow(b1.v.y, 2)); b2.v.mag = sqrt(pow(b2.v.x, 2) + pow(b2.v.y, 2)); //Move balls one vx/vy forward to avoid double inverting collision detection b1.x += b1.v.x; b1.y += b1.v.y; b2.x += b2.v.x; b2.y += b2.v.y; } } } function toTimeString(time, hoursWanted){ var time = floor(time / 10); var hs = String(floor(time % 100)); var fs = String(floor((time / 100) % 60)); if (hoursWanted){ var min = String(floor(((time / 100) / 60) % 60)); var hr = String(floor(((time / 100) / 60) / 60)); if (hs.length < 2) hs = "0" + hs; if (fs.length < 2) fs = "0" + fs; if (min.length < 2) min = "0" + min; if (hr.length < 2) hr = "0" + hr; var timeString = hr + ":" + min + ":" + fs + ":" + hs; } else { var min = String(floor(((time / 100) / 60) % 60)); if (hs.length < 2) hs = "0" + hs; if (fs.length < 2) fs = "0" + fs; if (min.length < 2) min = "0" + min; var timeString = min + ":" + fs + ":" + hs; } return timeString; } function setCookie(name, value, years){ var expires = ""; if (years){ var date = new Date(); date.setTime(date.getTime() + (years * 365 * 24 * 60 * 60 * 1000)); expires = "; expires=" + date.toUTCString(); } document.cookie = name + "=" + value + expires + "; path=/"; } function getCookie(name){ var nameEQ = name + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++){ var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; } function deleteCookies(){ for (var i = 0; i < arguments.length; i++) setCookie(arguments[i], "", -1); } function deleteAllCookies(){ var cookies = document.cookie.split(";"); for (var i = 0; i < cookies.length; i++) deleteCookies(cookies[i].split("=")[0]); } Array.prototype.shuffle = function(){ let currentIndex = this.length, temporaryValue, randomIndex; while (0 != currentIndex) { randomIndex = floor(random() * currentIndex); currentIndex -= 1; temporaryValue = this[currentIndex]; this[currentIndex] = this[randomIndex]; this[randomIndex] = temporaryValue; } } Array.prototype.clone = function() { return this.slice(0); }; Array.prototype.partitiate = function(dimensions){ if (dimensions == 0) return this; let parts = []; while(this.length) parts.push(this.splice(0, round(pow(this.length, 1 / (1 + 1 / dimensions)))).partitiate(dimensions - 1)); return parts; }