Polygon weaving

Testing my polygon-clipping algorithm of Polygon hatching more.

#polygons

Log in to post a comment.

// Polygon weaving. Created by Reinder Nijhoff 2018
// @reindernijhoff
//
// https://turtletoy.net/turtle/6b7bb60f9d
//

Canvas.setpenopacity(.75);

const turtle = new Turtle();

const num_polygons = 20;
const polygons = [];
const sort_matrix = [];

function walk(i) {
    if (i == 0) {
        for (let x=0; x<=num_polygons; x++) {
            polygons.push(createPolygon(x/num_polygons*200-100, 0, Math.PI/2));
        }
        for (let y=0; y<=num_polygons; y++) {
            polygons.push(createPolygon(0, y/num_polygons*200-100, 0));
        }
        
        // create sort matrix
        for (let j=0; j<polygons.length; j++) {
            sort_matrix[j] = [];
            for (let k=0; k<polygons.length; k++) {
                if (k < j) {
                    sort_matrix[j][k] = !sort_matrix[k][j];
                } else {
                    sort_matrix[j][k] = Math.random() > .5 ? true : false;
                }
            }
        }
    }
    
    // clip polygon to polygons
    const c = polygons[i];
    
    let vis = true;
    for (let j=0; j<polygons.length; j++) {
        if(i != j && sort_matrix[i][j] && !c.boolean(polygons[j])) {
            vis = false;
            break;
        }
    }
    if (vis) {
        c.draw(turtle, 0);
    }
    
    return i < polygons.length - 1;
}

function createPolygon(x, y, a) {
     x += (Math.random()-.5)*2;
     y += (Math.random()-.5)*2;
     a += (Math.random()-.5)*.05;
    const w = Math.random()*2+1;
    
    const cp = [];
    cp.push( [Math.cos(a)*500 - Math.sin(a)*w + x, Math.sin(a)*500 + Math.cos(a)*w + y] );
    cp.push( [Math.cos(a)*500 + Math.sin(a)*w + x, Math.sin(a)*500 - Math.cos(a)*w + y] );
    
    cp.push( [-Math.cos(a)*500 + Math.sin(a)*w + x, -Math.sin(a)*500 - Math.cos(a)*w + y] );
    cp.push( [-Math.cos(a)*500 - Math.sin(a)*w + x, -Math.sin(a)*500 + Math.cos(a)*w + y] );
    
    const p = new Polygon();
    p.cp = cp;
    p.addOutline();
    p.addHatching(Math.random()*2*Math.PI, .4+Math.random()*1.5);
    
    return p;
}

