gøy
bbbbb
Log in to post a comment.
const turtle = new Turtle();
const Start_Packing = 1; // min=0, max=1, step=1 (0=Tegn, 1=Pakk)
const isCircle = 0;
const path0 = `M-36,-27 C-30,-34 -19,-36 -11,-35 C-2,-34 6,-28 8,-19 C10,-11 8,-2 1,4 C-5,10 -15,12 -23,10 C-31,8 -37,1 -38,-7 C-39,-15 -35,-23 -29,-28`; // type=path Tegn form 1
const path1 = ``; // type=path Tegn form 2
const path2 = ``; // type=path Tegn form 3
const path3 = ``; // type=path Tegn form 4
const canvas_size = 95;
const grid_cols = 20;
const grid_rows = 20;
const shape_scale = 0.38;
const master_shapes = [];
function cross(a, b) { return a[0]*b[1]-a[1]*b[0]; }
function equal(a,b) { return .001>dist_sqr(a,b); }
function scale(a,b) { return [a[0]*b,a[1]*b]; }
function add(a,b) { return [a[0]+b[0],a[1]+b[1]]; }
function sub(a,b) { return [a[0]-b[0],a[1]-b[1]]; }
function dot(a,b) { return a[0]*b[0]+a[1]*b[1]; }
function dist_sqr(a,b) { return (a[0]-b[0])**2+(a[1]-b[1])**2; }
function transform(a, mat) { return [a[0]*mat[0]+a[1]*mat[2], a[0]*mat[1]+a[1]*mat[3]]; }
class Shape {
constructor(svg) {
this.strokes = getStrokes(svg);
if (this.strokes.length === 0) { this.ready = false; return; }
let allPoints = this.strokes.flat();
if (allPoints.length < 2) { this.ready = false; return; }
const center = scale(allPoints.reduce((a,c) => add(a,c), [0,0]), 1/allPoints.length);
allPoints = allPoints.map(p => sub(p, center));
let r = 0;
allPoints.forEach(p => r = Math.max(r, Math.hypot(p[0], p[1])));
if (r === 0) { this.ready = false; return; }
this.strokes = this.strokes.map(stroke =>
stroke.map(p => scale(sub(p, center), 1/r))
);
this.hull = this.getConvexHull(this.strokes.flat());
this.ready = true;
}
getConvexHull(S) {
S = S.filter((p, i) => i % 4 === 0);
S = [...S].sort((a,b) => a[0] - b[0]);
if (S.length === 0) return [];
let pointOnHull = S[0], endpoint, i = 0;
const P = [];
do {
P[i] = pointOnHull;
endpoint = S[0];
for (let j = 0; j < S.length; j++) {
if (equal(endpoint, pointOnHull) || cross(sub(S[j], P[i]), sub(endpoint, P[i])) < 0) {
endpoint = S[j];
}
}
i++;
pointOnHull = [...endpoint];
} while (!equal(endpoint, P[0]) && i < S.length);
return P;
}
draw(center, size, turtle, polygons) {
const rot = [1, 0, 0, 1];
const poly = polygons.create();
const tr = (p) => add(scale(transform(p, rot), size), center);
poly.addPoints(...this.hull.map(p => tr(p)));
// Hver strek tegnes separat — ingen linje mellom streker
this.strokes.forEach(stroke => {
for (let i = 0; i < stroke.length-1; i++) {
poly.addSegments(tr(stroke[i]), tr(stroke[i+1]));
}
});
polygons.draw(turtle, poly, true);
}
}
// Deler opp path i separate streker ved hver M-kommando
function getStrokes(svg, count = 80) {
const parsed = new Path(svg);
const strokes = [];
parsed.segments.forEach(seg => {
if (seg.constructor.name === 'MoveTo' || seg.type === 'M') {
strokes.push([]);
}
if (strokes.length === 0) strokes.push([]);
const last = strokes[strokes.length-1];
const len = seg.length();
if (len === 0) {
last.push(seg.p(0).slice(0,2));
} else {
const steps = Math.max(2, Math.round(count * len / 200));
for (let i = 0; i <= steps; i++) {
last.push(seg.p(i/steps).slice(0,2));
}
}
});
return strokes.filter(s => s.length > 1);
}
const polygons = new Polygons();
[path0, path1, path2, path3].forEach(p => {
if (p && p.length > 10) {
const s = new Shape(p);
if (s.ready) master_shapes.push(s);
}
});
function walk(i) {
if (Start_Packing === 0 || master_shapes.length === 0) return false;
const total = grid_cols * grid_rows;
if (i >= total) return false;
const col = i % grid_cols;
const row = Math.floor(i / grid_cols);
const cell_w = (canvas_size * 2) / grid_cols;
const cell_h = (canvas_size * 2) / grid_rows;
const x = -canvas_size + cell_w * col + cell_w / 2;
const y = -canvas_size + cell_h * row + cell_h / 2;
const size = Math.min(cell_w, cell_h) * shape_scale;
const shape = master_shapes[Math.floor(Math.random() * master_shapes.length)];
shape.draw([x, y], size, turtle, polygons);
return (i + 1) < total;
}
function Path(svg) {
class MoveTo { constructor(p) { this.p0 = p; this.type='M'; } p(t, s) { return [...this.p0, 1, 0]; } length() { return 0; } }
class LineTo { constructor(p0, p1) { this.p0 = p0, this.p1 = p1; this.type='L'; } p(t, s = 1) { const nt = 1 - t, p0 = this.p0, p1 = this.p1; return [nt*p0[0] + t*p1[0], nt*p0[1] + t*p1[1], (p1[0] - p0[0]) * s, (p1[1] - p0[1]) * s]; } length() { return Math.hypot(this.p0[0]-this.p1[0], this.p0[1]-this.p1[1]); } }
class BezierTo {
constructor(p0, c0, c1, p1) { this.p0=p0; this.c0=c0; this.c1=c1; this.p1=p1; this.type='C'; }
p(t, s=1) {
const nt=1-t, p0=this.p0, c0=this.c0, c1=this.c1, p1=this.p1;
return [
nt*nt*nt*p0[0]+3*t*nt*nt*c0[0]+3*t*t*nt*c1[0]+t*t*t*p1[0],
nt*nt*nt*p0[1]+3*t*nt*nt*c0[1]+3*t*t*nt*c1[1]+t*t*t*p1[1],
(3*nt*nt*(c0[0]-p0[0])+6*t*nt*(c1[0]-c0[0])+3*t*t*(p1[0]-c1[0]))*s,
(3*nt*nt*(c0[1]-p0[1])+6*t*nt*(c1[1]-c0[1])+3*t*t*(p1[1]-c1[1]))*s
];
}
length() { return this._length || (this._length = Array.from({length:25}, (x,i) => this.p(i/25)).reduce((a,c,i,v) => i>0 ? a+Math.hypot(c[0]-v[i-1][0],c[1]-v[i-1][1]) : a, 0)); }
}
class Path {
constructor(svg) { this.segments = []; this.parsePath(svg); }
parsePath(svg) {
const t = svg.match(/([0-9.-]+|[MLC])/g);
if (!t) return;
for (let s, i=0; i<t.length;) {
switch(t[i++]) {
case 'M': this.add(new MoveTo(s=[parseFloat(t[i++]),parseFloat(t[i++])])); break;
case 'L': this.add(new LineTo(s, s=[parseFloat(t[i++]),parseFloat(t[i++])])); break;
case 'C': this.add(new BezierTo(s, [parseFloat(t[i++]),parseFloat(t[i++])], [parseFloat(t[i++]),parseFloat(t[i++])], s=[parseFloat(t[i++]),parseFloat(t[i++])])); break;
}
}
}
add(segment) { this.segments.push(segment); this._length=0; }
length() { return this._length || (this._length = this.segments.reduce((a,c) => a+c.length(), 0)); }
p(t) {
t = Math.max(Math.min(t,1),0)*this.length();
for (let l=0,i=0,sl=0; i<this.segments.length; i++,l+=sl) {
sl=this.segments[i].length();
if (t>=l&&t<=l+sl) return this.segments[i].p(sl===0?0:(t-l)/sl, sl/this.length());
}
return this.segments[this.segments.length-1].p(1);
}
}
return new Path(svg);
}
function Polygons() {
const t=[], s=25;
const e=Array.from({length:s**2}, ()=>[]);
const n=class {
constructor() { this.cp=[]; this.dp=[]; this.aabb=[]; }
addPoints(...t) {
let s=1e5,e=-1e5,n=1e5,h=-1e5;
(this.cp=[...this.cp,...t]).forEach(t=>{s=Math.min(s,t[0]);e=Math.max(e,t[0]);n=Math.min(n,t[1]);h=Math.max(h,t[1]);});
this.aabb=[s,n,e,h];
}
addSegments(...t) { t.forEach(t=>this.dp.push(t)); }
draw(t) { for (let s=0,e=this.dp.length;s<e;s+=2) t.jump(this.dp[s]),t.goto(this.dp[s+1]); }
inside(t) {
let s=0;
for (let e=0,n=this.cp.length;e<n;e++) this.segment_intersect(t,[.1,-1e3],this.cp[e],this.cp[(e+1)%n])&&s++;
return 1&s;
}
boolean(t,s=true) {
const e=[];
for (let n=0,h=this.dp.length;n<h;n+=2) {
const h=this.dp[n],o=this.dp[n+1],a=[];
for (let s=0,e=t.cp.length;s<e;s++) { const n=this.segment_intersect(h,o,t.cp[s],t.cp[(s+1)%e]); if(n!==false)a.push(n); }
if(a.length===0) { s===!t.inside(h)&&e.push(h,o); }
else {
a.push(h,o);
const n=o[0]-h[0],i=o[1]-h[1];
a.sort((t,s)=>(t[0]-h[0])*n+(t[1]-h[1])*i-(s[0]-h[0])*n-(s[1]-h[1])*i);
for (let n=0;n<a.length-1;n++) (a[n][0]-a[n+1][0])**2+(a[n][1]-a[n+1][1])**2>=.001&&s===!t.inside([(a[n][0]+a[n+1][0])/2,(a[n][1]+a[n+1][1])/2])&&e.push(a[n],a[n+1]);
}
}
return (this.dp=e).length>0;
}
segment_intersect(t,s,e,n) {
const h=(n[1]-e[1])*(s[0]-t[0])-(n[0]-e[0])*(s[1]-t[1]);
if(h===0)return false;
const o=((n[0]-e[0])*(t[1]-e[1])-(n[1]-e[1])*(t[0]-e[0]))/h;
const a=((s[0]-t[0])*(t[1]-e[1])-(s[1]-t[1])*(t[0]-e[0]))/h;
return o>=0&&o<=1&&a>=0&&a<=1&&[t[0]+o*(s[0]-t[0]),t[1]+o*(s[1]-t[1])];
}
};
return {
create: ()=>new n,
draw: (n,h,o=true)=>{
let r=(function(n){const h={},o=200/s;for(var a=0;a<s;a++){const c=a*o-100,r=[0,c,200,c+o];if(n[3]<r[1]||n[1]>r[3])continue;for(var i=0;i<s;i++){const c=i*o-100;r[0]=c;r[2]=c+o;if(n[0]>r[2]||n[2]<r[0])continue;e[i+a*s].forEach(s=>{const e=t[s];if(n[3]<e.aabb[1]||n[1]>e.aabb[3]||n[0]>e.aabb[2]||n[2]<e.aabb[0])return;h[s]=1;});}}return Array.from(Object.keys(h),s=>t[s]);})(h.aabb);
for(let t=0;t<r.length&&h.boolean(r[t]);t++);
h.draw(n);
if(o){t.push(h);const a=t.length-1,c=200/s;e.forEach((t,e)=>{const n=e%s*c-100,i=(e/s|0)*c-100,r=[n,i,n+c,i+c];if(r[3]<h.aabb[1]||r[1]>h.aabb[3]||r[0]>h.aabb[2]||r[2]<h.aabb[0])return;t.push(a);});}
}
};
}