PageSourceSearch

https://csynth.github.io/csynth/JSTS/csynth/ForceVis.js

js csynth.github.io collected 2026-10-03 08:51:48 UTC 14,648 bytes, 297 lines download raw bytes

1/**
2 * Drawing links between particles.
3 */
4class ForceVis {
5    constructor(springs) {
6        this.springs = springs;
7        //what kind of geometry do I want (lines vs quads)?
8        //what kind of links am I showing (how bad would it be to have n*n)?
9        //I already do something in annotationDisplay... so concrete first step is to refactor that to here. ('annotationLinkLines')
10    }
11    topologySpringLines(uniforms) {
12        // const topologyarr = this.springs.topologyarr;
13        // const activeSprings = [];
14        // for (let i=0; i<topologyarr.length; i+=4) {
15        //     const v = topologyarr.slice(i, i+4);
16        //     if (v.filter(x => x === -1).length !== 4) activeSprings.push({i: i, v: v});
17        // }
18        // console.log(activeSprings.length + " active springs");
19        // const activeSprings = [];
20        // for (let u=0; u<n; u++) {
21        //     for (let v=0; v<n; v++) {
22        //         const uvSprings = this.springs.showspring(u, v).filter(s => s !== undefined);
23        //         activeSprings.push(...uvSprings);
24        //     }
25        // }
26        // console.log(activeSprings.length + " active springs");
27    }
28    contactForceLines(uniforms) {
29        const n = this.springs.numInstances;
30        const p2 = this.springs.numInstancesP2;
31        //find active springs from contacts and generate geometry.
32        //TODO: make sure this keeps track of any changes... perhaps springs should generate an event.
33        //(being careful not to respond to each spring individually when changing entire set...)
34        //actually, another thing we ideally want is the pairforce() results in a buffer;
35        //might refactor to allow doing this in a separate pass so we don't have to redo equivalent work in this shader.
36        const contacts = this.springs.contacts;
37        const activeSprings = [];
38        for (let u = 0; u < n; u++) {
39            for (let v = 0; v < n; v++) {
40                const val = contacts.getab(u, v); //might be 0, some small float, -999...
41                if (val > 0) {
42                    activeSprings.push([u, v, val]);
43                    activeSprings.push([v, u, val]);
44                }
45            }
46        }
47        console.log((activeSprings.length / 2) + " active springs");
48        const n2 = activeSprings.length;
49        const particleIndex = new Float32Array(activeSprings.length).map((v, i) => activeSprings[i][0] / p2);
50        const oPart = new Float32Array(activeSprings.length).map((v, i) => activeSprings[i][1] / p2);
51        const geoIndex = new Uint32Array(n2).map((v, i) => i); //duh
52        const geometry = new THREE.BufferGeometry();
53        geometry.setAttribute('particleIndex', new THREE.BufferAttribute(particleIndex, 1));
54        geometry.setAttribute('oPart', new THREE.BufferAttribute(oPart, 1));
55        geometry.setIndex(new THREE.BufferAttribute(geoIndex, 1));
56        const linkVert = `
57            ${CSynth.CommonShaderCode()}
58            attribute float particleIndex;
59            attribute float oPart;
60            varying float d;
61            void main() {
62                vec4 pos = partposWorld(particleIndex);
63                vec4 oPos = partposWorld(oPart);
64                vec4 dp = pos-oPos;
65                //float lenSq = dot(dp, dp)
66                d = length(dp);
67                //could maybe let user inject glsl functions which gets info about the two particles and decide whether to discard / how to color etc.
68                //this could be
69                //   based on selection range (eg, of rectangular annotation drawn on matrix, or maybe even 'painted' selection), maybe best outside shader though
70                //   based on how far apart / how strong force is / etc.
71
72                //can't discard vertices, but I could cause resulting geometry to be degenerate or out of range...
73                //maybe do something different with w, or "pos += dp/2".
74                pos.w = 1.;
75                gl_Position = logdepth(projectionMatrix * viewMatrix * pos);
76            }
77        `;
78        const linkFrag = `
79            precision highp float;
80            varying float d;
81            void main() {
82                if (d > 300.) discard; //more useful would be to look at force and visualise only strong forces.
83                gl_FragColor = vec4(1.);
84                gl_FragColor.a = 1. / d;
85                //discard; //even with all fragments discarded, large models are slow.
86            }
87        `;
88        const material = new THREE.RawShaderMaterial({
89            vertexShader: linkVert, fragmentShader: linkFrag, uniforms,
90            transparent: true, depthWrite: false
91        });
92        const linkMesh = new THREE.LineSegments(geometry, material);
93        linkMesh.frustumCulled = false;
94        return linkMesh;
95    }
96    capsidInteractions() {
97    }
98    nucleusInteractions() {
99    }
100    /**
101     * This is a particular special case, designed to work with data generated internally in annotationDisplay.
102     * Each element includes 'r' range info used for picking, which is normalised differently to the indices in i1 & i2
103     * @param {*} linkData array of {i1, i2, r}
104     * where i1/i2 are indices (normalised to g.springs range) and r (normalised to global springs range)
105     * @param {*} uniforms
106     */
107    annotationLinkLines(linkData, uniforms) {
108        // I want a version of this that makes better visuals
109        // and I want to ?? abstract out the picking code and make the interface better ??
110        // somehow the better visuals seem like they should take priority.
111        const linkVert = `
112            ${CSynth.CommonShaderCode()}
113            //note: when using separate spring systems, particleIndex is annotation springs range
114            //while particleRange, used for selection test, is in 'main' springs range
115            attribute float particleIndex;
116            attribute vec3 particleRange;
117            uniform float minTextScale;
118            uniform float selWidth;
119            varying vec4 color;
120
121            void main() {
122                vec4 pos = partposWorld(particleIndex);
123                pos.w = 1.;
124                pos = viewMatrix * pos;
125                gl_Position = logdepth(projectionMatrix * pos);
126
127            //TODO:::: make this code common... (maybe use pos.w for selectedness)
128                float s = minTextScale;
129                for (int i=0; i<PICKNUM; i++) {
130                    //SKIP_PICK //TODO change the way SKIP_PICK works, vertex shader doesn't like 'continue'
131                    float p = getPick(i);
132                    if (p > 99.9) continue;
133                    vec3 r = particleRange;
134                    //are we within the range, or how far above or below it?
135                    float dp;
136                    bool inRange = r.y < p && r.z > p;
137                    if (inRange) {
138                        dp = 0.;
139                    } else {
140                        vec3 dr = abs(particleRange - p);
141                        dr = abs(dr);
142                        //dr.x is at time of writing distance of p to the point that this line points to
143                        dp = min(dr.x, min(dr.y, dr.z));
144                        //dp = min(abs(r.y-p), abs(r.z-p));
145                    }
146                    //dp = abs(r.x - p); //this was just looking at centre.
147
148
149                    float sw = selWidth + 1e-10;
150                    float inv = 1./sw;
151                    float s2 = dp <= sw ? 1.-(dp*inv) : 0.;
152                    s2 = VALID_PICK_INDEX ? s2 : 0.;
153                    //s2 *= 10.;
154                    s = max(s, s2);
155                }
156                //TODO: color options.
157                color.rgb = bedColor(particleRange.x);
158                color.a = s*s*s;
159            }
160        `;
161        const linkFrag = `
162            precision highp float;
163            varying vec4 color;
164            void main() {
165                gl_FragColor = color;
166            }
167        `;
168        const geometry = new THREE.BufferGeometry();
169        const flatten = arr => arr.reduce((a, b) => a.concat(b), []);
170        const particleIndices = new Float32Array(flatten(linkData.map(d => [d.i1, d.i2])));
171        const particleRanges = new Float32Array(flatten(linkData.map(d => d.r.concat(d.r))));
172        const geoIndex = new Uint16Array(flatten(linkData.map((d, j) => [j * 2, j * 2 + 1])));
173        geometry.setAttribute('particleIndex', new THREE.BufferAttribute(particleIndices, 1));
174        geometry.setAttribute('particleRange', new THREE.BufferAttribute(particleRanges, 3));
175        geometry.setIndex(new THREE.BufferAttribute(geoIndex, 1));
176        geometry.name = 'annotationSpringLink geometry';
177        const material = new THREE.RawShaderMaterial({
178            vertexShader: linkVert, fragmentShader: linkFrag, uniforms: uniforms,
179            transparent: true, depthWrite: false
180        });
181        // at some point I want to render these as mesh rather than lines.
182        const linkMesh = new THREE.LineSegments(geometry, material);
183        linkMesh.name = 'annotationSpringLink mesh';
184        linkMesh.frustumCulled = false;
185        this.linkMesh = linkMesh;
186        return linkMesh;
187    }
188    annotationLinkQuads(linkData, uniforms) {
189        // I want a version of this that makes better visuals
190        // and I want to ?? abstract out the picking code and make the interface better ??
191        // somehow the better visuals seem like they should take priority.
192        const linkVert = `
193            ${CSynth.CommonShaderCode()}
194            //note: when using separate spring systems, particleIndex is annotation springs range
195            //while particleRange, used for selection test, is in 'main' springs range
196            attribute float particleIndex;
197            attribute float adjacentParticleIndex;
198            attribute vec3 particleRange;
199            //attribute vec2 uv;
200            uniform float minTextScale;
201            uniform float selWidth;
202            uniform float lineWidth;
203            varying vec4 color;
204            varying vec2 vUv;
205
206            //TODO:::: make this code common... (maybe use pos.w for selectednes)
207            float computeSelectednes() {
208                float s = minTextScale;
209                for (int i=0; i<PICKNUM; i++) {
210                    //SKIP_PICK //TODO change the way SKIP_PICK works, vertex shader doesn't like 'continue'
211                    float p = getPick(i);
212                    if (p > 99.9) continue;
213                    vec3 r = particleRange;
214                    //are we within the range, or how far above or below it?
215                    float dp;
216                    bool inRange = r.y < p && r.z > p;
217                    if (inRange) {
218                        dp = 0.;
219                    } else {
220                        vec3 dr = abs(particleRange - p);
221                        dr = abs(dr);
222                        //dr.x is at time of writing distance of p to the point that this line points to
223                        dp = min(dr.x, min(dr.y, dr.z));
224                        //dp = min(abs(r.y-p), abs(r.z-p));
225                    }
226                    //dp = abs(r.x - p); //this was just looking at centre.
227
228
229                    float sw = selWidth + 1e-10;
230                    float inv = 1./sw;
231                    float s2 = dp <= sw ? 1.-(dp*inv) : 0.;
232                    s2 = VALID_PICK_INDEX ? s2 : 0.;
233                    //s2 *= 10.;
234                    s = max(s, s2);
235                }
236                return s;
237            }
238
239            void main() {
240                float s = computeSelectednes();
241                vec4 pos = partposWorld(particleIndex);
242                pos.w = 1.;
243                vec4 p2 = partposWorld(adjacentParticleIndex);
244                p2.w = 1.;
245                pos = viewMatrix * pos;
246                p2 = viewMatrix * p2;
247                vec2 dir = normalize(pos.xy - p2.xy) * 2.*(uv.y-0.5);
248                dir = dir.yx * vec2(-1., 1.);
249                pos.xy += s * dir * lineWidth * (uv.x - 0.5);
250                //trying to stop horizontal line from squishing. pending working out properly without computing other end...
251                //pos.y -= lineWidth*(uv.x - 0.5);
252                vUv = uv;
253                gl_Position = logdepth(projectionMatrix * pos);
254
255                //TODO: color options.
256                color.rgb = bedColor(particleRange.x);
257                color.a = s*s*s;
258            }
259        `;
260        const linkFrag = `
261            precision highp float;
262            varying vec4 color;
263            varying vec2 vUv;
264            void main() {
265                gl_FragColor = color;
266                gl_FragColor.a *= smoothstep(0., 0.5, 1. - abs(1.-2. * vUv.x));
267            }
268        `;
269        const geometry = HW.planeg(100, 100, linkData.length, 2);
270        //const geometry = new THREE.BufferGeometry();
271        const flatten = arr => arr.reduce((a, b) => a.concat(b), []);
272        //const positions = new Float32Array( flatten( linkData.map(d => [0,0,0]) ) );
273        const particleIndices = new Float32Array(flatten(linkData.map(d => [d.i1, d.i1, d.i2, d.i2])));
274        const adjacentParticleIndices = new Float32Array(flatten(linkData.map(d => [d.i2, d.i2, d.i1, d.i1])));
275        const particleRanges = new Float32Array(flatten(linkData.map(d => d.r.concat(d.r, d.r, d.r))));
276        const geoIndex = new Uint16Array(flatten(linkData.map((d, i) => [0, 1, 2, 1, 2, 3].map(v => v + (i * 4)))));
277        const UVs = new Float32Array(flatten(linkData.map(d => [0, 0, 1, 0, 0, 1, 1, 1])));
278        geometry.setAttribute('particleIndex', new THREE.BufferAttribute(particleIndices, 1));
279        geometry.setAttribute('adjacentParticleIndex', new THREE.BufferAttribute(adjacentParticleIndices, 1));
280        geometry.setAttribute('particleRange', new THREE.BufferAttribute(particleRanges, 3));
281        geometry.setAttribute('uv', new THREE.BufferAttribute(UVs, 2));
282        geometry.setIndex(new THREE.BufferAttribute(geoIndex, 1));
283        geometry.name = 'annotationSpringLink geometry';
284        uniforms.lineWidth = { value: 5 };
285        const material = new THREE.RawShaderMaterial({
286            vertexShader: linkVert, fragmentShader: linkFrag, uniforms: uniforms,
287            transparent: true, depthWrite: false, side: THREE.DoubleSide
288        });
289        ///// at some point I want to render these as mesh rather than lines.
290        const linkMesh = new THREE.Mesh(geometry, material); // new THREE.LineSegments(geometry, material);
291        linkMesh.name = 'annotationSpringLink mesh';
292        linkMesh.frustumCulled = false;
293        this.linkMesh = linkMesh;
294        return linkMesh;
295    }
296}
297//# sourceMappingURL=ForceVis.js.map

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.