// polygon functions
function LineSegment(p1, p2) {
    this.p1 = p1;
    this.p2 = p2;
}
function Polygon() {
    this.cp = []; // clip path: array of [x,y] pairs
    this.dp = []; // 2d line to draw: array of linesegments
}
Polygon.prototype.addOutline = function() {
    for (let i=0, l=this.cp.length; i<l; i++) {
        this.dp.push(new LineSegment(this.cp[i], this.cp[(i+1)%l]));
    }
}
Polygon.prototype.createPoly = function(x,y,c,r,a) {
    this.cp = [];
    for (let i=0; i<c; i++) {
        this.cp.push( [x + Math.sin(i*Math.PI*2/c+a) * r, y + Math.cos(i*Math.PI*2/c+a) * r] );
    }
}
Polygon.prototype.addHatching = function(a,d) {
    // todo, create a tight bounding polygon, for now fill screen
    const tp = new Polygon();
    tp.createPoly(0,0,4,200,Math.PI*.5);
    const dx = Math.sin(a)*d, dy = Math.cos(a)*d;
    const cx = Math.sin(a)*300, cy = Math.cos(a)*300;
    for (let i = .5; i<300/d; i++) {
        tp.dp.push(new LineSegment([dx*i+cy,dy*i-cx], [dx*i-cy,dy*i+cx]));
        tp.dp.push(new LineSegment([-dx*i+cy,-dy*i-cx], [-dx*i-cy,-dy*i+cx]));
    }
    tp.boolean(this, false);
    this.dp = this.dp.concat(tp.dp);
}
Polygon.prototype.draw = function(t, inp=0) {
    if (this.dp.length ==0) {
        return;
    }
    for (let i=0, l=this.dp.length; i<l; i++) {
        const d = this.dp[i];
        if (!vec2_equal(d.p1, t.pos())) {
            t.penup();
            t.goto([d.p1[0]+inp*(Math.random()-.5), d.p1[1]+inp*(Math.random()-.5)]);
            t.pendown();   
        }
        t.goto([d.p2[0]+inp*(Math.random()-.5), d.p2[1]+inp*(Math.random()-.5)]);
    }
}
Polygon.prototype.inside = function(p) {
    // find number of intersections from p to far away - if even you're outside
    const p1 = [0, -1000];
    let int = 0;
    for (let i=0, l=this.cp.length; i<l; i++) {
        if (vec2_find_segment_intersect(p, p1, this.cp[i], this.cp[(i+1)%l])) {
            int ++;
        }    
    }
    return int & 1;
}
Polygon.prototype.boolean = function(p, diff = true) {
    // very naive polygon diff algorithm - made this up myself
    const ndp = [];
    for (let i=0, l=this.dp.length; i<l; i++) {
        const ls = this.dp[i];
        
        // find all intersections with clip path
        const int = [];
        for (let j=0, cl=p.cp.length; j<cl; j++) {
            const pint = vec2_find_segment_intersect(ls.p1,ls.p2,p.cp[j],p.cp[(j+1)%cl]);
            if (pint) {
                int.push(pint);
            }
        }
        if (int.length == 0) { // 0 intersections, inside or outside?
            if (diff == !p.inside(ls.p1)) {
                ndp.push(ls);
            }
        } else {
            int.push(ls.p1);
            int.push(ls.p2);
            // order intersection points on line ls.p1 to ls.p2
            const cmp = [ls.p2[0]-ls.p1[0], ls.p2[1]-ls.p1[1]];
            int.sort( (a,b) => {
                const db = vec2_dot([b[0]-ls.p1[0], b[1]-ls.p1[1]], cmp);
                const da = vec2_dot([a[0]-ls.p1[0], a[1]-ls.p1[1]], cmp);
                return da - db;
            });
            for (let j=0; j<int.length-1; j++) {
                if (!vec2_equal(int[j], int[j+1])) {
                    if (diff == !p.inside([(int[j][0]+int[j+1][0])/2,(int[j][1]+int[j+1][1])/2])) {
                        ndp.push(new LineSegment(int[j], int[j+1]));
                    }
                }
            }
        }
    }
    this.dp = ndp;
    return this.dp.length > 0;
}

// vec2 functions
function vec2_equal(a,b) {
    return vec2_dist_sqr(a,b) < 0.001;
}
function vec2_dot(a, b) {
    return a[0]*b[0]+a[1]*b[1];
}
function vec2_dist_sqr(a, b) {
    return (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]);
}
//port of http://paulbourke.net/geometry/pointlineplane/Helpers.cs
function vec2_find_segment_intersect(l1p1, l1p2, l2p1, l2p2){
    const d = (l2p2[1] - l2p1[1]) * (l1p2[0] - l1p1[0]) - (l2p2[0] - l2p1[0]) * (l1p2[1] - l1p1[1]);
    const n_a = (l2p2[0] - l2p1[0]) * (l1p1[1] - l2p1[1]) - (l2p2[1] - l2p1[1]) * (l1p1[0] - l2p1[0]);
    const n_b = (l1p2[0] - l1p1[0]) * (l1p1[1] - l2p1[1]) - (l1p2[1] - l1p1[1]) * (l1p1[0] - l2p1[0]);
    if (d == 0) {
        return false;
    }
    const ua = n_a / d;
    const ub = n_b / d;
    if (ua > 0 && ua <= 1 && ub >= 0 && ub < 1) {
        return [l1p1[0] + (ua * (l1p2[0] - l1p1[0])), l1p1[1] + (ua * (l1p2[1] - l1p1[1])) ];
    }
    return false;  
}