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.