let TOP = 1, RIGHT = 2, BOTTOM = 3, LEFT = 4, TOP_RIGHT = 5, BOTTOM_RIGHT = 6, BOTTOM_LEFT = 7, TOP_LEFT = 8; function collisionDetection(obj0, obj1){ let sp = strokePadding; if (sp == null) sp = 0; if (obj0.isEllipse && obj1.isRectangle){ //Ball let b = obj0; //Rectangle let r = obj1; for (let 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 let bcx = b.x; // Ball Center Y let bcy = b.y; // Ball Border X let bbx = b.x + sin(i) * b.r; // Ball Border Y inverted because Y = 0 is the TOP of the screen let bby = b.y - cos(i) * b.r; // Rectangle Width let rW = r.width + 2 * sp; // Rectangle Height let rH = r.height + 2 * sp; // Rectangle Border X let rX = r.x - sp; // Rectangle Border Y let 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 let b1 = obj0; //Ball 2 let 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){ let 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){ let 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){ let cornerX, cornerY; 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: cornerX = obj1.x; cornerY = obj1.y; break; case TOP_RIGHT: cornerX = obj1.x + obj1.width; cornerY = obj1.y; break; case BOTTOM_LEFT: cornerX = obj1.x; cornerY = obj1.y + obj1.height; break; case BOTTOM_RIGHT: cornerX = obj1.x + obj1.width; cornerY = obj1.y + obj1.height; break; } let nx = ball.x - cornerX; let ny = ball.y - cornerY; let length = sqrt(nx * nx + ny * ny); nx /= length; ny /= length; let 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){ let cornerX, cornerY; 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: cornerX = obj1.x; cornerY = obj1.y + obj1.height; break; case BOTTOM_RIGHT: cornerX = obj1.x + obj1.width; cornerY = obj1.y + obj1.height; break; } let nx = ball.x - cornerX; let ny = ball.y - cornerY; let length = sqrt(nx * nx + ny * ny); nx /= length; ny /= length; let 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 let b1 = obj0; //Ball 2 let 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 let collisionAngle = atan2((b2.y - b1.y), (b2.x - b1.x)); //Converting directions of velocity vector of balls into angles let d1 = atan2(b1.v.y, b1.v.x); let d2 = atan2(b2.v.y, b2.v.x); //Ignoring mass effects new velocites are simply magnitude multiplied with value of angle differences let newXspeed1 = b1.v.mag * cos(d1 - collisionAngle); let newYspeed1 = b1.v.mag * sin(d1 - collisionAngle); let newXspeed2 = b2.v.mag * cos(d2 - collisionAngle); let 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 let finalXspeed1 = ((b1.mass - b2.mass) * newXspeed1 + b2.mass * 2 * newXspeed2) / (b1.mass + b2.mass); let finalYspeed1 = newYspeed1; let finalXspeed2 = (b1.mass * 2 * newXspeed1 + (b2.mass - b1.mass) * newXspeed2) / (b1.mass + b2.mass); let finalYspeed2 = newYspeed2; //Values of collisionAngle let cosAngle = cos(collisionAngle); let 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 let u1x = cosAngle * finalXspeed1 - sinAngle * finalYspeed1; let u1y = sinAngle * finalXspeed1 + cosAngle * finalYspeed1; let u2x = cosAngle * finalXspeed2 - sinAngle * finalYspeed2; let 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){ time = floor(time / 10); let hs = String(floor(time % 100)); let fs = String(floor((time / 100) % 60)); if (hoursWanted){ let min = String(floor(((time / 100) / 60) % 60)); let 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; let timeString = hr + ":" + min + ":" + fs + ":" + hs; } else { let 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; let timeString = min + ":" + fs + ":" + hs; } return timeString; } function setCookie(name, value, years){ let expires = ""; if (years){ let 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){ let nameEQ = name + "="; let ca = document.cookie.split(';'); for (let i = 0; i < ca.length; i++){ let 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 (let i = 0; i < arguments.length; i++) setCookie(arguments[i], "", -1); } function deleteAllCookies(){ let cookies = document.cookie.split(";"); for (let 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); }; //Divides big Array into big multidimensional Array 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; } String.prototype.isValidHEX = function(){ return /(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(this); } String.prototype.capitalize = function(){ return this.charAt(0).toUpperCase() + this.slice(1); } function debugInformation(){ let x = 0, y = 0; textSize(12); textStyle(NORMAL); stroke(255); strokeWeight(1); fill(255); text("FPS : " + round(frameRate()), 10 + x, 10 + textAscent("FPS : ") + y); text("MouseX : " + round(mouseX + x), 10 + x, 10 + textAscent("FPS : ") + 10 + textAscent("MouseX : ") + y); text("MouseY : " + round(-mouseY - y), 10 + x, 10 + textAscent("FPS : ") + 10 + textAscent("MouseX : ") + 10 + textAscent("MouseY : ") + y); } function ColorPicker(editor) { this.movingObject = ""; this.updateFromGraphical = function(){ this.h = (1 - $("#hue_picker").position().top / $("#hue").height()) * 360; this.s = ($("#sb_picker").position().left + 8) / $("#saturation").width() * 100; this.v = (1 - ($("#sb_picker").position().top + 8) / $("#value").height()) * 100; this.r = HSVtoRGB(this.h, this.s, this.v).r; this.g = HSVtoRGB(this.h, this.s, this.v).g; this.b = HSVtoRGB(this.h, this.s, this.v).b; this.hex = RGBtoHEX(this.r, this.g, this.b); this.updateInterface(); } this.updateFromHSV = function(){ this.h = $($("#color_picker_hsv input")[0]).val(); this.s = $($("#color_picker_hsv input")[1]).val(); this.v = $($("#color_picker_hsv input")[2]).val(); this.r = HSVtoRGB(this.h, this.s, this.v).r; this.g = HSVtoRGB(this.h, this.s, this.v).g; this.b = HSVtoRGB(this.h, this.s, this.v).b; this.hex = RGBtoHEX(this.r, this.g, this.b); this.updateFromHEX(null, true); this.updateInterface(); } this.updateFromRGB = function(){ this.r = $($("#color_picker_rgb input")[0]).val(); this.g = $($("#color_picker_rgb input")[1]).val(); this.b = $($("#color_picker_rgb input")[2]).val(); this.h = RGBtoHSV(this.r, this.g, this.b).h; this.s = RGBtoHSV(this.r, this.g, this.b).s; this.v = RGBtoHSV(this.r, this.g, this.b).v; this.hex = RGBtoHEX(this.r, this.g, this.b); this.updateFromHEX(null, true); this.updateInterface(); } this.updateFromHEX = function(input, otf){ if (!otf){ //Not on the fly if ($(input).val().isValidHEX()) this.hex = $(input).val(); else { alert("Error!"); return; } } this.r = HEXtoRGB(this.hex).r; this.g = HEXtoRGB(this.hex).g; this.b = HEXtoRGB(this.hex).b; this.h = RGBtoHSV(this.r, this.g, this.b).h; this.s = RGBtoHSV(this.r, this.g, this.b).s; this.v = RGBtoHSV(this.r, this.g, this.b).v; this.updateInterface(); } this.updateInterface = function(){ let r = $($("#color_picker_rgb input")[0]), g = $($("#color_picker_rgb input")[1]), b = $($("#color_picker_rgb input")[2]), h = $($("#color_picker_hsv input")[0]), s = $($("#color_picker_hsv input")[1]), v = $($("#color_picker_hsv input")[2]), hex = $("#color_picker_hex"), bgColor; r.val(round(this.r)); g.val(round(this.g)); b.val(round(this.b)); h.val(round(this.h)); s.val(round(this.s)); v.val(round(this.v)); bgColor = color(this.r, 0, 0); r.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); bgColor = color(0, this.g, 0); g.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); bgColor = color(0, 0, this.b); b.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); colorMode(HSL); bgColor = color(this.h, 100, 50); h.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); bgColor = color(this.h, this.s, 100 - this.s / 2); s.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); bgColor = color(this.h, 100, this.v / 2); v.css({ "background-color": bgColor.toString(), "color": fontColor(bgColor) }); colorMode(RGB); hex.val(this.hex); hex.css({ "background-color": this.hex, "color": fontColor(color(this.hex)) }); let sRGB = HSVtoRGB(this.h, 100, 100); let saturationBackground = "linear-gradient(to right, #FFF 0%, rgb(" + sRGB.r + "," + sRGB.g + "," + sRGB.b + ") 100%)"; $("#hue_picker").css("top", (1 - this.h / 360) * $("#hue").height()); $("#sb_picker").css({ "left": this.s / 100 * $("#saturation").width() - 8, "top": (1 - this.v / 100) * $("#value").height() - 8 }); $("#saturation").css("background", saturationBackground); } this.mousePressed = function(){ let x = winMouseX - $("#saturation").offset().left; let y = winMouseY - $("#value").offset().top; if (x > 0 && x < $("#saturation").width() && y > 0 && y < $("#value").height()){ this.movingObject = "sb"; } if (x > $("#saturation").width() + 6 && x < $("#saturation").width() + 6 + $("#hue").width() && y > 0 && y < $("#hue").height()){ this.movingObject = "hue"; } this.mouseDragged(); } this.mouseDragged = function(){ if (this.movingObject == "hue"){ let objH = $("#hue"); let picker = $("#hue_picker"); let h = winMouseY - objH.offset().top; if (h > 0 && h < objH.height()){ picker.css("top", h - 1); } else if (h > objH.height()){ picker.css("top", objH.height() - 1); } else if (h < 0){ picker.css("top", -1); } } if (this.movingObject == "sb"){ let objS = $("#saturation"); let objV = $("#value"); let picker = $("#sb_picker"); let s = winMouseX - objS.offset().left; let v = winMouseY - objV.offset().top; if (s > 0 && s < objS.width()){ picker.css("left", s - 8); } else if (s < 0){ picker.css("left", -8); } else if (s < objS.width()){ picker.css("left", objS.width() - 8); } if (v > 0 && v < objV.height()){ picker.css("top", v - 8); } else if (v < 0){ picker.css("top", -8); } else if (v > objV.height()){ picker.css("top", objV.height() - 8); } } this.updateFromGraphical(); } this.mouseReleased = function(){ this.movingObject = ""; } this.getColor = function(){ return this.hex; } } function fontColor(color){ //https://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx let o = (red(color) * 299 + green(color) * 587 + blue(color) * 114) / 1000; return (o > 125) ? "#000" : "#CCC"; } //www.stackoverflow.com --> function RGBtoHEX(r, g, b) { let rgb = b | (g << 8) | (r << 16); return '#' + (0x1000000 + rgb).toString(16).slice(1); } function HEXtoRGB(hex) { let shorthandRegex = /^#?([a-fA-F\d])([a-fA-F\d])([a-fA-F\d])$/i; hex = hex.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); let result = /^#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } function HSVtoRGB(h, s, v) { let r, g, b, i, f, p, q, t; if (arguments.length === 1) { s = h.s, v = h.v, h = h.h; } h /= 360; s /= 100; v /= 100; i = Math.floor(h * 6); f = h * 6 - i; p = v * (1 - s); q = v * (1 - f * s); t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return { r: r * 255, g: g * 255, b: b * 255 }; } function RGBtoHSV(r, g, b) { if (arguments.length === 1) { g = r.g, b = r.b, r = r.r; } let max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min, h, s = (max === 0 ? 0 : d / max), v = max / 255; switch (max) { case min: h = 0; break; case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break; case g: h = (b - r) + d * 2; h /= 6 * d; break; case b: h = (r - g) + d * 4; h /= 6 * d; break; } return { h: h * 360, s: s * 100, v: v * 100 }; } function ranBool(chance){ if (!chance) chance = 2; return Math.floor(Math.random() * chance + 1) == chance ? true : false; }