initial commit
This commit is contained in:
parent
1341645fa3
commit
a347aca0b8
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
SVGS/**
|
||||||
197
fourier.js
Normal file
197
fourier.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
var cycles;
|
||||||
|
|
||||||
|
onmessage = function(e) {
|
||||||
|
var eq = [];
|
||||||
|
var paths = e.data[0];
|
||||||
|
cycles = e.data[1];
|
||||||
|
for(var i = 0; i < paths.length; i++) {
|
||||||
|
eq.push(calculate(paths[i], i, paths.length))
|
||||||
|
}
|
||||||
|
postMessage(eq);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculate(ft, pathNum, pathTotal) {
|
||||||
|
var bottom = -Math.floor(cycles/2);
|
||||||
|
var top = Math.ceil(cycles/2);
|
||||||
|
// These two objects store Ck values for the epicycloids.
|
||||||
|
var reC = {};
|
||||||
|
var imC = {};
|
||||||
|
var piMult = 2*Math.PI/(ft.length);
|
||||||
|
var eachInterval = [];
|
||||||
|
var tInterval = [];
|
||||||
|
var zeroConstants = [];
|
||||||
|
|
||||||
|
for(var i = 0; i < ft.length; i++) { // Precalculate equations
|
||||||
|
var t = [piMult*i, piMult*(i+1)];
|
||||||
|
var pt = ft[i];
|
||||||
|
var ts = 1/(piMult);
|
||||||
|
var shift = poly(new Array(pt.length-1).fill([]));
|
||||||
|
var vec = poly(new Array(pt.length).fill([]));
|
||||||
|
|
||||||
|
for(var j = 0; j < pt.length; j++) vec[0] = vec[0].concat(poly([pt[j]]));
|
||||||
|
for(var j = 0; j < pt.length-1; j++) {
|
||||||
|
for(var k = 0; k < pt.length-1-j; k++) {
|
||||||
|
shift[j] = shift[j].concat(polyOp(-ts*t[0], "*", polyOp(vec[j][k+1], "-", vec[j][k])));
|
||||||
|
vec[j+1] = vec[j+1].concat(polyOp(polyOp(vec[j][k], "+", shift[j][k]), "+", polyOp(ts, "*", polyOp(vec[j][k+1], "-", vec[j][k]), 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var finalVec = Object.values(vec[pt.length-1][0]);
|
||||||
|
eachInterval.push(integralConstants(finalVec, t)); // Precalculate constants for integral.
|
||||||
|
zeroConstants.push(integralZero(finalVec, t));
|
||||||
|
tInterval.push(t);
|
||||||
|
}
|
||||||
|
for(var k = bottom; k <= top; k++) { // For every epicycloid
|
||||||
|
var reCk = 0;
|
||||||
|
var imCk = 0;
|
||||||
|
|
||||||
|
for(var i = 0; i < ft.length; i++) { // Carry out integral for finding Ck
|
||||||
|
var integralPart = (k === 0) ? zeroConstants[i] : integrate(eachInterval[i][0], eachInterval[i][1], tInterval[i], k);
|
||||||
|
reCk += integralPart[0];
|
||||||
|
imCk += integralPart[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
reC[k] = Rnd(reCk/(2*Math.PI),5);
|
||||||
|
imC[k] = Rnd(imCk/(2*Math.PI),5);
|
||||||
|
postMessage(.9*((pathNum + (k + (cycles)/2+1)/(cycles+1))/pathTotal));
|
||||||
|
}
|
||||||
|
return [reC, imC];
|
||||||
|
}
|
||||||
|
|
||||||
|
function integralZero(shearConsts, t) {
|
||||||
|
var val = Object.values(shearConsts);
|
||||||
|
var f = polyOp(poly(val.map((a,i)=>arrOp(a, "/" ,i+1))), "+", poly([0]), 1);
|
||||||
|
return arrOp(polySub(f, t[1]), "-",polySub(f, t[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function integralConstants(shearConsts, t) {
|
||||||
|
var n = shearConsts.length; // One more than actual order
|
||||||
|
/* Since there are different constants for the real and imaginary polynomials,
|
||||||
|
the integrals are now separated. The integral is calculated separately, with the real
|
||||||
|
and imaginary portions calculated at the same time, but using the different respective
|
||||||
|
constants. The real and imaginary portions that resolve after the integration
|
||||||
|
is taken into consideration later.
|
||||||
|
*/
|
||||||
|
var integralRe = new Array(n).fill([]);
|
||||||
|
var integralIm = new Array(n).fill([]);
|
||||||
|
for(var i = 0; i < n; i++) {
|
||||||
|
for(var j = 0; j < n-i; j++) {
|
||||||
|
if(i === 0) {
|
||||||
|
integralRe[i] = integralRe[i].concat([shearConsts[j][0]]);
|
||||||
|
integralIm[i] = integralIm[i].concat([shearConsts[j][1]]);
|
||||||
|
} else {
|
||||||
|
integralRe[i] = integralRe[i].concat([integralRe[i-1][j+1]*(j+1)]);
|
||||||
|
integralIm[i] = integralIm[i].concat([integralIm[i-1][j+1]*(j+1)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
integralRe[i] = poly(integralRe[i]);
|
||||||
|
integralIm[i] = poly(integralIm[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullPolyRe = [{}, {}]; // Real expanded constants
|
||||||
|
var fullPolyIm = [{}, {}]; // Imaginary expanded constants
|
||||||
|
for(var i = 0; i < n; i++) {
|
||||||
|
var neg = (i%4===2 || i%4===3) ? -1 : 1;
|
||||||
|
integralRe[i] = [polySub(integralRe[i], t[0]), polySub(integralRe[i], t[1])];
|
||||||
|
integralIm[i] = [polySub(integralIm[i], t[0]), polySub(integralIm[i], t[1])];
|
||||||
|
//console.log([integralRe, integralIm])
|
||||||
|
integralRe[i] = polyOp(poly([integralRe[i]]), "*", neg, -i-1);
|
||||||
|
integralIm[i] = polyOp(poly([integralIm[i]]), "*", neg, -i-1);
|
||||||
|
if(i%2===0) {
|
||||||
|
fullPolyIm[0] = Object.assign(fullPolyIm[0], integralRe[i]);
|
||||||
|
fullPolyRe[1] = Object.assign(fullPolyRe[1], integralIm[i]);
|
||||||
|
} else {
|
||||||
|
fullPolyRe[0] = Object.assign(fullPolyRe[0], integralRe[i]);
|
||||||
|
fullPolyIm[1] = Object.assign(fullPolyIm[1], integralIm[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [fullPolyRe, fullPolyIm];
|
||||||
|
}
|
||||||
|
|
||||||
|
function integrate(kRe, kIm, t, k) {
|
||||||
|
var c = [
|
||||||
|
polySub(kRe[0], k),
|
||||||
|
polySub(kIm[0], k),
|
||||||
|
arrOp(-1,"*",polySub(kRe[1], k)),
|
||||||
|
polySub(kIm[1], k),
|
||||||
|
[Math.sin(k*t[0]), Math.sin(k*t[1])],
|
||||||
|
[Math.cos(k*t[0]), Math.cos(k*t[1])]
|
||||||
|
];
|
||||||
|
c = c[0].map((a,i)=>c.map(b=>b[i]));
|
||||||
|
|
||||||
|
function reCki(c) {
|
||||||
|
return c[4]*(c[1]+c[3]) + c[5]*(c[0]+c[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function imCki(c) {
|
||||||
|
return c[5]*(c[1]+c[3]) - c[4]*(c[0]+c[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [(reCki(c[1]) - reCki(c[0])), (imCki(c[1]) - imCki(c[0]))];
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrOp(arr1, op, arr2) { // Applies operator arr1n (op) arr2n thus not following normal vector rules.
|
||||||
|
if(arr1.length === undefined) arr1 = new Array(arr2.length).fill(arr1);
|
||||||
|
if(arr2.length === undefined) arr2 = new Array(arr1.length).fill(arr2);
|
||||||
|
return arr1.map((a,i)=>eval(a+op+"("+arr2[i]+")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function poly(list) {
|
||||||
|
var obj = {};
|
||||||
|
for(var i = 0; i < list.length; i++) obj[i] = list[i];
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function polyOp(poly1, op, poly2, pow) {
|
||||||
|
pow = pow || 0;
|
||||||
|
var obj = {};
|
||||||
|
var which = poly1;
|
||||||
|
if(Object.keys(poly1).length === 0) {
|
||||||
|
poly1 = poly(new Array(Object.keys(poly2).length).fill(poly1));
|
||||||
|
which = poly2;
|
||||||
|
}
|
||||||
|
if(Object.keys(poly2).length === 0) {
|
||||||
|
poly2 = poly(new Array(Object.keys(poly1).length).fill(poly2));
|
||||||
|
which = poly1;
|
||||||
|
}
|
||||||
|
var greater = poly1;
|
||||||
|
if(Object.keys(poly2).length > Object.keys(poly1).length) greater = poly2;
|
||||||
|
Object.keys(greater).map(function(a) {
|
||||||
|
if((poly1[a] || []).length > 1 || (poly2[a] || []).length > 1) {
|
||||||
|
obj[parseInt(a)+pow] = arrOp((poly1[a] || 0), op, (poly2[a] || 0));
|
||||||
|
for(var i = 0; i < pow; i++) obj[i] = new Array(which[a].length).fill(0);
|
||||||
|
} else {
|
||||||
|
obj[parseInt(a)+pow] = eval((poly1[a] || 0) + op + "("+(poly2[a] || 0)+")");
|
||||||
|
for(var i = 0; i < pow; i++) obj[i] = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function polySub(poly, x) {
|
||||||
|
var key = Object.keys(poly);
|
||||||
|
var val = Object.values(poly).map((a,i)=>arrOp(a,"*",Math.pow(x,parseInt(key[i])))).reduce((a,b)=>arrOp(a,"+",b))
|
||||||
|
return (val.length === 1) ? val[0] : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function varType(variable) {
|
||||||
|
var type = typeof variable;
|
||||||
|
if(type === "object") {
|
||||||
|
return (variable.constructor === Array) ? "Array" : "Object";
|
||||||
|
} else {
|
||||||
|
return type[0].toUpperCase() + type.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rnd(item,fig) {
|
||||||
|
if(varType(item) === "Array") {
|
||||||
|
var arr = [];
|
||||||
|
for(var i = 0; i < item.length; i++) {
|
||||||
|
arr[i] = Rnd(item[i],fig);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
} else if(varType(item) === "Number") {
|
||||||
|
return Math.round(item*Math.pow(10,fig))/Math.pow(10,fig);
|
||||||
|
} else {
|
||||||
|
throw new TypeError("Expected Integers, got " + varType(item) + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
34
grapher.js
Normal file
34
grapher.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
onmessage = function(e) {
|
||||||
|
var calcArray = [];
|
||||||
|
var eq = e.data;
|
||||||
|
for(var i = 0; i < eq.length; i++) {
|
||||||
|
calcArray = calcEquation(eq[i]);
|
||||||
|
postMessage(.9+.1*((i+1)/eq.length));
|
||||||
|
postMessage(calcArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcEquation(cycleArray) {
|
||||||
|
var step = 1000;
|
||||||
|
/* x(t) and y(t) are in form
|
||||||
|
reCk * cos(kt) - imCk * sin(kt)
|
||||||
|
reCk * sin(kt) + imCk * cos(kt)
|
||||||
|
respectively.
|
||||||
|
*/
|
||||||
|
var keys = Object.keys(cycleArray[0]);
|
||||||
|
var calcArray = [];
|
||||||
|
calcArray.push(new Array(step).fill([cycleArray[0][0], cycleArray[1][0]]));
|
||||||
|
for(var i = 0; i < keys.length-1; i++) {
|
||||||
|
var num = ((i+1)%2===1) ? Math.ceil((i+1)/2) : -Math.floor((i+1)/2);
|
||||||
|
var cycloidPoints = [];
|
||||||
|
for(var j = 0; j < step; j++) {
|
||||||
|
var cycNum = num*2*Math.PI*j/step;
|
||||||
|
cycloidPoints.push([
|
||||||
|
cycleArray[0][num]*Math.cos(cycNum) - cycleArray[1][num]*Math.sin(cycNum),
|
||||||
|
cycleArray[0][num]*Math.sin(cycNum) + cycleArray[1][num]*Math.cos(cycNum)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
calcArray.push(calcArray[i].map((a,j)=>[a[0]+cycloidPoints[j][0], a[1]+cycloidPoints[j][1]]));
|
||||||
|
}
|
||||||
|
return calcArray;
|
||||||
|
}
|
||||||
326
main.css
Normal file
326
main.css
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
html, body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 25% 75%;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.transition {
|
||||||
|
-webkit-transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
-moz-transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
-ms-transition: all 0.3s cubic-bezier(.25, .8, .25, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar {
|
||||||
|
color: white;
|
||||||
|
background-color: #00897B;
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: 10% 8% 74% 8%;
|
||||||
|
box-shadow: 1px 0px 4px 2px #2c2c2c;
|
||||||
|
z-index: 10;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sideTitle {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar h1 {
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: underline;
|
||||||
|
text-decoration-style: dotted;
|
||||||
|
margin: auto 0 auto 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graph {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#doGraph {
|
||||||
|
background-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#doGraph:hover {
|
||||||
|
background-color: rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvas {
|
||||||
|
background-color: #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setCont {
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2% 18% 40% 40%;
|
||||||
|
grid-template-rows: 55% 45%;
|
||||||
|
height: 9vh;
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set:first-child {
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set:hover {
|
||||||
|
background-color: rgba(0,0,0,0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1/3;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: 5% 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color h2 {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: 100;
|
||||||
|
margin: 0 auto 0 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color > div {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1/3;
|
||||||
|
border-radius: 50px;
|
||||||
|
height: 4vh;
|
||||||
|
width: 4vh;
|
||||||
|
margin: auto;
|
||||||
|
box-shadow: inset 0 0 0 0 rgba(0,0,0,0);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color > div:hover {
|
||||||
|
box-shadow: inset 50px 0 rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.colSel {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
background-color:rgba(255,255,255,0.9);
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-template-rows: repeat(4, 1fr);
|
||||||
|
grid-gap: 1vh;
|
||||||
|
width: 500%;
|
||||||
|
padding: 20%;
|
||||||
|
border-radius: 5px;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colSel div {
|
||||||
|
border-radius: 50px;
|
||||||
|
height: 4vh;
|
||||||
|
width: 4vh;
|
||||||
|
margin: auto;
|
||||||
|
box-shadow: inset 0 0 0 0 rgba(0,0,0,0);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colSel div:hover {
|
||||||
|
box-shadow: inset 50px 0 rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1/3;
|
||||||
|
background-color: #9CCC65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-weight: 200;
|
||||||
|
margin-top: auto;
|
||||||
|
grid-column: 3/5;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms {
|
||||||
|
font-weight: 100;
|
||||||
|
margin: auto 0 auto 0;
|
||||||
|
grid-column: 3;
|
||||||
|
grid-row: 2;
|
||||||
|
color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eqs {
|
||||||
|
font-weight: 100;
|
||||||
|
margin: auto 0 auto 0;
|
||||||
|
grid-column: 4;
|
||||||
|
grid-row: 2;
|
||||||
|
color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ondrag {
|
||||||
|
animation: ondrag 1s ease-in-out 0s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ondrag {
|
||||||
|
from {background-color: rgba(255,255,255,0);}
|
||||||
|
to {background-color: rgba(255,255,255,0.3);}
|
||||||
|
}
|
||||||
|
|
||||||
|
#clickFile {
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 4;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 80% 20%;
|
||||||
|
grid-template-rows: 1fr;
|
||||||
|
font-size: 120%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clickFile:hover {
|
||||||
|
background-color: rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#clickFile input {
|
||||||
|
height: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clickFile p, #clickFile i {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {transform:rotate(0deg);}
|
||||||
|
to {transform:rotate(360deg);}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin {
|
||||||
|
animation: spin 2.5s linear 0s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
height: 6vh;
|
||||||
|
background-color: rgba(0,0,0,0.05);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 40% 40% 20%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:hover {
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
background-color: rgba(0,0,0,0.15);
|
||||||
|
color: #AEAEAE;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled input {
|
||||||
|
color: #AEAEAE !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled:hover {
|
||||||
|
background-color: rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option p {
|
||||||
|
margin: auto 0 auto 10%;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 110%;
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 3;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option p:first-child {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option input {
|
||||||
|
margin: auto;
|
||||||
|
height: 50%;
|
||||||
|
width: 50%;
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 2;
|
||||||
|
background-color: rgba(0,0,0,0);
|
||||||
|
color: white;
|
||||||
|
border: 0;
|
||||||
|
font-family: 'Poppins',sans-serif;
|
||||||
|
padding: 1%;
|
||||||
|
font-size: 110%;
|
||||||
|
font-weight: 300;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option input:hover {
|
||||||
|
background-color: rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option input:focus {
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number]::-webkit-inner-spin-button,
|
||||||
|
input[type=number]::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mathContRe, #mathContIm {
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 46.9vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mathType p {
|
||||||
|
grid-column: 1/3;
|
||||||
|
margin: auto 0 auto 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mathContRe {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.MJXc-display {
|
||||||
|
font-size: 80% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#copyBox {
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
}
|
||||||
34
main.html
Normal file
34
main.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||||
|
<title>Epicycles</title>
|
||||||
|
<link rel="icon" href="favicon.ico?v=2">
|
||||||
|
<link rel="stylesheet" href="./main.css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Poppins:100,200,400" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>
|
||||||
|
<script src="https://fastcdn.org/FileSaver.js/1.1.20151003/FileSaver.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="sidebar">
|
||||||
|
<div id="sideTitle">
|
||||||
|
<h1>Equation List</h1>
|
||||||
|
</div>
|
||||||
|
<div id="setCont" class="transition" ondrop="fileDrop(event);" ondragover="fileDrag(event);" ondragleave="fileDragLeave(event);">
|
||||||
|
</div>
|
||||||
|
<div id="clickFile" class="transition" onclick="this.children[2].click();">
|
||||||
|
<p>Drag or browse for a file!</p>
|
||||||
|
<i class="fas fa-file-upload"></i>
|
||||||
|
<input type="file">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="graph">
|
||||||
|
<canvas id="canvas">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
<input id="copyBox">
|
||||||
|
<script src="./main.js"></script>
|
||||||
|
</body>
|
||||||
992
main.js
Normal file
992
main.js
Normal file
@ -0,0 +1,992 @@
|
|||||||
|
var canvas = document.getElementById("canvas");
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
var graphColors = [
|
||||||
|
"#E57373","#F06292","#BA68C8","#9575CD","#7986CB",
|
||||||
|
"#64B5F6","#4FC3F7","#4DD0E1","#4DB6AC","#81C784",
|
||||||
|
"#AED581","#CDDC39","#FFEB3B","#FFD54F","#FFB74D",
|
||||||
|
"#FF8A65"
|
||||||
|
];
|
||||||
|
|
||||||
|
var allSets = [];
|
||||||
|
var calcIndex;
|
||||||
|
var loadStart = null;
|
||||||
|
var loadFrame;
|
||||||
|
var percent = 0;
|
||||||
|
|
||||||
|
var f = new FontFace('Poppins', 'url(https://fonts.gstatic.com/s/poppins/v5/pxiEyp8kv8JHgFVrJJbecmNE.woff2)');
|
||||||
|
var fourier = new Worker("fourier.js");
|
||||||
|
var grapher = new Worker("grapher.js");
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var dim = document.getElementById("graph").getBoundingClientRect();
|
||||||
|
canvas.width = dim.width;
|
||||||
|
canvas.height = dim.height;
|
||||||
|
f.load().then(function() {
|
||||||
|
drawGraphBase();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.onclick = function() {
|
||||||
|
var close = document.getElementsByClassName("colSel");
|
||||||
|
for(var i = 0; i < close.length; i++) {
|
||||||
|
var div = close[i];
|
||||||
|
div.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
div.style.display = "none";
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fourier.onmessage = function(e) {
|
||||||
|
var result = e.data;
|
||||||
|
if(result.length === undefined) {
|
||||||
|
percent = result;
|
||||||
|
} else {
|
||||||
|
var thisSet = allSets[calcIndex];
|
||||||
|
thisSet.eq = result;
|
||||||
|
thisSet.calcArray = [];
|
||||||
|
grapher.postMessage(result);
|
||||||
|
var termDiv = document.getElementsByClassName("terms")[0];
|
||||||
|
var eqDiv = document.getElementsByClassName("eqs")[0];
|
||||||
|
var termNum = Object.keys(thisSet.eq[0][0]).length-1;
|
||||||
|
termDiv.textContent = termNum + " term" + ((termNum === 1) ? "" : "s");
|
||||||
|
var eqNum = thisSet.eq.length;
|
||||||
|
eqDiv.textContent = eqNum + " equation" + ((eqNum === 1) ? "" : "s");
|
||||||
|
var disable = ["graphOp", "viewEqOp", "viewFormOp", "exportData"];
|
||||||
|
for(var i = 0; i < disable.length; i++) {
|
||||||
|
var d = document.getElementById(disable[i]);
|
||||||
|
if(d === null) continue;
|
||||||
|
d.className = d.className.replace(" disabled", "");
|
||||||
|
}
|
||||||
|
if(eqNum > 1) {
|
||||||
|
var div = document.getElementById("viewFormOp");
|
||||||
|
div.children[0].textContent = "View equations";
|
||||||
|
div.onclick = function() {
|
||||||
|
viewAllEq(calcIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grapher.onmessage = function(e) {
|
||||||
|
var result = e.data;
|
||||||
|
if(result.length === undefined) {
|
||||||
|
percent = result;
|
||||||
|
} else {
|
||||||
|
var thisSet = allSets[calcIndex];
|
||||||
|
thisSet.calcArray.push(result);
|
||||||
|
if(thisSet.calcArray.length === thisSet.eq.length) {
|
||||||
|
var range = thisSet.range;
|
||||||
|
var ratio = (canvas.height > canvas.width) ? canvas.height/canvas.width : canvas.width/canvas.height;
|
||||||
|
var r = [1.1*ratio*range[0]/2, 1.1*ratio*range[1]/2];
|
||||||
|
var [scale, axes] = drawGraphBase([[-r[0], r[0]],[-r[1], r[1]]]);
|
||||||
|
window.cancelAnimationFrame(loadFrame);
|
||||||
|
for(var i = 0; i < thisSet.eq.length; i++) {
|
||||||
|
drawEquation(thisSet.calcArray[i], thisSet.calcArray[i].length-2, scale, axes);
|
||||||
|
}
|
||||||
|
percent = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll("#clickFile input")[0].onchange = function() {
|
||||||
|
var that = this;
|
||||||
|
loadSVGStatus(true);
|
||||||
|
setTimeout(function() {
|
||||||
|
var file = that.files[0];
|
||||||
|
if(file.name.search(".svg") === -1) {
|
||||||
|
alert("This file is not an svg! Please select another file.");
|
||||||
|
loadSVGStatus(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function() {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.innerHTML = reader.result;
|
||||||
|
svg = Array.from(el.getElementsByTagName("path")).map(a=>a.getAttribute("d").replace(/(\n|\t)/g,"")).reduce((a,b)=>a.concat(b));
|
||||||
|
var [paths, range] = processSVG(svg);
|
||||||
|
var oneSet = {
|
||||||
|
"name": file.name.replace(".svg","").substring(0,15),
|
||||||
|
"color": graphColors[Math.floor(Math.random()*graphColors.length)],
|
||||||
|
"paths": paths,
|
||||||
|
"range": range,
|
||||||
|
"eq": [],
|
||||||
|
"calcArray": [],
|
||||||
|
};
|
||||||
|
allSets.push(oneSet);
|
||||||
|
updateSidebar();
|
||||||
|
}
|
||||||
|
reader.readAsText(file);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileDrop(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById("setCont").className = "";
|
||||||
|
if(!e.dataTransfer.items) return;
|
||||||
|
var diff = allSets.length;
|
||||||
|
for (var i = 0; i < e.dataTransfer.items.length; i++) {
|
||||||
|
if (e.dataTransfer.items[i].kind === 'file') {
|
||||||
|
loadSVGStatus(true);
|
||||||
|
setTimeout(function() {
|
||||||
|
var file = e.dataTransfer.items[i].getAsFile();
|
||||||
|
if(file.name.search(".svg") === -1) {
|
||||||
|
alert("This file is not an svg! Please select another file.");
|
||||||
|
loadSVGStatus(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function() {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.innerHTML = reader.result;
|
||||||
|
svg = Array.from(el.getElementsByTagName("path")).map(a=>a.getAttribute("d").replace(/(\n|\t)/g,"")).reduce((a,b)=>a.concat(b));
|
||||||
|
var [paths, range] = processSVG(svg);
|
||||||
|
var oneSet = {
|
||||||
|
"name": file.name.replace(".svg",""),
|
||||||
|
"color": graphColors[Math.floor(Math.random()*graphColors.length)],
|
||||||
|
"paths": paths,
|
||||||
|
"range": range,
|
||||||
|
"eq": [],
|
||||||
|
"calcArray": []
|
||||||
|
};
|
||||||
|
allSets.push(oneSet);
|
||||||
|
if(allSets.length-diff === e.dataTransfer.items.length) updateSidebar();
|
||||||
|
}
|
||||||
|
reader.readAsText(file);
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.dataTransfer.items.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileDrag(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById("setCont").className = "ondrag";
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileDragLeave(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById("setCont").className = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSVGStatus(processing) {
|
||||||
|
var p = document.querySelectorAll("#clickFile p")[0];
|
||||||
|
var i = document.querySelectorAll("#clickFile i")[0];
|
||||||
|
if(processing) {
|
||||||
|
p.textContent = "Processing...";
|
||||||
|
i.className = "fas fa-circle-notch spin";
|
||||||
|
} else {
|
||||||
|
p.textContent = "Drag or browse for a file!";
|
||||||
|
i.className = "fas fa-file-upload";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSidebar() {
|
||||||
|
loadSVGStatus(false);
|
||||||
|
var setCont = document.getElementById("setCont");
|
||||||
|
setCont.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
while(setCont.children[0]) setCont.removeChild(setCont.children[0]);
|
||||||
|
allSets.forEach(function(a,i) {
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.className = "set transition";
|
||||||
|
div.onclick = function() {
|
||||||
|
let counter = i;
|
||||||
|
setInfo(counter);
|
||||||
|
}
|
||||||
|
var sel = document.createElement("div");
|
||||||
|
sel.className = "selected";
|
||||||
|
var col = document.createElement("div");
|
||||||
|
col.className = "color";
|
||||||
|
var h2 = document.createElement("h2");
|
||||||
|
h2.appendChild(document.createTextNode(i+1));
|
||||||
|
var circ = document.createElement("div");
|
||||||
|
circ.className = "transition";
|
||||||
|
circ.style.backgroundColor = a.color;
|
||||||
|
var colSel = document.createElement("div");
|
||||||
|
colSel.className = "colSel transition";
|
||||||
|
for(var j = 0; j < graphColors.length; j++) {
|
||||||
|
var col1 = document.createElement("div");
|
||||||
|
col1.className = "transition";
|
||||||
|
col1.style.backgroundColor = graphColors[j];
|
||||||
|
col1.onclick = function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
let counter = i;
|
||||||
|
var parent = this.parentNode;
|
||||||
|
var thisSet = allSets[counter];
|
||||||
|
parent.parentNode.style.backgroundColor = this.style.backgroundColor;
|
||||||
|
thisSet.color = this.style.backgroundColor;
|
||||||
|
parent.style.opacity = "0";
|
||||||
|
if(thisSet.calcArray.length > 0 && calcIndex === counter) {
|
||||||
|
var range = thisSet.range;
|
||||||
|
var ratio = (canvas.height > canvas.width) ? canvas.height/canvas.width : canvas.width/canvas.height;
|
||||||
|
var r = [1.1*ratio*range[0]/2, 1.1*ratio*range[1]/2];
|
||||||
|
var [scale, axes] = drawGraphBase([[-r[0], r[0]],[-r[1], r[1]]]);
|
||||||
|
window.cancelAnimationFrame(loadFrame);
|
||||||
|
for(var k = 0; k < thisSet.eq.length; k++) {
|
||||||
|
drawEquation(thisSet.calcArray[k], thisSet.calcArray[k].length-2, scale, axes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
parent.style.display = "none";
|
||||||
|
},300);
|
||||||
|
}
|
||||||
|
colSel.appendChild(col1);
|
||||||
|
}
|
||||||
|
circ.onclick = function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var innerDiv = this.children[0];
|
||||||
|
innerDiv.style.display = "grid";
|
||||||
|
setTimeout(function() {
|
||||||
|
innerDiv.style.opacity = "1";
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
circ.appendChild(colSel);
|
||||||
|
col.appendChild(h2);
|
||||||
|
col.appendChild(circ);
|
||||||
|
var name = document.createElement("h2");
|
||||||
|
name.className = "name";
|
||||||
|
name.appendChild(document.createTextNode(a.name));
|
||||||
|
div.appendChild(sel);
|
||||||
|
div.appendChild(col);
|
||||||
|
div.appendChild(name);
|
||||||
|
var terms = document.createElement("p");
|
||||||
|
terms.className = "terms";
|
||||||
|
var eqs = document.createElement("p");
|
||||||
|
eqs.className = "eqs";
|
||||||
|
if(a.eq.length !== 0) {
|
||||||
|
var termNum = Object.keys(a.eq[0][0]).length-1;
|
||||||
|
terms.appendChild(document.createTextNode(termNum + " term" + ((termNum === 1) ? "" : "s")));
|
||||||
|
var eqNum = a.eq.length;
|
||||||
|
eqs.appendChild(document.createTextNode(eqNum + " equation" + ((eqNum === 1) ? "" : "s")));
|
||||||
|
}
|
||||||
|
div.appendChild(terms);
|
||||||
|
div.appendChild(eqs);
|
||||||
|
setCont.appendChild(div);
|
||||||
|
});
|
||||||
|
setCont.style.opacity = "1";
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInfo(index) {
|
||||||
|
calcIndex = index;
|
||||||
|
var options = [
|
||||||
|
{
|
||||||
|
name: "Calculate",
|
||||||
|
id: "calcOp",
|
||||||
|
input: true,
|
||||||
|
inputRange: [1, 1500],
|
||||||
|
inputDefValue: 70,
|
||||||
|
onclick: function() {
|
||||||
|
var terms = parseInt(this.children[1].value);
|
||||||
|
loadFrame = window.requestAnimationFrame(loading);
|
||||||
|
fourier.postMessage([allSets[index].paths, terms]);
|
||||||
|
var gInp = document.getElementById("graphOp").children[1];
|
||||||
|
gInp.setAttribute("min", "1");
|
||||||
|
gInp.setAttribute("max", terms);
|
||||||
|
gInp.value = terms;
|
||||||
|
this.setAttribute("min", terms);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Graph",
|
||||||
|
id: "graphOp",
|
||||||
|
input: true,
|
||||||
|
inputRange: [1,1500],
|
||||||
|
inputDefValue: 1,
|
||||||
|
disableDef: true,
|
||||||
|
onclick: function() {
|
||||||
|
var thisSet = allSets[index];
|
||||||
|
var range = thisSet.range;
|
||||||
|
var ratio = (canvas.height > canvas.width) ? canvas.height/canvas.width : canvas.width/canvas.height;
|
||||||
|
var r = [1.1*ratio*range[0]/2, 1.1*ratio*range[1]/2];
|
||||||
|
var [scale, axes] = drawGraphBase([[-r[0], r[0]],[-r[1], r[1]]]);
|
||||||
|
for(var i = 0; i < thisSet.eq.length; i++) {
|
||||||
|
drawEquation(thisSet.calcArray[i], this.children[1].value, scale, axes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var viewForm = {
|
||||||
|
name: "View formula",
|
||||||
|
id: "viewFormOp",
|
||||||
|
input: false,
|
||||||
|
disableDef: true,
|
||||||
|
onclick: function() {
|
||||||
|
viewMath(index, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var viewEq = {
|
||||||
|
name: "View equations",
|
||||||
|
id: "viewEqOp",
|
||||||
|
input: false,
|
||||||
|
disableDef: true,
|
||||||
|
onclick: function() {
|
||||||
|
viewAllEq(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var exportDiv = {
|
||||||
|
name: "Export data",
|
||||||
|
id: "exportData",
|
||||||
|
input: false,
|
||||||
|
disableDef: true,
|
||||||
|
onclick: function() {
|
||||||
|
var thisSet = allSets[index];
|
||||||
|
var data = {
|
||||||
|
name: thisSet.name,
|
||||||
|
paths: thisSet.paths,
|
||||||
|
constants: thisSet.eq
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
var blob = new Blob([JSON.stringify(data)], {type: "text/plain;charset=utf-8"});
|
||||||
|
} catch(err) {
|
||||||
|
var BlobBuilder = window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
|
||||||
|
var bb = new BlobBuilder();
|
||||||
|
bb.append((new XMLSerializer).serializeToString(data));
|
||||||
|
var blob = bb.getBlob("text/plain;charset=utf-8");
|
||||||
|
}
|
||||||
|
saveAs(blob, allSets[index].name+"_data.txt");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var back = {
|
||||||
|
name: "Back",
|
||||||
|
id: "backOp",
|
||||||
|
input: false,
|
||||||
|
onclick: function() {
|
||||||
|
updateSidebar();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(allSets[index].eq.length > 1) {
|
||||||
|
options.push(viewEq);
|
||||||
|
} else {
|
||||||
|
options.push(viewForm);
|
||||||
|
}
|
||||||
|
options.push(exportDiv);
|
||||||
|
options.push(back);
|
||||||
|
var setCont = document.getElementById("setCont");
|
||||||
|
var selectedSet = setCont.children[index];
|
||||||
|
setCont.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
while(setCont.children[0]) setCont.removeChild(setCont.children[0]);
|
||||||
|
setCont.appendChild(selectedSet);
|
||||||
|
for(var i = 0; i < options.length; i++) {
|
||||||
|
var op = document.createElement("div");
|
||||||
|
op.className = "option transition" + ((options[i].disableDef && allSets[index].eq.length === 0) ? " disabled" : "");
|
||||||
|
op.id = options[i].id;
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.appendChild(document.createTextNode(options[i].name));
|
||||||
|
op.appendChild(p);
|
||||||
|
if(options[i].input) {
|
||||||
|
var inp = document.createElement("input");
|
||||||
|
inp.setAttribute("type", "number");
|
||||||
|
inp.setAttribute("min", options[i].inputRange[0]);
|
||||||
|
inp.setAttribute("max", options[i].inputRange[1]);
|
||||||
|
inp.onkeyup = function(e) {
|
||||||
|
if(e.keyCode === 13) this.parentNode.click();
|
||||||
|
}
|
||||||
|
inp.className = "transition";
|
||||||
|
inp.oninput = function() {
|
||||||
|
var val = parseInt(this.value);
|
||||||
|
this.value = Math.floor(val);
|
||||||
|
if(val < parseInt(this.getAttribute("min"))) this.value = this.getAttribute("min");
|
||||||
|
if(val > parseInt(this.getAttribute("max"))) this.value = this.getAttribute("max");
|
||||||
|
}
|
||||||
|
inp.onclick = function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
var thisSet = allSets[index];
|
||||||
|
if(thisSet.eq.length !== 0) {
|
||||||
|
inp.value = Object.keys(thisSet.eq[0][0]).length-1;
|
||||||
|
} else {
|
||||||
|
inp.value = options[i].inputDefValue;
|
||||||
|
}
|
||||||
|
op.appendChild(inp);
|
||||||
|
var p2 = document.createElement("p");
|
||||||
|
p2.appendChild(document.createTextNode("terms!"));
|
||||||
|
op.appendChild(p2);
|
||||||
|
}
|
||||||
|
op.onclick = options[i].onclick;
|
||||||
|
setCont.appendChild(op);
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
setCont.style.opacity = "1";
|
||||||
|
}, 300);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewMath(index, index2) {
|
||||||
|
var mathContIm = document.createElement("div");
|
||||||
|
mathContIm.id = "mathContIm";
|
||||||
|
var mathContRe = document.createElement("div");
|
||||||
|
mathContRe.id = "mathContRe";
|
||||||
|
var thisEq = allSets[index].eq[index2];
|
||||||
|
var options = [
|
||||||
|
{
|
||||||
|
name: "Form: Imaginary",
|
||||||
|
id: "mathType",
|
||||||
|
input: false,
|
||||||
|
onclick: function() {
|
||||||
|
var mc = [document.getElementById("mathContIm"), document.getElementById("mathContRe")];
|
||||||
|
if(this.textContent.search("Imag") !== -1) {
|
||||||
|
this.children[0].textContent = "Form: Parametric";
|
||||||
|
mc[0].style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
mc[0].style.display = "none";
|
||||||
|
mc[1].style.display = "block";
|
||||||
|
setTimeout(function() {
|
||||||
|
mc[1].style.opacity = "1";
|
||||||
|
}, 10);
|
||||||
|
}, 300);
|
||||||
|
} else {
|
||||||
|
this.children[0].textContent = "Form: Imaginary";
|
||||||
|
mc[1].style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
mc[1].style.display = "none";
|
||||||
|
mc[0].style.display = "block";
|
||||||
|
setTimeout(function() {
|
||||||
|
mc[0].style.opacity = "1";
|
||||||
|
}, 10);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Copy",
|
||||||
|
id: "copyButton",
|
||||||
|
input: true,
|
||||||
|
inputRange: [1, Object.keys(thisEq[0]).length],
|
||||||
|
inputDefValue: 1,
|
||||||
|
disableDef: true,
|
||||||
|
onclick: function() {
|
||||||
|
var inp = document.getElementById("copyBox");
|
||||||
|
var type = (document.getElementById("mathType").textContent.search("Imag") !== -1) ? "im" : "re";
|
||||||
|
inp.value = toLatex(thisEq, type, this.children[1].value);
|
||||||
|
inp.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
insert: true,
|
||||||
|
insertDiv: mathContIm
|
||||||
|
},
|
||||||
|
{
|
||||||
|
insert: true,
|
||||||
|
insertDiv: mathContRe
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Back",
|
||||||
|
id: "backOp",
|
||||||
|
input: false,
|
||||||
|
onclick: function() {
|
||||||
|
if(index2 === 0) {
|
||||||
|
setInfo(0);
|
||||||
|
} else {
|
||||||
|
setInfoEq(index, index2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var setCont = document.getElementById("setCont");
|
||||||
|
var selectedSet = setCont.children[0];
|
||||||
|
setCont.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
while(setCont.children[0]) setCont.removeChild(setCont.children[0]);
|
||||||
|
setCont.appendChild(selectedSet);
|
||||||
|
var eqKeys = Object.keys(thisEq[0]);
|
||||||
|
for(var i = 0; i < eqKeys.length-1; i++) {
|
||||||
|
var j = ((i+1)%2===1) ? Math.ceil((i+1)/2) : -Math.floor((i+1)/2);
|
||||||
|
var num = (j === 1) ? "" :j;
|
||||||
|
var end = (i === eqKeys.length-2) ? "" : "+";
|
||||||
|
var add = (thisEq[1][eqKeys[i]] < 1) ? "-" : "+";
|
||||||
|
var sub = (thisEq[1][eqKeys[i]] < 1) ? "+" : "-";
|
||||||
|
var oneTermIm = document.createElement("div");
|
||||||
|
var oneTermRe = document.createElement("div");
|
||||||
|
oneTermIm.textContent = "$$\\left(" + thisEq[0][j] + add + Math.abs(thisEq[1][j]) + "i\\right)e^{" + num + "it}" + end + "$$" ;
|
||||||
|
if(i === 0) {
|
||||||
|
oneTermRe.textContent = "$$\\left\\{\\begin{matrix}x=" + thisEq[0][j] + "\\cos\\left(" + num + "t\\right)" + sub + Math.abs(thisEq[1][j]) + "\\sin\\left(" + num + "t\\right)"
|
||||||
|
+ "+\\\\y=" + thisEq[0][j] + "\\sin\\left(" + num + "t\\right)" + add + Math.abs(thisEq[1][j]) + "\\cos\\left(" + num + "t\\right)+\\end{matrix}\\right.$$";
|
||||||
|
} else {
|
||||||
|
oneTermRe.textContent = "$$"+ thisEq[0][j] + "\\cos\\left(" + num + "t\\right)" + sub + Math.abs(thisEq[1][j]) + "\\sin\\left(" + num + "t\\right)"
|
||||||
|
+ "+\\\\" + thisEq[0][j] + "\\sin\\left(" + num + "t\\right)" + add + Math.abs(thisEq[1][j]) + "\\cos\\left(" + num + "t\\right)+$$"
|
||||||
|
}
|
||||||
|
mathContIm.appendChild(oneTermIm);
|
||||||
|
mathContRe.appendChild(oneTermRe);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < options.length; i++) {
|
||||||
|
if(options[i].insert) {
|
||||||
|
setCont.appendChild(options[i].insertDiv);
|
||||||
|
} else {
|
||||||
|
var op = document.createElement("div");
|
||||||
|
op.className = "option transition";
|
||||||
|
op.id = options[i].id;
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.appendChild(document.createTextNode(options[i].name));
|
||||||
|
op.appendChild(p);
|
||||||
|
if(options[i].input) {
|
||||||
|
var inp = document.createElement("input");
|
||||||
|
inp.setAttribute("type", "number");
|
||||||
|
inp.setAttribute("min", options[i].inputRange[0]);
|
||||||
|
inp.setAttribute("max", options[i].inputRange[1]);
|
||||||
|
inp.onkeyup = function(e) {
|
||||||
|
if(e.keyCode === 13) this.parentNode.click();
|
||||||
|
}
|
||||||
|
inp.className = "transition";
|
||||||
|
inp.oninput = function() {
|
||||||
|
var val = parseInt(this.value);
|
||||||
|
this.value = Math.floor(val);
|
||||||
|
if(val < parseInt(this.getAttribute("min"))) this.value = this.getAttribute("min");
|
||||||
|
if(val > parseInt(this.getAttribute("max"))) this.value = this.getAttribute("max");
|
||||||
|
}
|
||||||
|
inp.onclick = function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
var thisSet = allSets[index];
|
||||||
|
if(thisSet.eq.length !== 0) {
|
||||||
|
inp.value = Object.keys(thisSet.eq[0][0]).length-1;
|
||||||
|
} else {
|
||||||
|
inp.value = options[i].inputDefValue;
|
||||||
|
}
|
||||||
|
op.appendChild(inp);
|
||||||
|
var p2 = document.createElement("p");
|
||||||
|
p2.appendChild(document.createTextNode("terms!"));
|
||||||
|
op.appendChild(p2);
|
||||||
|
}
|
||||||
|
op.onclick = options[i].onclick;
|
||||||
|
setCont.appendChild(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MathJax.Hub.Queue(["Typeset",MathJax.Hub,"mathCont"]);
|
||||||
|
setTimeout(function() {
|
||||||
|
setCont.style.opacity = "1";
|
||||||
|
}, 300);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewAllEq(index) {
|
||||||
|
var setCont = document.getElementById("setCont");
|
||||||
|
var selectedSet = setCont.children[0];
|
||||||
|
setCont.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
while(setCont.children[0]) setCont.removeChild(setCont.children[0]);
|
||||||
|
setCont.appendChild(selectedSet);
|
||||||
|
allSets[index].eq.forEach(function(a,i) {
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.className = "option";
|
||||||
|
div.onclick = function() {
|
||||||
|
setInfoEq(index, i);
|
||||||
|
};
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.appendChild(document.createTextNode("Equation " + (i+1)));
|
||||||
|
div.appendChild(p);
|
||||||
|
setCont.appendChild(div);
|
||||||
|
});
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.className = "option";
|
||||||
|
div.onclick = function() {
|
||||||
|
setInfo(calcIndex);
|
||||||
|
}
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.appendChild(document.createTextNode("Back"));
|
||||||
|
div.appendChild(p);
|
||||||
|
setCont.appendChild(div);
|
||||||
|
setTimeout(function() {
|
||||||
|
setCont.style.opacity = "1";
|
||||||
|
}, 300);
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInfoEq(index1, index2) {
|
||||||
|
var termNum = Object.keys(allSets[index1].eq[0][0]).length - 1;
|
||||||
|
var options = [
|
||||||
|
{
|
||||||
|
name: "Graph",
|
||||||
|
id: "graphOp",
|
||||||
|
input: true,
|
||||||
|
inputRange: [1, termNum],
|
||||||
|
inputDefValue: termNum,
|
||||||
|
onclick: function() {
|
||||||
|
var thisSet = allSets[index1];
|
||||||
|
var thisPath = allSets[index1].paths[index2];
|
||||||
|
var flat = thisPath.reduce((a,b)=>a.concat(b));
|
||||||
|
flat = flat[0].map((a,i)=>flat.map(b=>b[i]));
|
||||||
|
var range = [Math.max.apply(null, flat[0]), Math.max.apply(null, flat[1])];
|
||||||
|
thisPath = thisPath.map(b=>b.map(c=>[c[0]-range[0]/2, -1*(c[1]-range[1]/2)]));
|
||||||
|
var thisCalcArray = new Array(thisSet.calcArray[index2].length);
|
||||||
|
var thisTerm = thisSet.calcArray[index2][this.children[1].value];
|
||||||
|
flat = thisTerm[0].map((a,i)=>thisTerm.map(b=>b[i]));
|
||||||
|
var min = [Math.min.apply(null, flat[0]), Math.min.apply(null, flat[1])]
|
||||||
|
var range = [
|
||||||
|
Math.abs(Math.max.apply(null, flat[0])-min[0]),
|
||||||
|
Math.abs(Math.max.apply(null, flat[1])-min[1])
|
||||||
|
];
|
||||||
|
thisTerm = thisTerm.map(c=>[c[0]-min[0]-range[0]/2, c[1]-min[1]-range[1]/2]);
|
||||||
|
thisCalcArray[this.children[1].value] = thisTerm;
|
||||||
|
var ratio = (canvas.height > canvas.width) ? canvas.height/canvas.width : canvas.width/canvas.height;
|
||||||
|
var r = [1.1*ratio*range[0]/2, 1.1*ratio*range[1]/2];
|
||||||
|
var [scale, axes] = drawGraphBase([[-r[0], r[0]],[-r[1], r[1]]]);
|
||||||
|
drawEquation(thisCalcArray, this.children[1].value, scale, axes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "View formula",
|
||||||
|
id: "viewFormOp",
|
||||||
|
input: false,
|
||||||
|
onclick: function() {
|
||||||
|
viewMath(index1, index2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Back",
|
||||||
|
id: "backOp",
|
||||||
|
input: false,
|
||||||
|
onclick: function() {
|
||||||
|
viewAllEq(index1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
var setCont = document.getElementById("setCont");
|
||||||
|
var selectedSet1 = setCont.children[0];
|
||||||
|
var selectedSet2 = setCont.children[index2+1];
|
||||||
|
selectedSet2.onclick = function() {};
|
||||||
|
setCont.style.opacity = "0";
|
||||||
|
setTimeout(function() {
|
||||||
|
while(setCont.children[0]) setCont.removeChild(setCont.children[0]);
|
||||||
|
setCont.appendChild(selectedSet1);
|
||||||
|
setCont.appendChild(selectedSet2);
|
||||||
|
for(var i = 0; i < options.length; i++) {
|
||||||
|
var op = document.createElement("div");
|
||||||
|
op.className = "option transition";
|
||||||
|
op.id = options[i].id;
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.appendChild(document.createTextNode(options[i].name));
|
||||||
|
op.appendChild(p);
|
||||||
|
if(options[i].input) {
|
||||||
|
var inp = document.createElement("input");
|
||||||
|
inp.setAttribute("type", "number");
|
||||||
|
inp.setAttribute("min", options[i].inputRange[0]);
|
||||||
|
inp.setAttribute("max", options[i].inputRange[1]);
|
||||||
|
inp.onkeyup = function(e) {
|
||||||
|
if(e.keyCode === 13) this.parentNode.click();
|
||||||
|
}
|
||||||
|
inp.className = "transition";
|
||||||
|
inp.oninput = function() {
|
||||||
|
var val = parseInt(this.value);
|
||||||
|
this.value = Math.floor(val);
|
||||||
|
if(val < parseInt(this.getAttribute("min"))) this.value = this.getAttribute("min");
|
||||||
|
if(val > parseInt(this.getAttribute("max"))) this.value = this.getAttribute("max");
|
||||||
|
}
|
||||||
|
inp.onclick = function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
inp.value = options[i].inputDefValue;
|
||||||
|
op.appendChild(inp);
|
||||||
|
var p2 = document.createElement("p");
|
||||||
|
p2.appendChild(document.createTextNode("terms!"));
|
||||||
|
op.appendChild(p2);
|
||||||
|
}
|
||||||
|
op.onclick = options[i].onclick;
|
||||||
|
setCont.appendChild(op);
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
setCont.style.opacity = "1";
|
||||||
|
}, 300);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawEquation(calcArray, cycles, scale, axes) {
|
||||||
|
var arr = calcArray[cycles];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.lineJoin = "round";
|
||||||
|
ctx.strokeStyle = allSets[calcIndex].color;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.moveTo(axes[0]+arr[0][0]*scale, axes[1]-arr[0][1]*scale);
|
||||||
|
for(var i = 1; i < arr.length; i++) {
|
||||||
|
ctx.lineTo(axes[0]+arr[i][0]*scale, axes[1]-arr[i][1]*scale);
|
||||||
|
}
|
||||||
|
ctx.lineTo(axes[0]+arr[0][0]*scale, axes[1]-arr[0][1]*scale);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawGraphBase(win) {
|
||||||
|
var win = win || [[-55,55], [-55,55]];
|
||||||
|
var range = [Math.abs(win[0][0]-win[0][1]), Math.abs(win[1][0]-win[1][1])];
|
||||||
|
var largeRange, which, winWhich;
|
||||||
|
if(range[0] > range[1]) {
|
||||||
|
largeRange = range[0];
|
||||||
|
which = "width";
|
||||||
|
winWhich = 0;
|
||||||
|
} else {
|
||||||
|
largeRange = range[1];
|
||||||
|
which = "height";
|
||||||
|
winWhich = 1;
|
||||||
|
}
|
||||||
|
var nonRound = largeRange/13;
|
||||||
|
var log = Math.ceil(Math.log10(nonRound));
|
||||||
|
var scale = (Math.abs(nonRound-Math.pow(10, log)) > Math.abs(nonRound-5*Math.pow(10, log-1))) ? 5*Math.pow(10, log-1) : Math.pow(10, log);
|
||||||
|
var ppu = canvas[which]/largeRange;
|
||||||
|
var unit = Math.floor(ppu*scale);
|
||||||
|
var realAxes = [];
|
||||||
|
var axes = [];
|
||||||
|
var offset = [];
|
||||||
|
if(win[0][0] >= 0 && win[0][1] >= 0) {
|
||||||
|
axes[0] = .1 * canvas.width;
|
||||||
|
offset[0] = Math.min.apply(null, win[0].map((a=>Math.floor(Math.abs(a)))));
|
||||||
|
realAxes[0] = axes[0] + ppu*(scale-offset[0]);
|
||||||
|
} else if(win[0][0] <= 0 && win[0][1] <= 0) {
|
||||||
|
axes[0] = .9 *canvas.width;
|
||||||
|
offset[0] = -Math.min.apply(null, win[0].map((a=>Math.floor(Math.abs(a)))));
|
||||||
|
realAxes[0] = axes[0] + ppu*(scale-offset[0]);
|
||||||
|
} else {
|
||||||
|
axes[0] = Math.abs(win[winWhich][0]/largeRange) * canvas.width;
|
||||||
|
offset[0] = 0;
|
||||||
|
realAxes[0] = axes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(win[1][0] >= 0 && win[1][1] >= 0) {
|
||||||
|
axes[1] = .9 * canvas.height;
|
||||||
|
offset[1] = Math.min.apply(null, win[1].map((a=>Math.floor(Math.abs(a)))));
|
||||||
|
realAxes[1] = axes[1] + ppu*(scale-offset[1]);
|
||||||
|
} else if(win[0][0] <= 0 && win[1][1] <= 0) {
|
||||||
|
axes[1] = .1 *canvas.height;
|
||||||
|
offset[1] = -Math.min.apply(null, win[1].map((a=>Math.floor(Math.abs(a)))));
|
||||||
|
realAxes[1] = axes[1] + ppu*(scale-offset[1]);
|
||||||
|
} else {
|
||||||
|
axes[1] = Math.abs(win[winWhich][1]/largeRange) * canvas.height;
|
||||||
|
offset[1] = 0;
|
||||||
|
realAxes[1] = axes[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.clearRect(0,0,canvas.width,canvas.height);
|
||||||
|
// Sub-intervals
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = "#E0E0E0";
|
||||||
|
ctx.beginPath();
|
||||||
|
for(var i = -unit*4; i <= unit*4; i++) {
|
||||||
|
if(i === 0) continue;
|
||||||
|
var x = axes[0] + ppu*i*scale/5;
|
||||||
|
var y = axes[1] + ppu*i*scale/5;
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, canvas.height);
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(canvas.width, y);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
// Intervals
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = "#BDBDBD";
|
||||||
|
ctx.beginPath();
|
||||||
|
for(var i = -unit; i <= unit; i++) {
|
||||||
|
if(i === 0) continue;
|
||||||
|
var x = axes[0] + ppu*i*scale;
|
||||||
|
var y = axes[1] - ppu*i*scale;
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, canvas.height);
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(canvas.width, y);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
// Axes
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = "#424242";
|
||||||
|
ctx.moveTo(axes[0], 0);
|
||||||
|
ctx.lineTo(axes[0], canvas.height);
|
||||||
|
ctx.moveTo(0, axes[1]);
|
||||||
|
ctx.lineTo(canvas.width, axes[1]);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
// Texts
|
||||||
|
var textOffset = canvas.width/75;
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
for(var i = -unit; i <= unit; i++) {
|
||||||
|
var x = axes[0] + ppu*i*scale;
|
||||||
|
var y = axes[1] - ppu*i*scale;
|
||||||
|
ctx.font = (canvas.width/75).toString() + "px Poppins";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
if(i === 0) {
|
||||||
|
ctx.fillText((i*scale).toString(), axes[0]-textOffset/2, y+textOffset);
|
||||||
|
} else {
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.fillText((i*scale + Math.floor(offset[0])).toString(), x, axes[1]+textOffset);
|
||||||
|
ctx.textAlign = "end";
|
||||||
|
ctx.fillText((i*scale + Math.floor(offset[1])).toString(), axes[0]-textOffset/2, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [ppu, realAxes];
|
||||||
|
}
|
||||||
|
|
||||||
|
function loading(t) {
|
||||||
|
if(!loadStart) loadStart = t;
|
||||||
|
var circTime = 700;
|
||||||
|
var progress = t - loadStart;
|
||||||
|
progress = progress - Math.floor(progress/(3.5*circTime))*3.5*circTime;
|
||||||
|
ctx.clearRect(0,0,canvas.width,canvas.height)
|
||||||
|
var r = 0.05*canvas.width;
|
||||||
|
var beg = 1.5*Math.PI;
|
||||||
|
var circProg = progress/circTime - Math.floor(progress/circTime);
|
||||||
|
var cent = [canvas.width/2, canvas.height*.35];
|
||||||
|
var animProg = beg - (3*Math.pow(circProg,2)-2*Math.pow(circProg,3))*2*Math.PI;
|
||||||
|
animProg = (animProg < 0) ? 2*Math.PI+animProg : animProg;
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
ctx.strokeStyle = "#000";
|
||||||
|
ctx.fillStyle = "#000";
|
||||||
|
if(progress < circTime) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]-3.5*r, cent[1], r, beg, animProg, true);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if(progress < 2*circTime) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]-3.5*r, cent[1], r, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0], cent[1], r, beg, animProg, true);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if(progress < 3*circTime) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]-3.5*r, cent[1], r, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0], cent[1], r, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]+3.5*r, cent[1], r, beg, animProg, true);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if(progress <= 4*circTime) {
|
||||||
|
var newR = r-3*r*Math.pow(2*circProg,2)+2*r*Math.pow(2*circProg,3);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]-3.5*r, cent[1], newR, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0], cent[1], newR, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cent[0]+3.5*r, cent[1], newR, 0, 2*Math.PI, true);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.baseLine = "middle";
|
||||||
|
ctx.font = (canvas.width/50).toString() + "px Poppins";
|
||||||
|
ctx.fillText("Generating equations, this may take a while.", canvas.width/2, canvas.height/2);
|
||||||
|
var rectWidth = canvas.width*0.7;
|
||||||
|
var edge = (canvas.width-rectWidth)/2;
|
||||||
|
ctx.rect(edge, canvas.height*.65, rectWidth, rectWidth*.05);
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle="#4CAF50";
|
||||||
|
ctx.fillRect(edge+2, canvas.height*.65+2, percent*(rectWidth-4), rectWidth*.05-4);
|
||||||
|
loadFrame = window.requestAnimationFrame(loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toLatex(eq, type, amount) {
|
||||||
|
var eqKeys = Object.keys(eq[0]);
|
||||||
|
amount = amount || eqKeys.length-1;
|
||||||
|
var final = "";
|
||||||
|
for(var i = 0; i < amount; i++) {
|
||||||
|
var j = ((i+1)%2===1) ? Math.ceil((i+1)/2) : -Math.floor((i+1)/2);
|
||||||
|
var num = (j === 1) ? "" : j;
|
||||||
|
var end = (i === eqKeys.length-2) ? "" : "+";
|
||||||
|
var add = (eq[1][eqKeys[i]] < 1) ? "-" : "+";
|
||||||
|
var sub = (eq[1][eqKeys[i]] < 1) ? "+" : "-";
|
||||||
|
|
||||||
|
if(type === "im") {
|
||||||
|
final += "\\left(" + eq[0][j] + add + Math.abs(eq[1][j]) + "i\\right)e^{" + num + "it}" + end;
|
||||||
|
} else {
|
||||||
|
if(i === 0) {
|
||||||
|
final += "\\left\\{\\begin{matrix}x=" + eq[0][j] + "\\cos\\left(" + num + "t\\right)" + sub + Math.abs(eq[1][j]) + "\\sin\\left(" + num + "t\\right)"
|
||||||
|
+ "+\\\\y=" + eq[0][j] + "\\sin\\left(" + num + "t\\right)" + add + Math.abs(eq[1][j]) + "\\cos\\left(" + num + "t\\right)+\\end{matrix}\\right.";
|
||||||
|
} else {
|
||||||
|
final += eq[0][j] + "\\cos\\left(" + num + "t\\right)" + sub + Math.abs(eq[1][j]) + "\\sin\\left(" + num + "t\\right)"
|
||||||
|
+ "+\\\\" + eq[0][j] + "\\sin\\left(" + num + "t\\right)" + add + Math.abs(eq[1][j]) + "\\cos\\left(" + num + "t\\right)+"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processSVG(svg) {
|
||||||
|
var commLength = {
|
||||||
|
"M": 1, "L": 1, "H": 1, "V": 1,
|
||||||
|
"Q": 2, "C": 3, "T": 1, "S": 2,
|
||||||
|
"A": 3
|
||||||
|
};
|
||||||
|
var allPaths = svg.replace(/z/gi,"").split(/(?=M)/gi);
|
||||||
|
for(var k = 0; k < allPaths.length; k++) {
|
||||||
|
var processed = [];
|
||||||
|
var arr = allPaths[k].split(/(?=[a-z])/gi);
|
||||||
|
if(arr.length === 0) continue;
|
||||||
|
var commList = [];
|
||||||
|
for(var i = 0; i < arr.length; i++) {
|
||||||
|
var command = arr[i].match(/[a-z]/gi)[0];
|
||||||
|
var values = arr[i].match(/(-)?([0-9]+)(\.([0-9])+)?/gi);
|
||||||
|
if(command.toUpperCase() !== "A") {
|
||||||
|
values = [values.filter((a,i)=>i%2===0),values.filter((a,i)=>i%2===1)].map(a=>a.map(b=>parseFloat(b)));
|
||||||
|
values = values[0].map((a,i)=>[a, (values[1][i]||0)]);
|
||||||
|
if(command.toUpperCase() === "V") values.reverse();
|
||||||
|
} else {
|
||||||
|
values = [
|
||||||
|
values.filter((a,i)=>i%7===0||i%4===1),
|
||||||
|
values.filter((a,i)=>i%7===2||i%7===3||i%7===4),
|
||||||
|
values.filter((a,i)=>i%7===5||i%7===6)
|
||||||
|
].map(a=>a.map(b=>parseFloat(b)));
|
||||||
|
}
|
||||||
|
var len = commLength[command.toUpperCase()];
|
||||||
|
if(values.length !== len) {
|
||||||
|
for(var j = 0; j < values.length/len; j++) {
|
||||||
|
commList.push([command].concat(values.slice(len*j, len*(j+1))));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
commList.push([command].concat(values));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastPoint = [0,0];
|
||||||
|
for(var i = 0; i < commList.length; i++) {
|
||||||
|
var command = commList[i][0];
|
||||||
|
var rel = false;
|
||||||
|
if(command === command.toLowerCase()) {
|
||||||
|
rel = true;
|
||||||
|
command = command.toUpperCase();
|
||||||
|
}
|
||||||
|
var points = commList[i].slice(1);
|
||||||
|
var offset = relOff(arrOp(lastPoint, "*", rel), points);
|
||||||
|
if(command === "M") {
|
||||||
|
lastPoint = offset[0];
|
||||||
|
} else if(command === "T") {
|
||||||
|
var lastSet = processed[processed.length-1];
|
||||||
|
var newBez = arrOp(arrOp(2,"*",lastSet[2]), "-", lastSet[1]);
|
||||||
|
processed.push([lastPoint].concat([newBez]).concat(offset));
|
||||||
|
lastPoint = processed[processed.length-1].slice().reverse()[0];
|
||||||
|
} else if(command === "S") {
|
||||||
|
var lastSet = processed[processed.length-1];
|
||||||
|
var newBez = arrOp(arrOp(2,"*",lastSet[3]), "-", lastSet[2]);
|
||||||
|
processed.push([lastPoint].concat([newBez]).concat(offset));
|
||||||
|
lastPoint = processed[processed.length-1].slice().reverse()[0];
|
||||||
|
} else if(command === "A") {
|
||||||
|
alert("The A path command in the SVG is not supported! Please modify or change the SVG file to not contain the A command.")
|
||||||
|
return [];
|
||||||
|
} else {
|
||||||
|
processed.push([lastPoint].concat(offset));
|
||||||
|
lastPoint = processed[processed.length-1].slice().reverse()[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allPaths[k] = processed;
|
||||||
|
}
|
||||||
|
var flat = allPaths.reduce((a,b)=>a.concat(b)).reduce((a,b)=>a.concat(b));
|
||||||
|
flat = flat[0].map((a,i)=>flat.map(b=>b[i]));
|
||||||
|
var range = [Math.max.apply(null, flat[0]), Math.max.apply(null, flat[1])];
|
||||||
|
allPaths = allPaths.map(a=>a.map(b=>b.map(c=>[c[0]-range[0]/2, -1*(c[1]-range[1]/2)])));
|
||||||
|
return [allPaths, range];
|
||||||
|
}
|
||||||
|
|
||||||
|
function relOff(offset, pointSet) {
|
||||||
|
return pointSet.map(a=>arrOp(a,"+", offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrOp(arr1, op, arr2) { // Applies operator arr1n (op) arr2n thus not following normal vector rules.
|
||||||
|
if(arr1.length === undefined) arr1 = new Array(arr2.length).fill(arr1);
|
||||||
|
if(arr2.length === undefined) arr2 = new Array(arr1.length).fill(arr2);
|
||||||
|
return arr1.map((a,i)=>eval(a+op+"("+arr2[i]+")"));
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user