rain
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); let a1 = 0; // min = -720, max=720, step=1 let a2 = 46; // min = -90, max=90, step=1 let cd = 71.9; // min = 0, max=360, step=0.1 let perspective = 1; // min=0, max=1, step=1, (off, on) let viewSize = 200; // min=10, max=400, step=1 let fov = 90; // min=10, max=160, step=0.1 let radius = 109; // min = 10, max=200, step=1 let amount = 9800; // min=0, max=50000, step=1 let rainLen = 2.2; // min=0, max=100, step=0.1 let circleSize = 3.8; // min=0, max=30, step=0.1 let boxSize = 27.6; // min=1, max=50, step=0.1 function circle(p, r) { let loop = []; const STEPS =32; for (let i=0; i<STEPS; i++) { let a = Math.PI * 2 * i / STEPS; let p2 = new V3(Math.sin(a), Math.cos(a)); loop.push(p2.mul(r).add(p)); } return Ob.fromLoop(loop); } // The walk function will be called until it returns false. function walk(i) { initlib(); let scene = new Scene(); if (perspective > 0) { scene.fov = [fov, fov]; scene.w = scene.h = viewSize; scene.setPerspective(new V3(0, 0, 0), new V3(0, 0, 0)); } else { scene.setOrthographic(1, new V3(0,0,0), new V3(0, 0, 0)); } scene.camera_pos = Scene.worldCameraOrbit(new V3(0, 0, 0), cd, a1, a2); // let cube = new Ob([ //[0, 0, 0, 20, 0, 0], [0, 0, 0, 10, 0, 0], [11, 0, 0, 20, 0, 0], //[0, 0, 0, 0, 20, 0], [0, 0, 0, 0, 10, 0], [0, 11, 0, 0, 20, 0], [0, 0, 0, 0, 0, 20], ]); //scene.addOb(cube); /*scene.addLine(new V3(50, 5, 0), new V3(50, 0, 0)); scene.addLine(new V3(0, 0, 0), new V3(0, 50, 0)); scene.addLine(new V3(0, 0, 0), new V3(0, 0, 25));*/ let sdFunc = SDF.move(SDF.sphere(V(0, 0, 0), boxSize), V(0, 0, boxSize) );// SDF.box(V(20, 10, 10)); for (let i =0; i<amount; i++) { let p = new V3(Util.rndRange(-radius, radius), Util.rndRange(-radius, radius), Util.rndRange(-0.1*radius, radius)); if (p.magnitude() > radius) { continue; } let posXY = p.changez(0); let p2 = p.add(new V3(0, 0, Util.rndRange(rainLen, rainLen * 1.05))); const [hit, travel] = SDF.runRay(sdFunc, posXY.changez(radius), V(0, 0, -1), radius); let h = hit.z; if (p.z < h && h > 0.01) { if (p.z < h && h-p.z < circleSize ) { let r= h-p.z; if (h > 0.01) { r = 0.5; } scene.addOb(circle(p.changez(h), r)); p.z = Math.max(h, p.z); p2.z = Math.max(h, p2.z); scene.addLine(p2, p); } else { let d = Math.random() * Math.PI * 2; let dv = V(Math.cos(d), Math.sin(d)); let l=0, r = radius; for (let i=0; i<20; i++) { let m = (l+r)/2; let pSide = posXY.changez(radius).add(dv.mul(m)); let [hit2, travel2] = SDF.runRay(sdFunc, pSide, V(0, 0, -1), radius); if (hit2.z <= 0.01) { r = m; } else { l = m; } } let pnew = posXY.add(dv.mul(l)); p = pnew.changez(Math.max(0, p.z)); p2 = pnew.changez(Math.max(0, p2.z)); scene.addLine(p2, p); } } else { if (p.z < h && h-p.z < circleSize ) { let r= h-p.z; if (h > 0.01) { r = 2; } scene.addOb(circle(p.changez(h), r)); } p.z = Math.max(h, p.z); p2.z = Math.max(h, p2.z); scene.addLine(p2, p); } } /*let o1 = new Ob(); scene.addLine(new V3(0, 0, 0), new V3(50, 0, 0)); scene.addLine(new V3(50, 5, 0), new V3(50, 0, 0)); scene.addLine(new V3(0, 0, 0), new V3(0, 50, 0)); scene.addLine(new V3(0, 0, 0), new V3(0, 0, 25)); //o1.transform(M4.translate(new V3(-10, -10, -10))); scene.addOb(o1);*/ scene.draw(); return false; } class V3 { constructor(x=0, y=0, z=0) { this.x = x; this.y = y; this.z = z; } static V0 = new V3(0, 0, 0); toString() { return `V3(${this.x}, ${this.y}, ${this.z})`; } add(b) { return new V3(this.x + b.x, this.y + b.y, this.z + b.z); } sub(b) { return new V3(this.x - b.x, this.y - b.y, this.z - b.z); } mul(b) { return new V3(this.x * b, this.y * b, this.z * b); } scale(b) { return new V3(this.x * b.x, this.y * b.y, this.z * b.z); } flipx() { return new V3(-this.x, this.y, this.z); } flipy() { return new V3(this.x, -this.y, this.z); } copy() { return new V3(this.x,this.y,this.z); } changex(v) { let res = this.copy(); res.x = v; return res;} changey(v) { let res = this.copy(); res.y = v; return res;} changez(v) { let res = this.copy(); res.z = v; return res; } len2() { return this.x*this.x + this.y*this.y + this.z*this.z; } static lerp(a, b, x) { return a.mul(1-x).add(b.mul(x)); } magnitude() { return Math.sqrt(this.len2()); } normalized() { return this.mul(1/this.magnitude()); } xyzs() { return this.x + this.y + this.z } xyzMax() { return Math.max(this.x, this.y, this.z); } xyzMin() { return Math.min(this.x, this.y, this.z); } abs() { return new V3(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z)); } max(b) { return new V3(Math.max(this.x, b.x), Math.max(this.y, b.y), Math.max(this.z, b.z)); } min(b) { return new V3(Math.min(this.x, b.x), Math.min(this.y, b.y), Math.min(this.z, b.z)); } maxK(k) { return new V3(Math.max(this.x, k), Math.max(this.y, k), Math.max(this.z, k)); } minK(k) { return new V3(Math.min(this.x, k), Math.min(this.y, k), Math.min(this.z, k)); } dot(b) { return this.scale(b).xyzs(); } rotDeg(deg) { const a = deg*Math.PI/180; const s = Math.sin(a); const c = Math.cos(a); return new V2(this.x*c-this.y*s, this.x*s+this.y*c); } } class M4 { constructor() { this.d = [[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]]; } mul(b) { let res = new M4(); for (let i=0; i<4; i++) { for (let j=0; j<4; j++) { let s = 0; for (let k=0; k<4; k++) { s += this.d[i][k] * b.d[k][j] } res.d[i][j] = s; } } return res; } transpose() { let res = new M4(); for (let i=0; i<4; i++) { for (let j=0; j<4; j++) { res.d[i][j] = this.d[j][i]; } } return res; } mulv(v) { let vv = [v.x, v.y, v.z, 1]; let res = [0, 0, 0, 0]; for (let i=0; i<4; i++) { for (let k=0; k<4; k++) { res[i] += this.d[i][k] * vv[k]; } } return new V3(res[0], res[1], res[2]); } static translate(v) { let r = new M4(); r.d[0][3] = v.x; r.d[1][3] = v.y; r.d[2][3] = v.z; return r; } static euler(a,b,c) { let m1 = new M4(); let m2 = new M4(); let m3 = new M4(); m1.d = [[1, 0, 0, 0], [0, Math.cos(a), -Math.sin(a), 0], [0, Math.sin(a), Math.cos(a), 0], [0, 0, 0, 1]]; m2.d = [[Math.cos(b), 0, Math.sin(b), 0], [0, 1, 0, 0], [-Math.sin(b), 0, Math.cos(b), 0], [0, 0, 0, 1]]; m3.d = [[Math.cos(c), -Math.sin(c), 0, 0], [Math.sin(c), Math.cos(c), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; return m1.mul(m2).mul(m3); } static eulerv(v) { return euler(v.x, v.y, v.z); } } class Util { static rndRange(min, max) { return Math.random() * (max-min)+min; } static inverseLerp(a, b, x) { let d = b-a; if (d != 0) { return (x-a)/d; } else { return 0; } } static lerp(a, b, x) { return (1-x)*a + x*b; } static planeDistance(p0, pnorm, x) { pnorm = pnorm.normalized(); let k = p0.dot(pnorm); return x.dot(pnorm) - k; } static rectContains(r, p) { return p.x >= r[0] && p.x <= r[1] && p.y >= r[2] && p.y <= r[3]; } static planeClip(p0, pnorm, a, b) { let d1 = Util.planeDistance(p0, pnorm, a); let d2 = Util.planeDistance(p0, pnorm, b); if (d1 >= 0 && d2 >= 0) { return [a, b]; } if (d1 < 0 && d2 < 0) { return null; } let m = Util.inverseLerp(d1, d2, 0); let p = V3.lerp(a, b, m); if (d1 < 0) { return [p, b]; } else { return [a, p]; } } static clipRect(a,b,r) { let ca = rectContains(r, a); let cb = rectContains(r, b); if (ca && cb) { return [a, b]; } } } class Scene { constructor() { this.camera_pos = Scene.worldCameraM(new V3(0, 0, 0), new V3(1, 0, 0)); this.camera = Scene.orthographic1(); this.lines=[]; this.ortho = true; this.w=100; this.h=100; this.fov = [90, 90]; } addLine(a,b) { this.lines.push([a, b]); } addOb(x) { this.lines.push(...x.lines); } mapPoint(x) { let p = this.camera.mulv(x); if (p.z != 0) { p = p.mul(1/Math.abs(p.z)); } else { p.x = 0; p.y = 0; } return [p.x, p.y]; } draw() { this.lines.forEach((l) => { let debug=false; if (l[0].z > 0 && l[1].z > 0) { debug=true; } let p1 = this.camera_pos.mulv(l[0]); let p2 = this.camera_pos.mulv(l[1]); /*if (p1.z > 0 && p2.z > 0) { return; } else if (p1.z > 0 || p2.z > 0) { let x = Util.inverseLerp(p1.z, p2.z, 0.011); if (p1.z > 0) { p1 = V3.lerp(p1, p2, x); } else { p2 = V3.lerp(p1, p2, x); } }*/ if (!this.ortho) { let p0 = new V3(0, 0, 0); let norm = new V3(); let line = [p1, p2]; let a1 = Math.PI * 0.5*this.fov[0]/180; let a2 = Math.PI * 0.5*this.fov[1]/180; if (line) { norm = new V3(Math.cos(a1), 0, -Math.sin(a1)); line = Util.planeClip(p0, norm, line[0], line[1]); } if (line) { norm = new V3(-Math.cos(a1), 0, -Math.sin(a1)); line = Util.planeClip(p0, norm, line[0], line[1]); } if (line) { norm = new V3(0, -Math.cos(a2), -Math.sin(a2)); line = Util.planeClip(p0, norm, line[0], line[1]); } if (line) { norm = new V3(0, Math.cos(a2), -Math.sin(a2)); line = Util.planeClip(p0, norm, line[0], line[1]); } if (line) { p1 = line[0]; p2 = line[1]; } else { return; } } p1 = this.mapPoint(p1); p2 = this.mapPoint(p2); turtle.jump(p1) turtle.pendown(); turtle.goto(p2); }); } static worldCameraM(from, to) { let d = to.sub(from); let m1 = M4.translate(from.mul(-1)); let a1 = Math.atan2(d.y, d.x); let xy = d.changez(0); let a2 = Math.atan2(d.z, xy.magnitude()); //let m2 = (new M4()).mul(M4.euler(0, 0, +a1)).mul(M4.euler(-Math.PI/2 - a2, 0, 0)); let m2 = (new M4()).mul(M4.euler(-a2, -a1, 0)).mul(M4.euler(Math.PI/2, Math.PI, -Math.PI/2)); return m2.mul(m1); } static worldCameraOrbit(to, distance, z0, x0) { let x = x0* Math.PI / 180; let z = z0* Math.PI / 180; let p = M4.euler(0, 0, z).mul(M4.euler(0, x, 0)).mulv(new V3(-1, 0, 0)); let p2 = p.mul(distance).add(to); if (x0 > -90 && x0 < 90) { return Scene.worldCameraM(p2, p2.sub(p)); } else { let m1 = M4.translate(p2.mul(-1)); if (x0 > 0) { return M4.euler(0, 0, 1*Math.PI/2-z).mul(m1); } else { return M4.euler(0, Math.PI, -1*Math.PI/2-z).mul(m1); } } } static orthographic1(scale=1) { let res = new M4(); res.d = [ [scale,0,0,0], [0,-scale,0,0], [0, 0, 0,1], [0,0,0,1], ]; //res = res.mul(Scene.worldCameraM(from, to)); res = res; return res; } setOrthographic(scale, from, to) { this.camera = Scene.orthographic1(scale); this.camera_pos = Scene.worldCameraM(from, to); this.ortho = true; } static perspective(scale) { let res = new M4(); res.d = [ [scale,0,0,0], [0, -scale,0,0], [0, 0, -1,0], [0,0,0,1], ]; return res; } setPerspective(from, to) { this.camera = Scene.perspective(this.w*0.5 * Math.tan((90-this.fov[0]*0.5)/180*Math.PI)); this.camera_pos = Scene.worldCameraM(from, to); this.ortho = false; } } class Ob { constructor(linesa=[]){ this.lines=[]; if (linesa) { this.addLines(linesa); } } static fromChain(points) { let res = new Ob(); for (let i=1; i<points.length; i++) { res.addLine(points[i-1], points[i]); } return res; } static fromLoop(points) { let res = Ob.fromChain(points); if (points.length > 1) { res.addLine(points[points.length-1], points[0]); } return res; } addLine(a,b) { this.lines.push([a, b]); } addLines(ar) { for (const x of ar) { this.addLine(new V3(x[0], x[1], x[2]), new V3(x[3], x[4], x[5])); } } transform(m) { this.lines.forEach((v, i) => { this.lines[i] = [m.mulv(v[0]), m.mulv(v[1])]; }); } } class SDF { static bind1(f, ...args) { return function(...x) { f.bind(null, ...x); } } // combinations static unionD(a, b, x) { return Math.min(a(x), b(x)); } static union(a, b) { return SDF.unionD.bind(null, a, b); } static diffD(a, b, x) { return Math.max(-a(x), b(x)); } static diff(a, b) { return SDF.diffD.bind(a, b); } static intersectionD(a, b, x) { return Math.max(a(x), b(x)); } static intersection(a, b) { return SDF.intersectionD.bind(a, b); } static xorD(a, b, x) { let d1=a(x), d2=b(x); return Math.max(min(d1, d2), -max(d1, d2)); } static xor(a, b) { return SDF.xorD.bind(a, b); } static moveD(f, p, x) { return f(x.sub(p)); } static move(f, p) { return SDF.moveD.bind(null, f, p); } static rotateD(f, euler, x) { return f(M4.euler(euler).mulV(x)); } static rotate(f, euler) { return SDF.rotateD.bind(f, euler); } // primitives static sphereD(p, r, x) { let dv = x.sub(p); return dv.magnitude() - r; } static sphere(p, r) { //SDF.bind1(SDF.sphereD); return SDF.sphereD.bind(null, p, r); } static boxD(s, x) { let q = x.abs().sub(s); return q.maxK(0).magnitude() + Math.min(q.xyzMax()); } static box = SDF.bind1(SDF.boxD); // static runRay(f, p0, dir, limit) { let travel = 0; let p = p0; while (travel < limit) { let distance = f(p); if (distance < 0.001) { break; } distance = Math.min(limit-travel, distance); p = p.add(dir.mul(distance)); travel += distance; } return [p, travel]; } } function initlib() { this.V3 = V3; this.V = (x,y,z)=>new V3(x, y, z); this.M4 = M4; this.Ob = Ob; this.Scene = Scene; this.SDF = SDF; }