xxxxxxxxxx
532
// shoutout mohamad aljanabi https://youtu.be/Cv7Sbuuo2X8
// ok so now the hard part: turning the lines into uninterrupted polygons
// and then from that it makes polygon arrays for the actual tiles, could be similar to the voronoi construction method, along with an array of coordinates/rotation
// ok where to start? multiple approaches:
// in the corner then check for neighbours until all fall outside the square?
// for each construction point: does it fall within or border a polygon all around?
// calculate intersection points, save shortest path segments, loop through line + intersection points to find polygons
// then calculate the polygons:
// follow in one direction
// check for intersections
// then the function that draws all of them
// basically my ratios. maybe i can tweak em
const gp1 = 25;
const gp2 = 50;
let gp3 = 10; //i think maybe actually this is the only one i can tweak without breaking 45 degree relations (and like; everything)
let startingPoint = [0, 100];
let conPts = [];
let lPts = [], lPts2 = [];
let segs = [], segs2 = [];
let intersections = [];
let prevDir;
let errorBreaker = 0;
let slider;
let frameCounter = 0;
function setup() {
createCanvas(400, 400);
strokeWeight(3);
noFill();
strokeCap(SQUARE);
slider = createSlider(1, 20, 1);
slider.style("width", width + "px");
createButton("regenerate")
.mousePressed(regen)
.position(10, height + 30);
createButton("save png")
.mousePressed(savePNG)
.position(90, height + 30);
createButton("save pattern data")
.mousePressed(saveCSV)
.position(160, height + 30);
regen();
}
function regen() {
constructionPoints();
// ok now it has to actually make the lines between them lmao
// traditional tiling: mirror hor and vert to make a tile,
// rules:
// 1. diagonal symmetry: R should match B and T should match L.
// 2. prevent infinite tiles: this happens when there is an uninterrupted connection between T&B or L&R.
// 3. no 180 degree turns!
// 4. (optional) no full horizontal or vertical lines
// method 1: draw a line from bottom left corner through some points, bounce when it hits a normal wall and end when it hits the left wall, it should touch or cross the diagonal at least once (r2) -> do not go left until the diagonal is hit
// then mirror over diagonal(r1), derive tiles from intersections.
generatePath();
print('points on line: ' + lPts.length);
findIntersections();
// calculatePolygons();
}
function constructionPoints() {
// generate construction points
conPts = [];
for (let i = gp1; i < gp1 * 16; i += 2 * gp1) {
let startx = floor(i/100) * gp1;
let starty = (i % (gp1 * 8)) % (gp1 * 5);
conPts.push(
[startx + gp3, starty],
[startx - gp3, starty],
[startx, starty + gp3],
[startx, starty - gp3]
);
}
// move points around a bit
for (let i = 0; i < conPts.length * 2; i++) {
if (conPts[floor(i / 2)][i % 2] < 0) {
conPts[floor(i / 2)][i % 2] = conPts[floor(i / 2)][i % 2] + 100;
} else if (conPts[floor(i / 2)][i % 2] == 0) {
conPts.push([0.01, 0.01]);
conPts[conPts.length - 1][i % 2] = conPts[floor(i / 2)][i % 2] + 100;
conPts[conPts.length - 1][1 - (i % 2)] =
conPts[floor(i / 2)][1 - (i % 2)];
}
}
conPts.push([0, 0], [0, 100], [100, 0], [100, 100]); //add the corners
}
function generatePath() {
let hitDia = false;
lPts = [startingPoint];
for (let i = 0; i < 20; i++) {
// down, right, up, left, sw, nw, ne, se
let dirs = [[], [], [], [], [], [], [], []];
// check availabile jumps
for (let j = 0; j < conPts.length; j++) {
// check orth directions
for (let k = 0; k < 2; k++) {
if (conPts[j][k] == lPts[i][k] && conPts[j][1 - k] != lPts[i][1 - k]) {
//x, y
dirs[k + 1 + Math.sign(lPts[i][1 - k] - conPts[j][1 - k])].push(j);
}
}
// check diagonal directions
for (let k = -1; k < 2; k += 2) {
if (lPts[i][0] - conPts[j][0] == k * (lPts[i][1] - conPts[j][1]) &&
conPts[j][0] != lPts[i][0] ) {
dirs[5 + (1 + k) / 2 + Math.sign(conPts[j][0] - lPts[i][0])].push(j);
}
}
}
// choose direction
let dir = findDir(dirs, hitDia);
let newpoint = conPts[dirs[dir][floor(random(0, dirs[dir].length))]];
errorBreaker = 0;
while(abs(lPts[i][0] - newpoint[0]) == 100 ||
abs(lPts[i][1] - newpoint[1]) == 100){
if(dirs[dir].length == 1){
dir = findDir(dirs, hitDia);
}
newpoint = conPts[dirs[dir][floor(random(0, dirs[dir].length))]];
errorBreaker++;
if (errorBreaker > 50) {
print("error 2!");
print(lPts[i], dirs);
break;
}
}
prevDir = dir;
lPts.push([newpoint[0], newpoint[1]]);
if (lPts[i + 1][0] > lPts[i + 1][1]) {
hitDia = true;
}
if (hitDia && lPts[i + 1][0] == 0) {
break;
}
}
lPts2 = [];
for (let i = 0; i < lPts.length; i++) {
lPts2.push([lPts[i][1], lPts[i][0]]);
}
}
function findDir(dirs, hitDia){
let dir = floor(random(0, 8));
errorBreaker = 0;
while (
(dir >= 3 && dir <= 5 && hitDia == false) ||
dirs[dir].length == 0 ||
dir % 4 == (prevDir + 2) % 4 ||
dir == prevDir
) {
dir = floor(random(0, 8));
errorBreaker++;
if (errorBreaker > 50) {
print("error 1!");
break;
}
}
return dir;
}
function findIntersections(){
// find intersections between lines and append
// cut em up into shortest segments
// going through one line i should be able to find all possible intersections: with the mirror occurs once and with itself we can mirror over diagonal
// kinda fucked up approach 1: cut it all up into line segments (essentially points are already line segments), for each line segment check for all other line segments if they intersect.
segs = [];
//turn them all into segments
for(let i=0; i<lPts.length-1; i++){
segs.push([lPts[i].slice(),lPts[i+1].slice()])
segs.push([lPts2[i].slice(),lPts2[i+1].slice()])
}
print('amount of segments initially: ' + segs.length)
//find all the intersections
let currentLength = segs.length;
for(let i=0; i<currentLength; i++){
//check intersections
for(let j=0;j<currentLength;j++){
// check self intersections
let intersection = intersect (segs[i], segs[j])
if(intersection &&
!(segs[i][0][0] == intersection[0] && segs[i][0][1] == intersection[1]) &&
!(segs[i][1][0] == intersection[0] && segs[i][1][1] == intersection[1])){
let coords = [segs[i][0],intersection]
segs.push(coords);
}
}
}
print('amount of segments after intersect: ' + segs.length)
//cut up overlapping ones, this function is so fucking bad holy shit also it creates wayyy too many
currentLength = segs.length;
for(let i=0;i<currentLength;i++){
for(let j=i+1;j<currentLength;j++){
if(overlap(segs[i],segs[j]) &&
!(segs[i][0][0] == segs[j][0][0] && segs[i][0][1] == segs[j][0][1] &&
segs[i][1][0] == segs[j][1][0] && segs[i][1][1] == segs[j][1][1]) &&
!(segs[i][0][0] == segs[j][1][0] && segs[i][0][1] == segs[j][1][1] &&
segs[i][1][0] == segs[j][0][0] && segs[i][1][1] == segs[j][0][1]) ){
//check if vertical
let dir = 0;
if( segs[i][0][0] == segs[i][1][0]){
dir = 1;
}
let orderedSegmentPoints = [];
let orderingArray = [ [segs[i][0][0],segs[i][0][1]],
[segs[i][1][0],segs[i][1][1]],
[segs[j][0][0],segs[j][0][1]],
[segs[j][1][0],segs[j][1][1]]];
let orderingArray2 = [segs[i][0][dir],
segs[i][1][dir],
segs[j][0][dir],
segs[j][1][dir]];
for(let k=0;k<4;k++){
for(let l=0;l<orderingArray.length;l++){
if(orderingArray2[l] == min(orderingArray2)){
orderedSegmentPoints.push(orderingArray[l].slice())
orderingArray.splice(l,1);
orderingArray2.splice(l,1);
break;
}
}
}
for(let k=0;k<3;k++){
if(orderedSegmentPoints[k][dir] != orderedSegmentPoints[k+1][dir]){
segs.push([orderedSegmentPoints[k].slice(), orderedSegmentPoints[k+1].slice()])
}
}
}
}
}
print('amount of segments after cutting overlap: ' + segs.length)
//filter duplicates/overlap
let duplicates = 0
for(let i=0;i<segs.length;i++){
for(let j=0;j<segs.length;j++){
if(i!=j && overlap(segs[i],segs[j])){
duplicates++
// delete the longest one!!!!
let segLi = (segs[i][0][0]-segs[i][1][0])**2 + (segs[i][0][1]-segs[i][1][1])**2 ;
let segLj = (segs[j][0][0]-segs[j][1][0])**2 + (segs[j][0][1]-segs[j][1][1])**2 ;
if(segLi > segLj){
segs.splice(i,1)
i--;
break;
}
if(segLi <= segLj){
segs.splice(j,1)
if(j<i){
i--;
}
j--;
}
}
}
}
print('duplicates: ' + duplicates)
print('amount of segments after pruning duplicates: ' + segs.length)
print('')
}
function intersect(c1,c2){
// how to find a single intersection?
// -> between two lines? between a line and every other line? between my dick and your ass, cockboy?
// bunch of stolen code
let cmP = [c2[0][0] - c1[0][0], c2[0][1] - c1[0][1]];
let r = [c1[1][0] - c1[0][0], c1[1][1] - c1[0][1]];
let s = [c2[1][0] - c2[0][0], c2[1][1] - c2[0][1]];
let cmPxr = cmP[0] * r[1] - cmP[1] * r[0];
let cmPxs = cmP[0] * s[1] - cmP[1] * s[0];
let rxs = r[0] * s[1] - r[1] * s[0];
// lines are collinear or parallel
if (cmPxr == 0 || rxs == 0) {
return false;
}
let rxsr = 1 / rxs;
let t = cmPxs * rxsr;
let u = cmPxr * rxsr;
if((t < 0) || (t > 1) || (u < 0) || (u > 1)){
return false
}
let inxs = [ round(c1[0][0] + t * r[0]) , round(c1[0][1] + t * r[1]) ]
return inxs
}
function overlap(c1, c2){
// bunch of stolen code
let cmP = [c2[0][0] - c1[0][0], c2[0][1] - c1[0][1]];
let r = [c1[1][0] - c1[0][0], c1[1][1] - c1[0][1]];
let s = [c2[1][0] - c2[0][0], c2[1][1] - c2[0][1]];
let cmPxr = cmP[0] * r[1] - cmP[1] * r[0];
let rxs = r[0] * s[1] - r[1] * s[0];
if (cmPxr != 0 || rxs != 0) {
return false;
}
//check if vertical
let dir = 0;
if(c1[0][0] == c1[1][0]){
dir = 1;
}
if(max(c1[0][dir],c1[1][dir]) > min(c2[0][dir],c2[1][dir]) &&
max(c2[0][dir],c2[1][dir]) > min(c1[0][dir],c1[1][dir]) ){
return true;
}
return false;
}
function calculatePolygons(){
// ok so now we have 3 arrays, one with all the points of one line, then all mirrored and then all intersections
// i think this is the algorithm for finding a polygon:
// 1. start at a point we know to be on a path and start walking in a random direction
// 2. for each point, pick the direction that is MOST CLOCKWISE from all possible line segments ()
// we know we're done when each line segment is walked TWICE and only ONCE PER POLYGON. with the exception of segments on or outside the border which are walked ONCE IN TOTAL.
//ok so to start doing this we dont rly want 2 lines + intersections we just want all line segments basically unarranged. findIntersections shouldnt write a list of intersections but a list of line segments that are all [[x1,y1],[x2,y2]]
}
// DRAW FUNCTIONS
function draw() {
background(0);
// frameCounter+= 1;
angleMode(DEGREES);
// drawConstructionPoints();
drawSegments(segs);
// stroke(255)
// let i = floor(frameCounter%segs.length)
// let drawseg = [ [segs[i][0][0] * width / 100, segs[i][0][1] * height / 100] , [segs[i][1][0] * width / 100, segs[i][1][1] * height / 100] ];
// strokeWeight(3)
// line(drawseg[0][0], drawseg[0][1], drawseg[1][0], drawseg[1][1] )
}
function drawConstructionPoints(){
for(let i=0;i<conPts.length;i++){
stroke(255)
strokeWeight(3)
point(conPts[i][0] * width / 100, conPts[i][1] * height / 100);
noStroke()
fill(255)
text(i, conPts[i][0] * width / 100, conPts[i][1] * height / 100)
noFill()
stroke(255)
strokeWeight(3)
}
}
function drawSegments(segs, col=false){
for(let i=0;i<segs.length;i++){
if(col){stroke(col)}
else{stroke(55+(i*92)%200,55+(i*42)%200,55+(i*64)%200,200);}
let drawseg = [ [segs[i][0][0] * width / 100, segs[i][0][1] * height / 100] , [segs[i][1][0] * width / 100, segs[i][1][1] * height / 100] ];
strokeWeight(3)
line(drawseg[0][0], drawseg[0][1], drawseg[1][0], drawseg[1][1] )
// stroke(255)
// strokeWeight(1)
// line(drawseg[0][0] - 5, drawseg[0][1], drawseg[0][0] + 5,drawseg[0][1])
// line(drawseg[0][0], drawseg[0][1] - 5, drawseg[0][0],drawseg[0][1] + 5)
// line(drawseg[1][0] - 5, drawseg[1][1], drawseg[1][0] + 5,drawseg[1][1])
// line(drawseg[1][0], drawseg[1][1] - 5, drawseg[1][0],drawseg[1][1] + 5)
}
}
// BONUS FEATURES I DONT CARE
function savePNG(){
saveCanvas()
}
function saveCSV(){
let data = new p5.Table();
data.addColumn('x');
data.addColumn('y');
for(let i=0;i<lPts.length;i++){
let newRow = data.addRow();
newRow.setNum('x', lPts[i][0]);
newRow.setNum('y', lPts[i][1]);
}
for(let i=0;i<lPts2.length;i++){
let newRow = data.addRow();
newRow.setNum('x', lPts2[i][0]);
newRow.setNum('y', lPts2[i][1]);
}
saveTable(data, 'pattern.csv');
}
// old code im not using but dont want to throw away yet:
// for(let i=0; i<lPts.length-1; i++){
// // store coords and slope
// let c1 = [lPts[i], lPts[i+1]];
// let s1 = (c1[0][1]-c1[1][1]) / (c1[0][0]-c1[1][0]);
// //check intersections
// for(let j=0;j<lPts.length-1;j++){
// // check self intersections
// let coord = intersect ( c1, [lPts[j], lPts[j+1]] )
// if( coord && selfCheck(coord) ){
// let coords = [segs[segs.length-1][1],coord]
// segs.push(coords);
// segsLengths.push( (coords[0][0]-coords[1][0])**2 + (coords[0][1]-coords[1][1])**2 );
// }
// // check mirror intersections
// coord = intersect ( c1, [lPts2[j], lPts2[j+1]] );
// if( coord && selfCheck(coord) ){
// segs.push([segs[segs.length-1][1],coord])
// }
// }
// if(selfCheck(c1[1]) ){
// segs.push([ segs[segs.length-1][1], c1[1] ])
// }
// }
// segs.shift();