PageSourceSearch

https://swiss.gl/demo/Physarum.js

js swiss.gl collected 2026-10-03 23:20:28 UTC 4,341 bytes, 104 lines download raw bytes

1/** @license 
2  * Copyright 2023 Google LLC.
3  * SPDX-License-Identifier: Apache-2.0 
4  */
5
6// Inspired by https://cargocollective.com/sagejenson/physarum
7class Physarum {
8    static Tags = ['2d', 'simulation'];
9
10    constructor(glsl, gui) {
11        const U = this.U = {follow:false};
12        const controllers = [];
13        const par = (s, v, ...arg)=>{U[s]||=v;controllers.push(gui.add(U,s,...arg))};
14        const presets = {
15            worms: {senseFlip: false, senseAng:15, senseDist:10,moveAng:7,moveDist:1},
16            mesh:{senseFlip:false, senseAng:45, senseDist:15, moveAng:17,moveDist:1},        
17            flocks: {senseFlip: true, senseAng:40, senseDist:36,moveAng:7,moveDist:1},
18            swirl:{senseFlip: true, senseAng:1.28, senseDist: 19, moveAng: 7, moveDist: 2},    
19        };
20        this.preset = 'worms';
21        gui.add(this, 'preset', Object.keys(presets)).onChange(name=>{
22            Object.assign(U, presets[name]);
23            controllers.forEach(c=>c.updateDisplay());
24        });
25        par('step_n',      1,    0, 20, 1);
26        par('dt',          1,    0.3, 1.0);
27        par('density',     1,    1, 4,  1);
28        par('senseFlip',   false);
29        par('senseAng',   40,    0, 180  );
30        par('senseDist',  36,    1, 50   );
31        par('moveAng',     7,    0, 180  );
32        par('moveDist',    1,    0, 10   );
33        par('zoom',        1,    1, 8);
34        gui.add(U,'follow');
35        Object.assign(U, presets[this.preset]);
36    }
37
38    frame(glsl, {DPR}) {
39        for (let i=0; i<this.U.step_n; ++i) {
40            this.step(glsl, DPR);
41        }
42        const points = this.points[0];
43        const common = {field:this.field[0], ...this.U, points, Inc:`
44        vec2 wld2scr(vec2 p) {
45            vec2 d = follow?points(ivec2(0)).xy : vec2(field_size())*0.5;
46            return 2.0*zoom*(p-d.xy)/vec2(field_size()); //*rot2(d.z-PI/2.)
47        }`};
48        glsl({...common, VP:`
49        varying vec2 uv = XY*2.;
50        VPos.xy = wld2scr(uv*vec2(field_size()));`,
51        FP:`field(uv).x`});
52
53        glsl({...this.U, ...common, Grid: [1,1,4], Blend: 's+d*(1-sa)', VP:`
54        vec4 p = points(ID.xy);
55        varying vec4 color = ID.z==0 ? vec4(0,1,0,0.5):vec4(1,0,0,0.5); 
56        if (ID.z>0) {p.xy += rot2(p.z+radians(senseAng)*float(ID.z-2))[0]*senseDist;}
57        VPos.xy = wld2scr(p.xy+XY*10.0/zoom);`,
58        FP:`smoothstep(1.0, 0.0, length(XY))*color`});
59    }
60
61    step(glsl, DPR) {
62        const U=this.U, dt=U.dt;
63        const field = this.field = glsl({dt, Inc:`
64        float unpack(float x){return exp((x-1.0)*4.0);}
65        float pack(float x){return log(x)/4.0+1.0;}`, FP:`
66        vec2 dp = Src_step();
67        float x=UV.x, y=UV.y;
68        float l=x-dp.x, r=x+dp.x, u=y-dp.y, d=y+dp.y;
69        #define S(x,y) unpack(Src(vec2(x,y)).r)
70        float v0 = S(x,y);
71        float v1 = 0.95*(v0+S(l,y)+S(r,y)+S(x,u)+S(x,d)+S(l,u)+S(r,u)+S(l,d)+S(r,d))/9.0;
72        FOut.x = pack(clamp(mix(v0, v1, dt),1e-5,1.));
73        `}, {story:2, scale:1/DPR, format:'rgba8', filter:'linear', tag:'field'});
74
75        const points = this.points = glsl({field:field[0], ...this.U, 
76            rotAng:(1-U.senseFlip*2)*U.moveAng/180.0*Math.PI, FP:`
77        FOut = Src(I);
78        vec2 wldSize = vec2(field_size());
79        if (FOut.w == 0.0 || FOut.x>=wldSize.x || FOut.y>=wldSize.y) {
80            FOut = vec4(hash(ivec3(I, 123)), 1.0);
81            FOut.xyz *= vec3(wldSize, TAU); 
82            return;
83        }
84        vec2 dir = vec2(cos(FOut.z), sin(FOut.z));
85        mat2 R = rot2(radians(senseAng));
86        vec2 sense = senseDist*dir;
87        #define F(p) field((FOut.xy+(p))/wldSize).x
88        float c=F(sense), r=F(R*sense), l=F(sense*R);
89        if (l>c && c>r) {
90            FOut.z -= rotAng*dt;
91        } else if (r>c && c>l) {
92           FOut.z += rotAng*dt;
93        } else if (c<=r && c<=l) {
94           FOut.z += sign(hash(ivec3(FOut.xyz*5039.)).x-0.5)*rotAng*sqrt(dt);
95        }
96        FOut.xy += dir*moveDist*dt;
97        FOut.xy = mod(FOut.xy, wldSize);
98        `}, {scale:this.U.density/16/DPR, story:2, format:'rgba32f', tag:'points'});
99        
100        glsl({dt, points: points[0], Grid: points[0].size, Blend: 's+d', VP:`
101        VPos.xy = 2.0 * (points(ID.xy).xy+XY*2.0)/vec2(ViewSize) - 1.0;`, FP:`
102        smoothstep(1.0, 0.0, length(XY))*dt`}, field[0]);
103    }
104}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.