commit dc030f63ade9e84e9a2c995dda178526f300110e Author: Benjamin Kraft Date: Fri Mar 24 13:04:52 2023 +0100 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6ef218 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea + diff --git a/project.json b/project.json new file mode 100644 index 0000000..b8145a6 --- /dev/null +++ b/project.json @@ -0,0 +1,6 @@ +{ + "display_name": "Maths", + "info_text": "My try of copying GeoGebra.", + "visible": false, + "tags": ["Tool", "Maths"] +} \ No newline at end of file diff --git a/public/color_picker_styles.css b/public/color_picker_styles.css new file mode 100644 index 0000000..826c274 --- /dev/null +++ b/public/color_picker_styles.css @@ -0,0 +1,87 @@ +#color_picker{ + width: 350px; + height: 320px; + margin: 20px; + border: 5px solid #000; + background-color: #000; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + position: relative; +} +#color_picker_numeric{ + width: 80%; + padding: 5%; + margin: 5%; + background-color: #888; + border-radius: 10px; + overflow: auto; +} +.color_picker_rgb{ + float: left; + width: 24%; + height: 35px; + font-size: 25px; + color: #000; +} +.color_picker_rgb:nth-child(1){ + margin-right: 10%; + margin-left: 4%; + background-color: #F00; + +} +.color_picker_rgb:nth-child(2){ + background-color: #0F0; +} +.color_picker_rgb:nth-child(3){ + margin-left: 10%; + background-color: #00F; + color: #FFF; +} +#color_picker_hex{ + width: 50%; + height: 30px; + font-size: 25px; + margin: 10% 25% 0 25%; +} +#saturation{ + position: relative; + width: calc(100% - 33px); + height: 100%; + background: linear-gradient(to right, #FFF 0%, #F00 100%); + float: left; + margin-right: 6px; +} +#value { + width: 100%; + height: 100%; + background: linear-gradient(to top, #000 0%, rgba(255,255,255,0) 100%); +} +#sb_picker{ + border: 2px solid; + border-color: #FFF; + position: absolute; + width: 14px; + height: 14px; + border-radius: 10px; + bottom: 50px; + left: 50px; + box-sizing: border-box; + z-index: 10; +} +#hue { + width: 27px; + height: 100%; + position: relative; + float: left; + background: linear-gradient(to bottom, #F00 0%, #F0F 17%, #00F 34%, #0FF 50%, #0F0 67%, #FF0 84%, #F00 100%); +} +#hue_picker { + position: absolute; + background: #000; + border-bottom: 1px solid #000; + top: 0; + width: 27px; + height: 2px; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..72231db --- /dev/null +++ b/public/index.html @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + Graph + + +
+
+
+
+ Funktionen + + +
+
+
+ Variablen + + noch nicht implementiert + +
+
+
+ Einstellungen + Scroll-Verhalten: + + X-Axen Verschiebung: + +
+
+ Y-Axen Verschiebung: + +
+
+
+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + +
+ Dicke: + + + +
+
+ + \ No newline at end of file diff --git a/public/scripts/Editor.js b/public/scripts/Editor.js new file mode 100644 index 0000000..d10adfe --- /dev/null +++ b/public/scripts/Editor.js @@ -0,0 +1,259 @@ +function Editor(obj, i) { + var x = ($(obj).offset().left * 2 + $(obj).width()) / 2; + var y = ($(obj).offset().top * 2 + $(obj).height()) / 2; + + $("#graph_editor").css({ + "left": x, + "top": y + }); + $("#graph_editor_thickness").val(graphs[i].getThickness()); + $("#graph_editor_input").val(graphs[i].getDisplayFunction()); + $("#graph_editor_wrapper").show(); + editing = true; + + this.colorPicker = new ColorPicker(this); + this.colorPicker.hex = graphs[i].getColor(); + this.colorPicker.updateFromHEX(null, true); + + this.editDone = function(){ + var newFunc = $("#graph_editor_input").val(); + graphs[i].setDisplayFunction(newFunc); + graphs[i].setColor(this.colorPicker.getColor()); + graphs[i].setThickness(parseInt($("#graph_editor_thickness").val())); + $("#function_input").val(graphs[i].getFullDescription()); + $("#visible_" + i).css({ + "background-color": graphs[i].getColor(), + "border-color": graphs[i].getColor() + }); + $("#graph_editor_wrapper").hide(); + editing = false; + updateAll(); + } +} + + +function ColorPicker(editor) { + this.movingObject = ""; + + this.updateFromHSV = function(){ + this.h = 1 - $("#hue_picker").position().top / $("#hue").height(); + this.s = ($("#sb_picker").position().left + 8) / $("#saturation").width(); + this.v = 1 - ($("#sb_picker").position().top + 8) / $("#value").height(); + + 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.updateFromRGB = function(){ + this.r = $($(".color_picker_rgb")[0]).val(); + this.g = $($(".color_picker_rgb")[1]).val(); + this.b = $($(".color_picker_rgb")[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 (isValidHEX($(input).val())) 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(){ + var o = Math.round((this.r * 299 + (this.g * 587) + (this.b * 114)) / 1000); //https://www.nbdtech.com/Blog/archive/2008/04/27/Calculating-the-Perceived-Brightness-of-a-Color.aspx + this.fontColor = (o > 125) ? "#000" : "#FFF"; + + $($(".color_picker_rgb")[0]).val(this.r); + $($(".color_picker_rgb")[1]).val(this.g); + $($(".color_picker_rgb")[2]).val(this.b); + + $("#color_picker_hex").val(this.hex); + + $("#hue_picker").css("top", (1 - this.h) * $("#hue").height()); + $("#sb_picker").css({ + "left": this.s * $("#saturation").width() - 8, + "top": (1 - this.v) * $("#value").height() - 8 + }); + + var sRGB = HSVtoRGB(this.h, 1, 1); + var saturationBackground = "linear-gradient(to right, #FFF 0%, rgb(" + + sRGB.r + "," + + sRGB.g + "," + + sRGB.b + ") 100%)"; + + $("#saturation").css("background", saturationBackground); + + /*$($(".color_picker_rgb")[0]).css({ + "background-color": "rgb(" + this.r + ", 0, 0)" + }); + $($(".color_picker_rgb")[1]).css({ + "background-color": "rgb(0, " + this.g + ", 0)" + }); + $($(".color_picker_rgb")[2]).css({ + "background-color": "rgb(0, 0, " + this.b + ")" + });*/ + //Fancy but overloaded + + $("#color_picker_hex").css({ + "background-color": this.hex, + "color": this.fontColor + }); + } + + this.mousePressed = function(){ + var x = winMouseX - $("#saturation").offset().left; + var 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"){ + var objH = $("#hue"); + var picker = $("#hue_picker"); + var 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"){ + var objS = $("#saturation"); + var objV = $("#value"); + var picker = $("#sb_picker"); + var s = winMouseX - objS.offset().left; + var 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.updateFromHSV(); + } + + this.mouseReleased = function(){ + this.movingObject = ""; + } + + this.getColor = function(){ + return this.hex; + } +} + +//www.stackoverflow.com --> +function RGBtoHEX(r, g, b) { + var rgb = b | (g << 8) | (r << 16); + return '#' + (0x1000000 + rgb).toString(16).slice(1); +} + +function HEXtoRGB(hex) { + var 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; + }); + var 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) { + var r, g, b, i, f, p, q, t; + if (arguments.length === 1) { + s = h.s, v = h.v, h = h.h; + } + 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: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255) + }; +} + +function RGBtoHSV(r, g, b) { + if (arguments.length === 1) { + g = r.g, b = r.b, r = r.r; + } + var 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, + s: s, + v: v + }; +} + +function isValidHEX(hex){ + return /(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(hex); +} \ No newline at end of file diff --git a/public/scripts/Graph.js b/public/scripts/Graph.js new file mode 100644 index 0000000..caf785b --- /dev/null +++ b/public/scripts/Graph.js @@ -0,0 +1,59 @@ +function Graph(fullDescription) { + var d = fullDescription; + + this.name = d.substring(0, d.indexOf("(")); + + this.reference = d.charAt(d.indexOf("(") + 1); + + this.displayFunction = d.substring(d.indexOf("=") + 1, d.length); + + + this.color = "#000000"; + this.thickness = 2; + this.points = []; + this.visible = true; + + this.getFullDescription = function(){ + return (this.name + "(" + this.reference + ") = " + this.displayFunction).replace(/\s+/g, " "); + } + + this.getDisplayFunction = function(){return this.displayFunction;} + this.setDisplayFunction = function(displayFunction){ + this.displayFunction = displayFunction.replace(/\s+/g, " "); + this.convertToExecuteableFunction(); + } + + this.getExecuteFunction = function(){return this.executeFunction;} + + this.getReference = function(){return this.reference;} + + this.getName = function(){return this.name;} + + this.getColor = function(){return this.color;} + this.setColor = function(color){this.color = color;} + + this.getThickness = function(){return this.thickness;} + this.setThickness = function(thickness){this.thickness = thickness;} + + this.getPoints = function(){return this.points;} + this.setPoints = function(points){this.points = points;} + + this.convertToExecuteableFunction = function(){ + var f = this.displayFunction; + f = f.substr(0, f.length); + f = f.replace(/,/g, "."); + f = f.replace(/(\d+\.?\d*)\s*([a-z])/g, "$1*$2"); + f = f.replace(/([a-z])\s*(\d+\.?\d*)/g, "$2*$1"); + f = f.replace(/\-([a-z])/g, "-1*$1"); + f = f.replace(new RegExp("([a-z])" + this.reference, "g"), "$1*" + this.reference); + f = f.replace(new RegExp(this.reference + "([a-z])", "g"), "$1*" + this.reference); + variables.forEach(function(v){f = f.replace(new RegExp(v.getName(), "g"), v.getValue());}); + var powCount = f.split("^").length - 1; + for (var i = 0; i < powCount; i++){f = f.replace(/(\(.*(\(.*\))*.*\)|[a-z]?(\d+\.?\d*)?)\^(\(.*(\(.*\))*.*\)|[a-z]?(\d+\.?\d*)?)/, "pow($1, $4)");} + f = f.replace(/\|(.+)\|/g, "abs($1)"); + f = f.replace(/e/g, "Math.E"); + //console.log(f); + this.executeFunction = f; + } + this.convertToExecuteableFunction(); +} \ No newline at end of file diff --git a/public/scripts/ObjectStore.js b/public/scripts/ObjectStore.js new file mode 100644 index 0000000..b9c89b0 --- /dev/null +++ b/public/scripts/ObjectStore.js @@ -0,0 +1,38 @@ + + +function restoreSettings(){ + for (var i = 0; i < Infinity; i++){ + if (getItem("fullDescription" + i) == null) break; + var g = new Graph(getItem("fullDescription" + i)); + g.name = getItem("name" + i); + g.reference = getItem("reference" + i); + g.displayFunction = getItem("displayFunction" + i); + g.executeFunction = getItem("executeFunction" + i); + g.color = getItem("color" + i); + g.thickness = int(getItem("thickness" + i)); + g.visible = boolean(getItem("visible" + i)); + graphs.push(g); + } +} + +function safeSettings(){ + +} + +function restoreStandard(){ + if (graphs.length > 0){return;} + function restore(f, c, i){ + var g = new Graph(f); + g.setColor(c); + graphs[i] = g; + } + restore("f(x)=x^3-2x", "#ffe602", 0); + restore("g(x)=(1 - x^2)^(1/2)", "#ff0202", 1); + restore("h(x)=e^(-x)", "#000000", 2); + restore("i(x)= 1/(x^2)", "#31ff04", 3); + restore("j(x)= |x| * x", "#0f02ff", 4); + restore("k(x)=log(x)", "#ff0be2", 5); + restore("l(x)= sin(2x)", "#02ffc6", 6); + restore("m(x)=cos(0.5x)", "#ff8d03", 7); + restore("n(x)=atan(x)", "#01cfff", 8); +} \ No newline at end of file diff --git a/public/scripts/Variable.js b/public/scripts/Variable.js new file mode 100644 index 0000000..2565d78 --- /dev/null +++ b/public/scripts/Variable.js @@ -0,0 +1,17 @@ +function Variable(name, value){ + this.name = name; + this.value = value; + this.slider = new Slider(this); + + this.getName = function(){return this.name;} + this.setName = function(name){this.name = name;} + + this.getValue = function(){return this.value;} + this.setValue = function(value_obj, bool){ + + } +} + +function Slider(variable){ + this.variable = variable; +} \ No newline at end of file diff --git a/public/scripts/lib/BenjoLibrary.js b/public/scripts/lib/BenjoLibrary.js new file mode 100644 index 0000000..aaf89e0 --- /dev/null +++ b/public/scripts/lib/BenjoLibrary.js @@ -0,0 +1,357 @@ +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 (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.radius; + + // Ball Border Y inverted because Y = 0 is the TOP of the screen + var bby = b.y - cos(i) * b.radius; + + // 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.radius > b2.x - b2.radius + && b1.x - b1.radius < b2.x + b2.radius + && b1.y + b1.radius > b2.y - b2.radius + && b1.y - b1.radius < b2.y + b2.radius){ + + var distance = sqrt(pow(b1.x - b2.x, 2) + pow(b1.y - b2.y, 2)); + + if (distance < b1.radius + b2.radius) 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.radius; + b2.mass = b2.radius; + + //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]); +} \ No newline at end of file diff --git a/public/scripts/sketch.js b/public/scripts/sketch.js new file mode 100644 index 0000000..8b62f93 --- /dev/null +++ b/public/scripts/sketch.js @@ -0,0 +1,319 @@ +var graphs, + variables, + currentEditor, + maxSquareCount, + minSquareCount, + padding, + paddingFactor, + tickFactor, + textHeight, + paddingDivider, + scrollFactor, + squareCount, + axisDispo, + axisDispoPI, + origin, + distance, + valid, + editing; + + +function setup(){ + frameRate(0); + assignGlobalVariables(); + restoreSettings(); + //restoreStandard(); + prepareVisibilites(); + updateAll(); + updateScroll(); +} + +function assignGlobalVariables(){ + graphs = [], + variables = [], + currentEditor = null, + maxSquareCount = {x: 20, y: 15}, + minSquareCount = {x: 15, y: 10}, + squareCount = {x: 0, y: 0}, + padding = {x: 50, y: 50}, + paddingFactor = {x: 1, y: 1}, + paddingDivider = {x: 0, y: 0}, + axisDispoPI = {x: false, y: false}, + axisDispo = 2, + tickFactor = 1, + origin = {x: 0, y: 0}, + distance = {x: 0, y: 0}, + textHeight = 15; +} + + +function addGraph(){ + var newGraph = new Graph($("#input").val()); + var same = false; + if (newGraph.getName().length == 0) return; + graphs.forEach(function(g){ + if (g.getName() == newGraph.getName()){ + newGraph.setColor(g.getColor()); + newGraph.setThickness(g.getThickness()); + graphs[graphs.indexOf(g)] = newGraph; + same = true; + } + }); + if (!same){ + graphs.push(newGraph); + $("#input").val(newGraph.getFullDescription()); + } + updateAll(); +} + + +function updateAll(){ + updateInterface(); + updateDisplay(); + safeSettings(); +} + +function updateDisplay(){ + prepareGridValues(); + drawGrid(); + drawGraphs(); +} + +function updateInterface(){ + $("#functions").html(""); + graphs.forEach(function(g){ + var bc = g.getColor(); + var c = g.visible ? g.getColor() : "none"; + $("#functions").append("
"); + }); +} + +function prepareGridValues(){ + squareCount.x = width / padding.x; + squareCount.y = height / padding.y; + while (squareCount.x > maxSquareCount.x){ + squareCount.x = round(squareCount.x / axisDispo); + paddingFactor.x *= axisDispo; + padding.x *= axisDispo; + } + while (squareCount.y > maxSquareCount.y){ + squareCount.y = round(squareCount.y / axisDispo); + paddingFactor.y *= axisDispo; + padding.y *= axisDispo; + } + while (squareCount.x < minSquareCount.x){ + squareCount.x *= axisDispo; + paddingFactor.x /= axisDispo; + padding.x = round(padding.x / axisDispo); + } + while (squareCount.y < minSquareCount.y){ + squareCount.y *= axisDispo; + paddingFactor.y /= axisDispo; + padding.y = round(padding.y / axisDispo); + } + squareCount.x = round(squareCount.x); + squareCount.y = round(squareCount.y); + paddingDivider.x = padding.x / paddingFactor.x; + paddingDivider.y = padding.y / paddingFactor.y; +} + +function drawGrid(){ + clear(); + + stroke("#000"); + strokeWeight(2); + + line(0, origin.y, width, origin.y); + line(origin.x, height, origin.x, 0); + + strokeWeight(0.25); + + var centerX = (origin.x - width / 2) % padding.x + width / 2; + var centerY = (origin.y - height / 2) % padding.y + height / 2; + var x, y, numX, numY, textWidthX, textWidthY, i; + + for (i = 0; i < squareCount.x * 2; i++){ + x = centerX - squareCount.x * padding.x + padding.x * i; + if (axisDispoPI.x) numX = round((x - origin.x) / padding.x * paddingFactor.x / PI * 100) / 100 + "π"; + if (!axisDispoPI.x) numX = round((x - origin.x) / padding.x) * paddingFactor.x; + if (numX == 0 || numX == "0π") numX = ""; + line(x, 0, x, height); + textWidthX = textWidth(str(numX)); + text(numX, x - textWidthX / 2, origin.y + textHeight / 2, textWidthX, textHeight); + } + for (i = 0; i < squareCount.y * 2; i++){ + y = centerY - squareCount.y * padding.y + padding.y * i; + if (axisDispoPI.y) numY = -round((y - origin.y) / padding.y * paddingFactor.y / PI * 100) / 100 + "π"; + if (!axisDispoPI.y) numY = -round((y - origin.y) / padding.y) * paddingFactor.y; + if (numY == 0 || numY == "0π") numY = ""; + line(0, y, width, y); + textWidthY = textWidth(str(numY)); + text(numY, origin.x - textWidthY - textHeight / 2, y - textHeight / 2, textWidthY, textHeight); + } +} + +function drawGraphs(){ + graphs.forEach(function(g){ + if (!g.visible) return; + var rdyFunc = g.getExecuteFunction(); + var r = g.getReference(); + function f(value){ + var y; + var execFunc = rdyFunc.replace(new RegExp(r, "g"), value); + try{y = eval(execFunc);} catch(e){y = null;} + return y; + } + var pointsRange = [], x, y; + for (var px = 0; px <= width; px += tickFactor){ + x = (px - origin.x) / paddingDivider.x; + y = f(x); + pointsRange[px / tickFactor] = {x: x, y: y}; + } + g.setPoints(pointsRange); + }); + graphs.forEach(function(g){ + if (!g.visible) return; + var t = g.getThickness(), x, y; + stroke(g.getColor()); + strokeWeight(t); + + + g.getPoints().forEach(function(p){ + x = origin.x + p.x * paddingDivider.x; + y = origin.y - p.y * paddingDivider.y; + if (x != null && y != null) line(x, y, x + t / 4, y); + }); + + //Straight lines instead of points: + + /*for (var i = 0; i < g.getPoints().length; i++){ + p = g.getPoints()[i]; + x1 = origin.x + p.x * paddingDivider.x; + y1 = origin.y - p.y * paddingDivider.y; + try{ + x2 = origin.x + g.getPoints()[i + 1].x * paddingDivider.x; + y2 = origin.y - g.getPoints()[i + 1].y * paddingDivider.y; + line(x1, y1, x2, y2); + }catch(e){} + }*/ + + }); +} + + + +function editGraph(obj){ + var i = $(obj).parent().parent().index(); + obj.blur(); + currentEditor = new Editor(obj, i); +} + +function deleteGraph(obj){ + var i = $(obj).parent().parent().index(); + obj.blur(); + graphs.splice(i, 1); + updateAll(); +} + +function toggleGraph(obj){ + var i = $(obj).parent().parent().index(); + obj.blur(); + graphs[i].visible = !graphs[i].visible; + updateAll(); +} + + + +function mouseWheel(event){ + if (mouseX > 0 && mouseY < height && !editing){ + var oldPaddingX = padding.x; + var oldPaddingY = padding.y; + var oldPaddingFactorX = paddingFactor.x; + var oldPaddingFactorY = paddingFactor.y; + padding.x *= (scrollFactor - (event.delta > 0 ? 3 : -3)) / scrollFactor; + padding.y *= (scrollFactor - (event.delta > 0 ? 3 : -3)) / scrollFactor; + padding.x = round(padding.x); + padding.y = round(padding.y); + + prepareGridValues(); + + origin.x = -(mouseX - origin.x) / (oldPaddingX / oldPaddingFactorX) * (padding.x / paddingFactor.x) + mouseX; + origin.y = -(mouseY - origin.y) / (oldPaddingY / oldPaddingFactorY) * (padding.y / paddingFactor.y) + mouseY; + + updateDisplay(); + } +} + +function updateScroll(){ + scrollFactor = round((500 - $("#scroll_input").val() * 0.95) / 5); +} + +function updateDisposition(obj, xAxis){ + obj.blur(); + var dispo = $(obj).val(); + if (xAxis){ + if (dispo == "PI" && !axisDispoPI.x){ + paddingFactor.x *= PI; + padding.x *= PI; + axisDispoPI.x = true; + } + if (dispo == "1" && axisDispoPI.x){ + paddingFactor.x /= PI; + padding.x /= PI; + axisDispoPI.x = false; + } + } + if (!xAxis){ + if (dispo == "PI" && !axisDispoPI.y){ + paddingFactor.y *= PI; + padding.y *= PI; + axisDispoPI.y = true; + } + if (dispo == "1" && axisDispoPI.y){ + paddingFactor.y /= PI; + padding.y /= PI; + axisDispoPI.y = false; + } + } + updateAll(); +} + +function mouseDragged(){ + if (editing){currentEditor.colorPicker.mouseDragged();} + else if (valid){ + origin.x = round(mouseX - distance.x); + origin.y = round(mouseY - distance.y); + updateDisplay(); + } +} + +function mousePressed(){ + if (editing){currentEditor.colorPicker.mousePressed();} + else if (mouseX > 0 && mouseY < height){ + valid = true; + distance.x = mouseX - origin.x; + distance.y = mouseY - origin.y; + $("#defaultCanvas0").css("cursor", "all-scroll"); + } else {valid = false;} +} + +function mouseReleased(){ + if (editing){currentEditor.colorPicker.mouseReleased();} + $("#defaultCanvas0").css("cursor", "auto"); +} + + +function prepareVisibilites(){ + if (!editing) $("#graph_editor_wrapper").hide(); + $("#canvas_holder").css("height", windowHeight - $("#input_holder").outerHeight() + "px"); + var canvas = createCanvas(windowWidth - $("#displayer").outerWidth(), windowHeight - $("#input_holder").outerHeight()); + canvas.parent("canvas_holder"); + $(".holder:not(#settings_holder)").css("height", windowHeight * 0.7 * 0.5 + "px"); + $("#settings_holder").css("height", $("#displayer").outerHeight() - $("#function_holder").outerHeight() - $("#variable_holder").outerHeight() - 80 + "px"); + origin.x = width / 2; + origin.y = height / 2; + textFont("Arial", textHeight); + $("body").css("background-color", "#FFF"); +} diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..aff04db --- /dev/null +++ b/public/styles.css @@ -0,0 +1,147 @@ +a:link, a:hover, a:active, a:visited{color: #000;} + +body, canvas{margin: 0; padding: 0;} + +*:not(#input, #graph_editor_input){ + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#functions td:nth-child(1){ + width: 5%; +} +#functions td:nth-child(1) div{ + width: 20px; + height: 20px; + border-width: 3px; + border-style: solid; + border-radius: 50%; +} +#functions td:nth-child(3){ + width: 5%; +} +#functions td:nth-child(3) button{ + width: 100%; + background-color: #F00; + border: 2px solid #F00; + border-radius: 2px; +} + +#input_holder{ + position: absolute; + display: block; + bottom: 0; + right: 0; + padding: 5px 0.5% 5px 0.5%; + width: 79%; + height: 30px; + background-color: #777; +} +#input{ + width: 95%; + height: 30px; + font-size: 18px; +} +#submit{ + width: 4%; + height: 30px; + margin: 0; + padding: 0; +} + +#displayer{ + position: absolute; + display: block; + background-color: #999; + padding: 0 1% 0 1%; + width: 18%; + height: 100%; + border-right: 5px solid #000; +} + +#canvas_holder{ + position: absolute; + left: 20%; + width: 80%; +} + +.displayer{ + width: 100%; +} + +.holder{ + border: 5px solid #000; + background-color: #777; + overflow-y: auto; +} +#variable_holder{ + margin-top: 20px; + margin-bottom: 20px; +} +#settings_holder{ + text-align: left; +} +#settings_holder span{ + margin: 10px; +} +legend{ + font-size: 30px; +} + +#scroll_input{ + width: calc(100% - 20px); + margin: 10px; +} + +.displayer button{ + text-align: left; + font-size: 20px; + width: 100%; + padding: 5px; + cursor: pointer; +} + +#graph_editor_wrapper{ + position: absolute; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.75); +} + +#graph_editor{ + position: absolute; + width: 400px; + min-height: 400px; + background-color: #BBB; + border-radius: 10px; + border: 4px solid #000; + box-shadow: 10px 10px 10px #000; +} + +#graph_editor_thickness{ + width: 20%; + height: 25px; + margin-left: 5%; + font-size: 20px; +} + +#graph_editor_input{ + width: 90%; + height: 30px; + margin: 5%; + font-size: 23px; +} + +#graph_editor_done{ + width: 80px; + height: 40px; + font-size: 25px; + margin: 20px; + background-color: #99FF99; + float: right; + border: 3px solid #99FF99; + border-radius: 5px; + box-shadow: 2px 2px 5px #000; +} \ No newline at end of file diff --git a/public/thumbnail.png b/public/thumbnail.png new file mode 100644 index 0000000..9ca2d0a Binary files /dev/null and b/public/thumbnail.png differ