xxxxxxxxxx
362
// slay
let stepSize = 2; // works for ints 1-3. bigger ints give max call stack exceeded and floats give cannot read property of undefined. assuming its a precision thing
let stitchDist = 20;
let embroideryScale = 1;
let patternScale = 2;
let datafile = "pattern9";
let filename = datafile + '-ss' + stepSize + '-sd' + stitchDist + '-sc' + patternScale;
//might need to tweak these for some polygons. basically the smaller the crispier but if its too small theres a chance it misses an intersection and it just fucks up.
let angleErrorMargin = 0.0125 * stepSize; //this one especially sucks isince it will miss everything if u got small polygons
let stepErrorMargin = 1 * stepSize;
if(stepSize == 1){
angleErrorMargin *= 2;
stepErrorMargin *= 2;
}
let intersectionProximityCheck = 6 * stepSize;
let slider;
let coords = []
let table;
let csvExporter;
let polygons = [];
let drawPath;
let sqr = [[0,0],
[0,100],
[100,100],
[100,0]];
function preload(){
table = loadTable(datafile + '.csv', 'csv', 'header');
}
function setup() {
createCanvas(500, 500);
angleMode(RADIANS);
csvExporter = new CSVExporter(filename, stitchDist, embroideryScale);
createButton('download csv').mousePressed(() => {csvExporter.saveCSV(coords); print(filename);}).position(20,height + 20);
// slider = createSlider(0,90, 1).size(width)
noFill();
let i = 999;
// let xMin=0,yMin=0,xMax=0,yMax=0;
for (let r = 0; r < table.getRowCount(); r++){
// for (let r = table.getRowCount()-1; r >=0 ; r--){
if(table.get(r, 'poly') != i){
polygons.push([]);
i = table.get(r, 'poly');
}
polygons[i].push([ int(table.get(r, 'x'))*patternScale + width/3,
int(table.get(r, 'y'))*patternScale + height/3])
// if(polygons[i][polygons[i].length-1][0] > xMax){
// xMax = polygons[i][polygons[i].length-1][0];
// }
// if(polygons[i][polygons[i].length-1][1] > yMax){
// yMax = polygons[i][polygons[i].length-1][1];
// }
}
// createCanvas(xMax, yMax);
}
function draw() {
background(220);
coords = [];
for(let n=0; n<polygons.length; n++){
// if(n!=4){
// continue
// }
noStroke();
fill(255);
beginShape();
for(let i=0; i<polygons[n].length; i++){
vertex(polygons[n][i][0], polygons[n][i][1]);
}
endShape();
drawPath = contourFill(polygons[n]);
noFill();
stroke(noise(polygons[n].length) * 200,
noise(polygons[n].length + 20) * 200,
noise(polygons[n].length + 40) * 200);
beginShape();
for(let i=drawPath.length-1; i>=0; i--){
vertex(drawPath[i][0], drawPath[i][1]);
coords.push([drawPath[i][0], drawPath[i][1]]);
}
endShape();
}
}
function contourFill(polygon){
polygon = sanitizePath(polygon);
let clockDir = findClockDirection(polygon);
let contourPath = [[polygon[polygon.length-1][0],polygon[polygon.length-1][1]]];
contourPath = contourPath.concat(polygon);
let offsetPath = [];
offsetPath = offsetPath.concat(polygon);
// bounding box surfaces to cancel the show
let bbSurf = 20000 * 18000;
let prevBbSurf = 20000 * 18000;
let offset = stepSize;
for(let i=0; i < 999; i++){
offsetPath = getOffsetPath(offsetPath, offset, clockDir);
offsetPath = sanitizePath(offsetPath);
let done = checkSelfIntersections(offsetPath).recursive;
offsetPath = checkSelfIntersections(offsetPath).polygon;
bbSurf= getBbSurf(offsetPath);
if(bbSurf > prevBbSurf){
break;
}
prevBbSurf = bbSurf;
contourPath = contourPath.concat( offsetPath );
if(done){
break;
}
// // this can help my find shit at the end but it often fucks up and ends the party prematurely so i think we just leave it off. bounding box check works better
// if(findClockDirection(offsetPath) != clockDir){
// break;
// }
}
// print(offsetPath.length)
return contourPath;
// return offsetPath;
}
function sanitizePath(polygon){
// first check for points that are too close together
let filter1 = [];
for(let i=0; i<polygon.length; i++){
if(abs(polygon[i][0] - polygon[(i+1)%polygon.length][0]) < stepErrorMargin &&
abs(polygon[i][1] - polygon[(i+1)%polygon.length][1]) < stepErrorMargin){
continue;
}
filter1.push([polygon[i][0], polygon[i][1]]);
}
// then for points with bad angles (180 or 360)
let filter2 = [];
for(let i=0; i<filter1.length; i++){
let v0 = createVector( filter1[i][0] - filter1[(filter1.length+i-1)%filter1.length][0],
filter1[i][1] - filter1[(filter1.length+i-1)%filter1.length][1]);
let v1 = createVector( filter1[i][0] - filter1[(i+1)%filter1.length][0],
filter1[i][1] - filter1[(i+1)%filter1.length][1]);
if(abs(v1.angleBetween(v0) - PI) < angleErrorMargin || abs(v1.angleBetween(v0)) < angleErrorMargin){
continue;
}
filter2.push([filter1[i][0], filter1[i][1]]);
}
return filter2;
}
function findClockDirection(polygon){
// i dont have a proof but im pretty sure this returns 1 if a polygon is clockwise and -1 if its counterclockwise for any concave or convex polygon.
// turns out it doesnt. lol. ok sorry i think it does my polygon just started intersecting its damn self (angry)
let totalAngle = 0;
for(let i=0; i<polygon.length; i++){
let v0 = createVector( polygon[i][0] - polygon[(polygon.length+i-1)%polygon.length][0],
polygon[i][1] - polygon[(polygon.length+i-1)%polygon.length][1]);
let v1 = createVector( polygon[i][0] - polygon[(i+1)%polygon.length][0],
polygon[i][1] - polygon[(i+1)%polygon.length][1]);
totalAngle += v1.angleBetween(v0);
}
return Math.sign(totalAngle);
}
function getOffsetPath(polygon, offset, clockDir){
let offsetPath = [];
for(let i=0; i<polygon.length; i++){
// vector going to previous point
let v0 = createVector( polygon[i][0] - polygon[(polygon.length+i-1)%polygon.length][0],
polygon[i][1] - polygon[(polygon.length+i-1)%polygon.length][1]);
// vector to next point
let v1 = createVector( polygon[i][0] - polygon[(i+1)%polygon.length][0],
polygon[i][1] - polygon[(i+1)%polygon.length][1]);
//get the direction
let vDir = p5.Vector.add(v0.setMag(1), v1.setMag(1));
// should it be flipped or no? if its clockwise it should go "to the right", if its counterclockwise it should go "to the left"
if(Math.sign( v1.angleBetween(v0) ) == clockDir){
vDir.mult(-1);
}
vDir.setMag(offset)
offsetPath.push([polygon[i][0] + vDir.x, polygon[i][1] + vDir.y])
}
return offsetPath;
}
function checkSelfIntersections(polygon){
// if(depth > 1){
// return {polygon:polygon, recursive: false}
// }
let intersections = [];
let bottlenecked = false;
for(let i=0; i<polygon.length; i++){
intersections.push([]);
}
let doubleBreak = false;
for(let i=0; i< polygon.length; i++){
let p1 = createVector(polygon[i][0], polygon[i][1]);
let p2 = createVector(polygon[(i+1)%polygon.length][0], polygon[(i+1)%polygon.length][1]);
for(let j=0; j<polygon.length; j++){
//dont check self or neighbours
if(abs(i-j) < 2 || abs(i-j) == polygon.length-1){
continue;
}
let p3 = createVector(polygon[j][0], polygon[j][1]);
let p4 = createVector(polygon[(j+1)%polygon.length][0], polygon[(j+1)%polygon.length][1]);
if(intersects(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y)){
if( abs(p2.x - p3.x) + abs(p2.y-p3.y) < intersectionProximityCheck ){
polygon.splice( i+1 , 1);
polygon = checkSelfIntersections(polygon).polygon;
doubleBreak = true;
break;
}
else{
// i think this means theres a bottleneck
intersections[i].push(j);
bottlenecked = true;
}
}
}
if(doubleBreak){
break;
}
}
if(bottlenecked){
// bottlenecker
let cutPoints = [];
for(let i = 0; i<intersections.length; i++){
if(intersections[i].length == 1 && intersections[i+1].length == 1){
// intersections[i] = [];
intersections[i+1] = [];
cutPoints.push(i+1);
// fill("red")
// circle(polygon[i+1][0],polygon[i+1][1],15)
}
}
// this part cuts up the polygons. the approach was whispered to me by a muse after 12 consecutive hours on this sketch and its really strange. like its actually insane to me that it works. this is the kind of shit mathematicians should be proving
let subPolygons = [ ];
for(let i=0; i<cutPoints.length+1;i++){
subPolygons.push([]);
}
let currentPolygon = 0;
let direction = 1;
for(let i=0; i < polygon.length; i ++){
subPolygons[currentPolygon].push([polygon[i][0], polygon[i][1]]);
if(intersections[i].length == 1){ // when reaching a cutting line
subPolygons[currentPolygon].push([polygon[i+1][0], polygon[i+1][1]]);
if(currentPolygon+direction == subPolygons.length){ direction = -1; }
currentPolygon += direction;
}
if(intersections[i].length > 1){ // when reaching a line that is cut
if(currentPolygon+direction == subPolygons.length){direction = -1;}
currentPolygon += direction * int(intersections[i].length/2);
}
}
// print(intersections)
// print(cutPoints);
// print(subPolygons);
// print(' old polygon: ')
// print(polygon)
polygon = [];
for(let i=0; i<subPolygons.length;i++){
// try{
polygon = polygon.concat(contourFill(subPolygons[i]));
// }
// catch{
// print(subPolygons[i])
// }
}
// print(' new recursive polygon: ')
// print(polygon)
}
// return polygon;
return {polygon: polygon, recursive: bottlenecked};
}
// returns true if the line from (a,b)->(c,d) intersects with (p,q)->(r,s)
// stolen obviously i dont have the time to keep doing intersection math
function intersects(a,b,c,d,p,q,r,s) {
var det, gamma, lambda;
det = (c - a) * (s - q) - (r - p) * (d - b);
if (det === 0) {
return false;
} else {
lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
}
}
function getBbSurf(polygon){
let l = width, t = height, r = 0, b = 0; // left top right bottom
for(let i=0; i<polygon.length; i++){
if(polygon[i][0] > r){
r = polygon[i][0];
}
if(polygon[i][1] > b){
b = polygon[i][1];
}
if(polygon[i][0] < l){
l = polygon[i][0];
}
if(polygon[i][1] < t){
t = polygon[i][1];
}
}
return (r - l) * (b - t);
}