xxxxxxxxxx
416
// 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 = [];
let lPts2 = [];
let intersections = [];
let prevDir;
let errorBreaker = 0;
let slider;
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();
findIntersections();
// calculatePolygons();
print(lPts.length);
}
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.
intersections = [];
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]);
for(let j=0;j<lPts.length-1;j++){
// check self intersections
let coord = intersect ( c1, [lPts[j], lPts[j+1]] )
if( coord ){
if(checkDuplicate([coord[0],coord[1]])){
intersections.push(coord)
intersections.push([coord[1],coord[0]])
}
}
// check mirror intersections
coord = intersect ( c1, [lPts2[j], lPts2[j+1]] );
if( coord ){
if(checkDuplicate([coord[0],coord[1]])){
intersections.push(coord)
}
}
}
}
print(intersections);
}
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 = [ c1[0][0] + t * r[0] , c1[0][1] + t * r[1] ]
if(( inxs[0] == c1[0][0] && inxs[1] == c1[0][1] ) ||
( inxs[0] == c1[1][0] && inxs[1] == c1[1][1] ) ||
( inxs[0] == c2[0][0] && inxs[1] == c2[0][1] ) ||
( inxs[0] == c2[1][0] && inxs[1] == c2[1][1] ) ){
print('potentially obsolete check')
return false
}
return inxs
}
function checkDuplicate(coord){
for(let i=0;i<intersections.length-1;i++){
if(intersections[i][0]==coord[0] && intersections[i][1]==coord[1]){
return false
}
}
return true
}
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
}
// DRAW FUNCTIONS
function draw() {
background(60);
angleMode(DEGREES);
// // draw construction points
// 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)
// }
let tile = slider.value();
let size = width / tile;
for (let j = 0; j < tile; j++) {
for (let k = 0; k < tile; k++) {
translate(k * 2 * size, j * 2 * size);
for (let i = 0; i < 4; i++) {
drawShape(size/100);
rotate(90);
translate(0, -2 * size);
}
translate(-k * 2 * size, -j * 2 * size);
}
}
// draw intersections
strokeWeight(1)
stroke(0,255,0)
for(let i=0;i<intersections.length;i++){
drawMarker(intersections[i][0] * width / 100, intersections[i][1] * height / 100, 10);
}
strokeWeight(3)
stroke(255)
}
function drawShape(size) {
strokeWeight(4)
stroke(255)
beginShape();
for (let i = 0; i < lPts.length; i++) {
vertex(lPts[i][0] * size, lPts[i][1] * size);
}
endShape();
strokeWeight(2)
stroke(175)
beginShape();
for (let i = 0; i < lPts2.length; i++) {
vertex(lPts2[i][0] * size, lPts2[i][1] * size);
}
endShape();
}
function drawMarker(x,y,l){
line(x-l,y,x+l,y);
line(x,y-l,x,y+l);
}
// 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');
}