xxxxxxxxxx
215
let paths = [];
let offsetPaths = [];
let sMouse = {x:0,y:0};
function setup() {
createCanvas(400, 400);
}
function mousePressed() {
sMouse = {x:mouseX,y:mouseY};
paths = [[]];
}
function draw() {
background(220);
if (mouseIsPressed) {
let f = 0.6;
sMouse = {x: f*sMouse.x + (1-f)*mouseX,
y: f*sMouse.y + (1-f)*mouseY};
paths[0].push(sMouse);
}
noFill();
stroke(200);
strokeWeight(map(mouseX,0,width,0,50));
for (let path of paths) {
beginShape();
for (let pt of path) {
vertex(pt.x,pt.y);
}
endShape();
}
noFill();
stroke(0);
strokeWeight(1);
for (let path of paths) {
beginShape();
for (let pt of path) {
vertex(pt.x,pt.y);
}
endShape();
}
/*for (let i = 0; i < offsetPath.length; i++) {
let p0 = offsetPath[i];
let p1 = offsetPath[(i+1)%offsetPath.length];
if (!p0.cull && !p1.cull) line(p0.x,p0.y, p1.x,p1.y);
}*/
}
function keyPressed() {
let newPaths = [];
for (let path of paths) {
let offsetPath = computeOffsetPath(path, map(mouseX,0,width,0,50) / 2);
let culled = cull(offsetPath);
for (let c of culled) {
newPaths.push(c);
}
}
paths = newPaths;
}
function computeOffsetPath(path, r) {
for (let i = 0; i < path.length; i++) {
for (let j = 1; j < 25; j++) {
let i0 = Math.max(0, i-j);
let i1 = Math.min(path.length - 1, i+j);
path[i].dx = path[i0].x - path[i1].x;
path[i].dy = path[i0].y - path[i1].y;
let m = Math.sqrt(path[i].dx**2 + path[i].dy**2);
if (m !== 0) {
path[i].dx /= m;
path[i].dy /= m;
break;
}
}
}
let offsetPath = [];
for (let pt of path) {
offsetPath.push({x: pt.x + r*pt.dy, y: pt.y - r*pt.dx});
}
let p1 = path[path.length - 1];
let theta1 = Math.atan2(p1.dy, p1.dx);
for (let i = 1; i < 5; i++) {
let theta = map(i, 0, 5, theta1 + PI/2, theta1 - PI/2);
theta += PI;
offsetPath.push({x: p1.x + r*Math.cos(theta), y: p1.y + r*Math.sin(theta)});
}
for (p = path.length - 1; p >= 0; p--) {
let pt = path[p];
offsetPath.push({x: pt.x - r*pt.dy, y: pt.y + r*pt.dx});
}
let p0 = path[0];
let theta0 = Math.atan2(p0.dy, p0.dx);
for (let i = 1; i < 5; i++) {
let theta = map(i, 0, 5, theta0 + PI/2, theta0 - PI/2);
//theta += PI;
offsetPath.push({x: p0.x + r*Math.cos(theta), y: p0.y + r*Math.sin(theta)});
}
console.log('computed rough', offsetPath.length);
let nintersections = 0;
//add intersection points
for (let i = 0; i < offsetPath.length - 1; i++) {
let a = offsetPath[i];
let b = offsetPath[i+1];
for (let j = i+2; j < offsetPath.length - 1; j++) {
let c = offsetPath[j];
let d = offsetPath[j+1];
let I = intersectSegs(a,b,c,d);
if (I !== null) {
I.i = true;
offsetPath.splice(i+1, 0, I);
i++;
j++;
nintersections++;
//copy
I = {x: I.x, y: I.y, i: i.i};
offsetPath.splice(j+1, 0, I);
j++;
nintersections++;
}
}
}
console.log('computed intersections', nintersections);
let nremoved = 0;
//remove all points which are too close to the path
/*for (let pa of paths) {
for (let p of pa) {
for (let i = 0; i < offsetPath.length; i++) {
let op = offsetPath[i];
if (~op.cull && dist(p.x,p.y, op.x,op.y) < 0.95*r) {
op.cull = true;
nremoved++;
}
}
}
}*/
for (let p of path) {
for (let i = 0; i < offsetPath.length; i++) {
let op = offsetPath[i];
if (~op.cull && dist(p.x,p.y, op.x,op.y) < 0.95*r) {
op.cull = true;
nremoved++;
}
}
}
console.log('removed dups', nremoved);
//cyclic
offsetPath.push({x: offsetPath[0].x, y: offsetPath[0].y});
return offsetPath;
}
function cull(path) {
let result = [];
let currentPath = [];
let penDown = true;
for (let i = 0; i < path.length - 1; i++) {
let p0 = path[i];
let p1 = path[i+1];
let include = (!p0.cull && !p1.cull);
if (penDown && !include) {
penDown = false;
currentPath.push(p0);
result.push(currentPath);
currentPath = [];
}
else if (penDown && include) {
currentPath.push(p0);
}
else if (!penDown && include) {
currentPath.push(p0);
penDown = true;
}
}
if (penDown) {
currentPath.push(path[path.length - 1]);
result.push(currentPath);
}
return result;
}
function intersectSegs(a,b,c,d) {
let A = b.x - a.x;
let B = c.x - d.x;
let C = b.y - a.y;
let D = c.y - d.y;
let X = c.x - a.x;
let Y = c.y - a.y;
let det = 1 / (A*D - B*C);
let t1 = det * (D*X - B*Y);
let t2 = det * (-C*X + A*Y);
if (t1 > 0.001 && t1 < 0.999 && t2 > 0.001 && t2 < 0.999) {
return {x: a.x + (b.x - a.x) * t1,
y: a.y + (b.y - a.y) * t1};
}
else {
return null;
}
}