* Pilea Peperomioides
* Pannekoekenplant
Log in to post a comment.
// Forked from "Ellipse" by markknol // https://turtletoy.net/turtle/88914e0941 const turtle = new Turtle(); const polygons = new Polygons(); let minRadius = 8; // min=2, max=40, step=1 let maxRadius = 18; // min=2, max=40, step=1 const segments = 20; let useHatching = 0; // min=0, max=1, step=1 let usePolygons = 1; // min=0, max=1, step=1 let debug = 0; // min=0, max=1, step=1 function walk(i) { let order0 = []; let order1 = []; let order2 = []; let order3 = []; let spreadX = 50; let spreadY = [10, -40]; let trunkY = [range(-50,-20), 10]; let total = 8; // min=0, max=20, step=1 for(let ii=0; ii<total/2;ii++) { let t = ii/(total/2); let baseX = lerp(-1,1,t); let baseY = range(trunkY[0], trunkY[1]); let baseSize = range(minRadius, maxRadius); let sizeScale = lerp(0.25, 1.0, baseSize/Math.max(minRadius,maxRadius)); let x = lerp(-spreadX*sizeScale,spreadX*sizeScale, t+range(-0.1,0.1)); let y = baseY + lerp(spreadY[0],spreadY[1],1-(Math.random()*Math.random())); let radiusX = baseSize;//lerp(baseSize/2,baseSize, pingpong(t+range(-0.1,0.1))); let radiusY = baseSize*1.2;//lerp(13,13, pingpong(t)); let angle = lerp(1, -1, t);// + range(-1,1)); order1.push(() => drawStem([[baseX, baseY], [lerp(baseX,x,lerp(0.5, 0.25, pingpong(t))), lerp(baseY,y,0.5) + range(y > 0 ? 30 : -30, 0)], [x,y]])); order1.push(() => drawLeaf(x, y, radiusX, radiusY, angle, segments)); } let trunk = () => drawStem([[0, trunkY[0]], [0, trunkY[1]]], 3); for(let ii=0; ii<total/2;ii++) { let t = ii/(total/2); let baseX = lerp(-1,1,t); let baseY = range(trunkY[0], trunkY[1]); let baseSize = range(minRadius, maxRadius); let sizeScale = lerp(0.25, 1.0, baseSize/Math.max(minRadius,maxRadius)); let x = lerp(-spreadX*sizeScale,spreadX*sizeScale, t+range(-0.1,0.1)); let y = baseY + lerp(spreadY[0],spreadY[1], 1-(Math.random()*Math.random())); let radiusX = baseSize;//lerp(baseSize/2,baseSize, pingpong(t+range(-0.1,0.1))); let radiusY = baseSize*1.2;//lerp(13,13, pingpong(t)); let angle = lerp(1, -1, t);// + range(-1,1)); order2.push(() => drawStem([[baseX, baseY], [lerp(baseX,x,lerp(0.5, 0.25, pingpong(t))), lerp(baseY,y,0.5) + range(y > 0 ? 30 : -30, 0)], [x,y]])); order3.push(() => drawLeaf(x, y, radiusX, radiusY, angle, segments)); } // lol, poor man depth sorting let shifts = order3.length/2|0; while(shifts--) { order3.unshift(order3.pop()) order2.unshift(order2.pop()) order1.unshift(order1.pop()) order0.unshift(order0.pop()) } let order = [ ...order3, () => drawPot(0,0,35,66), ...order2, trunk, ...order1 ]; order.forEach(cb=>cb()); } function drawLeaf(x = 0, y = 0, radiusX = 40, radiusY = 20, angle = 0, segments = 15) { let path = ellipse(x,y,radiusX,radiusY,angle,segments); let hatchAngle = Math.atan2(y,x); const precision = 6; const smoothness = 4; let transformers = [ closePath, path => useHatching ? crossHatch(path, 0) : null, path => drawPoints(path, useHatching ? [hatchAngle,0.5] : null), path => debug ? drawDebugCircles(path) : null, ]; transform(path, transformers); } function drawPot(x,y,w,h, slant = 0.7,top=0.2) { let path = [ [x - w, y], [x + w, y], [x + w, y+h*top], [x + w*slant, y+h], [x - w*slant, y+h], [x - w, y+h*top], ]; const precision = 6; const smoothness = 4; let transformers = [ path => normalizePath(path, precision, true), //path => smoothPath(path, smoothness, true, false), closePath, path => drawPoints(path, [0, 2.5]), path => debug ? drawDebugCircles(path) : null, ]; transform(path, transformers); } function drawStem(path, lineWidth = 1.25) { const precision = 6; const smoothness = 4; let transformers = [ path => normalizePath(path, precision, true), path => smoothPath(path, smoothness, false, true), path => extrude(path, r => lerp(lineWidth/4, lineWidth, clamp(inverseLerp(1, 0.9, r))), true), closePath, path => useHatching ? crossHatch(path, 1) : null, path => drawPoints(path), path => debug ? drawDebugCircles(path) : null, ]; transform(path, transformers); } function crossHatch(pts, mode = 0) { let halfLength = (pts.length/2)|0; pts.forEach((p,i) => { if (i < halfLength) { let p1 = pts[i]; let p2 = mode ? pts[pts.length - i] : pts[(i+halfLength)%pts.length]; if (p1&&p2) drawPoints([p1,p2]); } }); } function drawPoints(pts, hatch1, hatch2) { if (usePolygons) { let p = polygons.create(); p.addPoints(...pts); p.addOutline(); if (hatch1) p.addHatching(...hatch1); if (hatch2) p.addHatching(...hatch2); polygons.draw(turtle,p); } else { pts.forEach((p,i) => i==0?turtle.jump(p):turtle.goto(p)); } } function drawDebugCircles(pts,radius = 0.75) { pts.forEach(p => circle(p, radius)); } function circle(p, radius = 1) { turtle.jump(p[0],p[1]-radius); turtle.circle(radius); } function ellipse(x = 0, y = 0, radiusX = 40, radiusY = 20, angle = 0, segments = 15) { return Array.from({length: segments}, (_, i) => (i / segments) * Math.PI*2).map(ang => ([ x - (radiusY * Math.sin(ang)) * Math.sin(angle) + (radiusX * Math.cos(ang)) * Math.cos(angle), y + (radiusX * Math.cos(ang)) * Math.sin(angle) + (radiusY * Math.sin(ang)) * Math.cos(angle), ])); } function inverseLerp(a, b, t) { return (t - a) / (b - a); } function lerp(a,b,t) { return a+(b-a)*t; } function range(a,b) { return lerp(a,b,Math.random()); } function clamp(v, a=0,b=1) { [a,b] = [Math.min(a,b), Math.max(b,a)]; return v<a ? a : (v > b ? b : v); } function pingpong(t) { return (t > 0.5 ? 1 - t : t) * 2; } function transform(path, transformers) { transformers.forEach(transformer => { let transformed = transformer(path); if (transformed) path = transformed; // prevent transforms returning void }); return path; } function normalizePath(path, minDist = 1, keepCorners = false) { const newPath = []; for (let i = 1; i < path.length; i++) { let prev = clone(path[i-1]); let curr = clone(path[i]); let delta = sub(curr, prev); let l = len(delta); let a = atan2(delta); if (keepCorners) newPath.push(clone(prev)); while (l >= minDist) { moveTo(prev, a, minDist); newPath.push(clone(prev)); l -= minDist; } } newPath.push(path[path.length-1]); return newPath; } function smoothPath(path, smoothness = 2, wrap = true, keepStartEnd = false) { let pp = clone(path) if (!smoothness) return path; let prevPos = null; for (let smooth = 0; smooth < smoothness; smooth++) { let prevPos; let nextPos path.forEach((pos, idx) => { if (idx > 0) { if (wrap) { prevPos = idx === 0 ? path[path.length-1] : path[idx-1]; nextPos = idx === path.length-1 ? path[0] : path[idx+1]; } else { prevPos = idx === 0 ? path[idx] : path[idx-1]; nextPos = idx === path.length-1 ? path[path.length-1] : path[idx+1]; } path[idx] = scl(add(add(prevPos, pos), nextPos), 1/3); } }); } if (!keepStartEnd) return path; else return [pp[0],...path, pp[pp.length-1]]; } function extrudePathSide(newPath, path, thickness, forward) { path.forEach((curr, idx) => { let t = thickness(forward ? idx / path.length : 1 - idx / path.length); if (forward) t *= -1; let next = path[idx + 1]; let diff; if (idx == 0) { diff = sub(next, curr); } else { let prev = path[idx - 1]; if (!next) next = curr; diff = sub(next, prev); } let dir = atan2(diff) + (Math.PI * 0.5 * (!forward ? -1 : 1)); newPath.push(moveTo(clone(curr), dir, t)); }); } function extrude(path, thickness, merged = false) { if (thickness === 0 || path.length < 2) return path; const newPath1 = []; extrudePathSide(newPath1, path, thickness, true); path = path.reverse(); const newPath2 = []; extrudePathSide(newPath2, path, thickness, false); return merged ? [...newPath1, ...newPath2] : [newPath1, newPath2]; } function closePath(path) { if (!isClosedPath(path)) path.push(path[0]); return path; } function isClosedPath(path) { return path[0] === path[path.length-1]; } function moveTo(a, angle, distance) { a[0] += Math.cos(angle) * distance; a[1] += Math.sin(angle) * distance; return a; } function vec2(a) { return [a,a]; } function scl(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 len(a) { return Math.sqrt(a[0]**2 + a[1]**2); } function nrm(a) { return scl(a, 1/len(a)); } function lrp(a,b,f) { return [a[0]*f+b[0]*(1-f), a[1]*f+b[1]*(1-f)]; } function atan2(a) { return Math.atan2(a[1], a[0]); } function eql(a,b) { return a[0]==b[0] && a[1]==b[1]; } function clone(a) { return [...a]; } //////////////////////////////////////////////////////////////// // Polygon Clipping utility code - Created by Reinder Nijhoff 2019 // (Polygon binning by Lionel Lemarie 2021) // https://turtletoy.net/turtle/a5befa1f8d //////////////////////////////////////////////////////////////// function Polygons(){const t=[],s=25,e=Array.from({length:s**2},t=>[]),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))}addOutline(){for(let t=0,s=this.cp.length;t<s;t++)this.dp.push(this.cp[t],this.cp[(t+1)%s])}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])}addHatching(t,s){const e=new n;e.cp.push([-1e5,-1e5],[1e5,-1e5],[1e5,1e5],[-1e5,1e5]);const h=Math.sin(t)*s,o=Math.cos(t)*s,a=200*Math.sin(t),i=200*Math.cos(t);for(let t=.5;t<150/s;t++)e.dp.push([h*t+i,o*t-a],[h*t-i,o*t+a]),e.dp.push([-h*t+i,-o*t-a],[-h*t-i,-o*t+a]);e.boolean(this,!1),this.dp=[...this.dp,...e.dp]}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=!0){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]);!1!==n&&a.push(n)}if(0===a.length)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(0===h)return!1;const o=((n[0]-e[0])*(t[1]-e[1])-(n[1]-e[1])*(t[0]-e[0]))/h,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{list:()=>t,create:()=>new n,draw:(n,h,o=!0)=>{reducedPolygonList=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]))for(var i=0;i<s;i++){const c=i*o-100;r[0]=c,r[2]=c+o,n[0]>r[2]||n[2]<r[0]||e[i+a*s].forEach(s=>{const e=t[s];n[3]<e.aabb[1]||n[1]>e.aabb[3]||n[0]>e.aabb[2]||n[2]<e.aabb[0]||(h[s]=1)})}}return Array.from(Object.keys(h),s=>t[s])}(h.aabb);for(let t=0;t<reducedPolygonList.length&&h.boolean(reducedPolygonList[t]);t++);h.draw(n),o&&function(n){t.push(n);const h=t.length-1,o=200/s;e.forEach((t,e)=>{const a=e%s*o-100,i=(e/s|0)*o-100,c=[a,i,a+o,i+o];c[3]<n.aabb[1]||c[1]>n.aabb[3]||c[0]>n.aabb[2]||c[2]<n.aabb[0]||t.push(h)})}(h)}}}