/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.WJuw4ovdPex/rev.2091
*
* authors:
* Alexander Ruff
*
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
/*
* INSTRUCTIONS:
* left-click/drag - make a fold through the line, that folds toward the
* top left corner
* left-click/drag - make a fold through the line, that folds away from the
* top left corner
* 'n' - New piece of paper
* 'Right' - Undo previous cut
* 'Left' - Redo/Next
* 'r' - reflect previous fold
* 'f' - flip page over
* TODO
* pull from server
* refactor sheets data structure to maintain pieces connected by folds
* unfold (different from undo because folds are still marked)
* clean up code
* mountain folds
* MAYBE:
* save to server
* symmetric folds
* bounce to grid points
* allow user to rotate model
* allow user to translate model
* fold bias at center of page instead of top left.
* ghost new fold
* RTCE
* scissors
* figure out which pieces are visible
* ruler (as an extension of grid bouncing)
* snap edges
* smart center
* smart rotate
* allow unfolds other than the most recent one where possible
*/
var foldingSheets = [];
var foldingFinal = [];
var foldingTime = 1000;
var foldingStart;
var firstX, firstY;
var lastX, lastY;
var sheets;
var oldSheets = [];
var btn;
50,50,450,50,450,600,50,600
var foldList = [
[249.9,50,50,249.9,true],
[250.1,50,450,249.9,false],
[249,49.9,50,528,true],
[251,49.9,450,528,false]];
function valley(x1,y1,x2,y2) {
strokeWeight(2);
stroke(0);
var d = dist(firstX, firstY, mouseX, mouseY);
for(var i=0; i<d; i+=20) {
line(lerp(firstX, mouseX, i/d),lerp(firstY, mouseY, i/d),
lerp(firstX, mouseX, min((i+10)/d,1)),lerp(firstY, mouseY, min((i+10)/d,1)));
}
}
function mountain(x1,y1,x2,y2) {
strokeWeight(2);
stroke(0);
var d = dist(firstX, firstY, mouseX, mouseY);
for(var i=0; i<d; i+=40) {
line(lerp(firstX, mouseX, i/d),lerp(firstY, mouseY, i/d),
lerp(firstX, mouseX, min((i+10)/d,1)),lerp(firstY, mouseY, min((i+10)/d,1)));
}
for(var i=0; i<d; i+=10) {
point(lerp(firstX, mouseX, i/d),lerp(firstY, mouseY, i/d));
}
}
function poly(P) {
strokeWeight(2);
stroke(0);
if(P[0]) fill(230);
else fill(250);
beginShape();
for (var i = 1; i < P.length; i += 2) {
vertex(P[i], P[i+1]);
}
endShape(CLOSE);
}
function intersect(P, x1, y1, x2, y2) {
var I = [];
var x3 = P[P.length-2];
var y3 = P[P.length-1];
for (var i = 1; i < P.length; i += 2) {
var x4 = P[i];
var y4 = P[i+1];
var d = (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4);
var x = ((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/d;
var y = ((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/d;
/*if(max(min(x1,x2),min(x3,x4))-.0001<=x &&
min(max(x1,x2),max(x3,x4))+.0001>=x &&
max(min(y1,y2),min(y3,y4))-.0001<=y &&
min(max(y1,y2),max(y3,y4))+.0001>=y)*/
if(min(x3,x4)-.0001<=x &&
max(x3,x4)+.0001>=x &&
min(y3,y4)-.0001<=y &&
max(y3,y4)+.0001>=y)
I.push([i,[x,y]]);
x3 = x4;
y3 = y4;
}
return I;
}
function cut(x1,y1,x2,y2,fold_dir) {
oldSheets.push(sheets.slice());
foldingSheets = [];
var newSheets = [];
for(var i = 0; i<sheets.length; i++) {
var I = intersect(sheets[i],x1,y1,x2,y2);
if(I.length==2) {
var a = [sheets[i][0]].concat(I[1][1],I[0][1],sheets[i].slice(I[0][0],I[1][0]));
var b = [sheets[i][0]].concat(sheets[i].slice(1,I[0][0]),I[0][1],I[1][1],sheets[i].slice(I[1][0]));
var x = sheets[i][I[0][0]];
var y = sheets[i][I[0][0]+1];
if((((x1*y2-x2*y1)*((x1-x)*(y2-y)-(x2-x)*(y1-y))>0) ^
fold_dir)==1) {
newSheets.push(a);
foldingSheets.push(b);
}
else {
newSheets.push(b);
foldingSheets.push(a);
}
}
else {
var x = sheets[i][1];
var y = sheets[i][2];
if((((x1*y2-x2*y1)*((x1-x)*(y2-y)-(x2-x)*(y1-y))>0)^fold_dir)==1)
newSheets.push(sheets[i]);
else foldingSheets.push(sheets[i]);
}
}
fold(x1,y1,x2,y2);
sheets = newSheets;
foldingStart = millis();
}
function reflect(x1, y1, x2, y2, x, y) {
var proj = ((x-x1)*(x2-x1)+(y-y1)*(y2-y1))/((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
return [2*proj*(x2-x1)+2*x1-x, 2*proj*(y2-y1)+2*y1-y];
}
function fold(x1,y1,x2,y2) {
foldingFinal = foldingSheets.slice();
for(var i=0; i<foldingFinal.length; i++) {
foldingFinal[i] = foldingFinal[i].slice();
foldingFinal[i][0] = !foldingFinal[i][0];
for(var j=1; j<foldingFinal[i].length; j+=2) {
var p = reflect(x1,y1,x2,y2,foldingFinal[i][j],foldingFinal[i][j+1]);
foldingFinal[i][j] = p[0];
foldingFinal[i][j+1] = p[1];
}
}
}
function folding() {
var r = (millis()-foldingStart)/foldingTime;
if(r>=1) {
sheets = sheets.concat(foldingFinal.reverse());
foldingSheets = [];
for(var i=0; i<foldingFinal.length; i++) {
poly(foldingFinal[i]);
}
}
if(r<1/2) for(var i=0; i<foldingSheets.length; i++) {
var p = [foldingSheets[i][0]];
for(far j=1; j<foldingSheets[i].length; j++) {
p.push(lerp(foldingSheets[i][j],foldingFinal[i][j],r));
}
poly(p);
}
else for(var i=foldingSheets.length-1; i>=0; i--) {
var p = [!foldingSheets[i][0]];
for(far j=1; j<foldingSheets[i].length; j++) {
p.push(lerp(foldingSheets[i][j],foldingFinal[i][j],r));
}
poly(p);
}
}
void setup() {
strokeJoin(BEVEL);
sheets = [[false,50,50,450,50,450,600,50,600]];
size(500, 650);
smooth();
}
void mousePressed() {
btn = mouseButton
firstX = mouseX;
firstY = mouseY;
}
void mouseReleased() {
if(dist(firstX,firstY,mouseX,mouseY)>.1 && foldingSheets.length==0)
cut(firstX,firstY,mouseX,mouseY,btn==RIGHT);
}
void keyPressed() {
switch (key) {
case 'u':
if(oldSheets.length>0) {
sheets = oldSheets.pop();
foldingSheets = [];
}
break;
case 'n':
sheets = [[false,50,50,450,50,450,600,50,600]];
foldingSheets = [];
oldSheets = [];
break;
case 'f':
//needs to actually flip over
if(foldingSheets.length==0) {
foldingSheets = sheets;
sheets = [];
fold(250,0,250,650);
foldingStart = millis();
}
break;
case 'p':
console.log(sheets);
console.log(foldingSheets);
console.log(foldingFinal);
break;
default:
if(key==CODED) {
switch(keyCode) {
case RIGHT:
if(foldingSheets.length==0) {
f = foldList.shift();
cut(f[0],f[1],f[2],f[3],f[4]);
}
break;
}
}
}
}
void draw() {
background(255);
// canvas border
strokeWeight(1);
stroke(100);
noFill();
rect(0, 0, width-1, height-1);
for(var i=0; i<sheets.length; i++) {
poly(sheets[i]);
}
if(foldingSheets.length>0) folding();
if (mousePressed) {
valley(firstX, firstY, mouseX, mouseY);
strokeWeight(6);
for(var i=0; i<sheets.length; i++) {
var I = intersect(sheets[i],firstX,firstY,mouseX,mouseY);
for(var j=0; j<I.length; j++) point(I[j][1][0],I[j][1][1]);
}
}
}