@@ -16,7 +16,8 @@ const ADD_PARTICLES = true;
1616
1717// total number of particles
1818let numParticles = 10000 ;
19- const USE_VARIABLE_NUMPARTICLES = false ;
19+
20+ const USE_VARIABLE_NUMPARTICLES = true ;
2021
2122// maximum particle age
2223const maxAge = 80 ;
@@ -46,6 +47,9 @@ let vCenter = null; // position vector for center point
4647// constants
4748const DEGREE_TO_RADIAN = Math . PI / 180 ;
4849
50+ // pre-allocate vector
51+ const vector = new Float32Array ( 2 ) ;
52+
4953// Initialize particles
5054function initializeParticles ( ) {
5155 // checks if anything to do
@@ -127,15 +131,14 @@ function createParticle(i) {
127131 // moves points according to vector field:
128132 // vx - x direction == lon
129133 // vy - y direction == lat
130- let vx , vy ;
131- ( { vx, vy } = vectorField . getVectorField ( lon , lat ) ) ;
134+ vectorField . getVectorField ( lon , lat , vector ) ;
132135
133136 // check if valid
134- if ( ! vx || ! vy ) { return ; }
137+ if ( ! vector [ 0 ] || ! vector [ 1 ] ) { return ; }
135138
136139 // scale velocities
137- vx *= VELOCITY_FACTOR ;
138- vy *= VELOCITY_FACTOR ;
140+ vector [ 0 ] *= VELOCITY_FACTOR ; // vx
141+ vector [ 1 ] *= VELOCITY_FACTOR ; // vy
139142
140143 // or simple vector field
141144 //const lonRad = lon * DEGREE_TO_RADIAN;
@@ -156,8 +159,8 @@ function createParticle(i) {
156159
157160 particles [ index ] = lon ; // lon
158161 particles [ index + 1 ] = lat ; // lat
159- particles [ index + 2 ] = vx ; // vx
160- particles [ index + 3 ] = vy ; // vy
162+ particles [ index + 2 ] = vector [ 0 ] ; // vx
163+ particles [ index + 3 ] = vector [ 1 ] ; // vy
161164 particles [ index + 4 ] = age ; // age
162165}
163166
@@ -224,7 +227,7 @@ function determineBoundsLonLat(projection,width,height){
224227 // set new bounds
225228 bounds = { lonMin, lonMax, latMin, latMax } ;
226229
227- console . log ( `determineBoundsLonLat: bounds lon: min/max = ${ lonMin } /${ lonMax } lat: min/max = ${ latMin } /${ latMax } ` ) ;
230+ // console.log(`determineBoundsLonLat: bounds lon: min/max = ${lonMin}/${lonMax} lat: min/max = ${latMin}/${latMax}`);
228231}
229232
230233function updateParticles ( projection , width , height ) {
@@ -237,7 +240,7 @@ function updateParticles(projection,width,height) {
237240 if ( ! vectorField . isGradientValid ( ) ) return ;
238241
239242 //console.time('updateParticles')
240- console . log ( `updateParticles: width/height ${ width } /${ height } ` ) ;
243+ // console.log(`updateParticles: width/height ${width}/${height}`);
241244
242245 // context for animation drawing
243246 //const context = d3.select("#animation").node().getContext("2d");
@@ -274,22 +277,25 @@ function updateParticles(projection,width,height) {
274277 // adapt number of particles to view range
275278 if ( USE_VARIABLE_NUMPARTICLES ) {
276279 // estimate number of particles based on window size
277- let estimate = 10 * Math . max ( width , height ) ;
278- console . log ( `updateParticles: numParticles ${ numParticles } estimate window size ${ estimate } ` ) ;
280+ // let estimate = 10 * Math.max(width,height);
281+ // console.log(`updateParticles: numParticles ${numParticles} estimate window size ${estimate}`);
279282
280283 // estimate based on bounds range
281- estimate = ( bounds . lonMax - bounds . lonMin ) * ( bounds . latMax - bounds . latMin ) ;
282- console . log ( `updateParticles: numParticles ${ numParticles } estimate bounds range ${ estimate } ` ) ;
284+ // estimate = (bounds.lonMax - bounds.lonMin) * (bounds.latMax - bounds.latMin);
285+ // console.log(`updateParticles: numParticles ${numParticles} estimate bounds range ${estimate}`);
283286
284- if ( estimate > 20000 ) {
285- numParticles = 20000 ;
286- } else {
287- numParticles = 10000 ;
288- }
287+ //if (estimate > 20000) {
288+ // numParticles = 20000;
289+ //} else {
290+ // numParticles = 10000;
291+ //}
292+
293+ // check pixel size limit 100 x 100 == 10,000
294+ if ( width * height < numParticles ) numParticles = Math . floor ( width * height * 0.7 ) ;
289295
290296 // check if we need to recreate the array
291297 if ( particles . length != numParticles * 5 ) {
292- console . log ( `updateParticles: recreate numParticles * 5 = ${ numParticles * 5 } particles length = ${ particles . length } ` ) ;
298+ // console.log(`updateParticles: recreate numParticles * 5 = ${numParticles * 5} particles length = ${particles.length}`);
293299 // re-create new array
294300 particles = new Float32Array ( numParticles * 5 ) ; // x, y, vx, vy, age
295301 }
@@ -349,18 +355,14 @@ function moveParticles() {
349355 let lon = particles [ index ] ;
350356 let lat = particles [ index + 1 ] ;
351357
352- let vx , vy ;
353- ( { vx, vy } = vectorField . getVectorField ( lon , lat ) ) ;
358+ vectorField . getVectorField ( lon , lat , vector ) ;
354359
355360 // check if valid
356- if ( ! vx || ! vy ) { return ; }
361+ if ( ! vector [ 0 ] || ! vector [ 1 ] ) { return ; }
357362
358363 // scale velocities
359- vx *= VELOCITY_FACTOR ;
360- vy *= VELOCITY_FACTOR ;
361-
362- particles [ index + 2 ] = vx ;
363- particles [ index + 3 ] = vy ;
364+ particles [ index + 2 ] = vector [ 0 ] * VELOCITY_FACTOR ; // vx
365+ particles [ index + 3 ] = vector [ 1 ] * VELOCITY_FACTOR ; // vy
364366
365367 // ageing
366368 let age = particles [ index + 4 ] ;
@@ -441,6 +443,12 @@ function drawParticles(projection, context, width, height) {
441443 colorBuckets [ color ] . length = 0 ; // set length to zero to avoid costly garbage collection
442444 } ) ;
443445
446+ // position vectors
447+ const p0 = [ 0 , 0 ] ;
448+ const p1 = [ 0 , 0 ] ;
449+ const v0 = [ 0 , 0 , 0 ] ;
450+ const v1 = [ 0 , 0 , 0 ] ;
451+
444452 // fill color buckets
445453 for ( let i = 0 ; i < particles . length ; i ++ ) {
446454 const index = i * 5 ;
@@ -457,6 +465,9 @@ function drawParticles(projection, context, width, height) {
457465 const vx = particles [ index + 2 ] ;
458466 const vy = particles [ index + 3 ] ;
459467
468+ // velocity strength
469+ const normSq = ( vx * vx + vy * vy ) / VELOCITY_FACTOR ; // in [0,1]
470+
460471 // updated position
461472 const lon1 = lon0 + vx ;
462473 const lat1 = lat0 + vy ;
@@ -465,35 +476,36 @@ function drawParticles(projection, context, width, height) {
465476 // current point location vector
466477 const lonRad = lon0 * DEGREE_TO_RADIAN ;
467478 const latRad = lat0 * DEGREE_TO_RADIAN ;
468- const v0 = [ Math . cos ( latRad ) * Math . cos ( lonRad ) , Math . cos ( latRad ) * Math . sin ( lonRad ) , Math . sin ( latRad ) ] ;
479+ v0 [ 0 ] = Math . cos ( latRad ) * Math . cos ( lonRad ) ;
480+ v0 [ 1 ] = Math . cos ( latRad ) * Math . sin ( lonRad ) ;
481+ v0 [ 2 ] = Math . sin ( latRad ) ;
469482
470483 const lonRad1 = lon1 * DEGREE_TO_RADIAN ;
471484 const latRad1 = lat1 * DEGREE_TO_RADIAN ;
472- const v1 = [ Math . cos ( latRad1 ) * Math . cos ( lonRad1 ) , Math . cos ( latRad1 ) * Math . sin ( lonRad1 ) , Math . sin ( latRad1 ) ] ;
485+ v1 [ 0 ] = Math . cos ( latRad1 ) * Math . cos ( lonRad1 ) ;
486+ v1 [ 1 ] = Math . cos ( latRad1 ) * Math . sin ( lonRad1 ) ;
487+ v1 [ 2 ] = Math . sin ( latRad1 ) ;
473488
474489 if ( ! isPointVisibleHemisphere ( vCenter , v0 ) ) { continue ; }
475490 if ( ! isPointVisibleHemisphere ( vCenter , v1 ) ) { continue ; }
476491
477492 // converted to x/y pixel indexing
478- const p0 = projection ( [ lon0 , lat0 ] ) ;
479- const p1 = projection ( [ lon1 , lat1 ] ) ;
493+ //([p0[0],p0[1]] = projection([lon0,lat0]) );
494+ //([p1[0],p1[1]] = projection([lon1,lat1]) );
480495
481496 // check if valid
482- if ( ! p0 || isNaN ( p0 [ 0 ] ) || isNaN ( p0 [ 1 ] ) ) { continue ; }
483- if ( ! p1 || isNaN ( p1 [ 0 ] ) || isNaN ( p1 [ 1 ] ) ) { continue ; }
497+ // if (!p0 || isNaN(p0[0]) || isNaN(p0[1])) { continue; }
498+ // if (!p1 || isNaN(p1[0]) || isNaN(p1[1])) { continue; }
484499
485500 // line points
486- const [ x0 , y0 ] = p0 ;
487- const [ x1 , y1 ] = p1 ;
501+ // const [x0,y0] = p0;
502+ // const [x1,y1] = p1;
488503
489504 // check if point is visible area
490505 // doesn't work, returned [x,y] pixel positions are all within globe area...
491506 //if (! isPointClose([x0,y0])) { continue; }
492507 //if (! isPointClose([x1,y1])) { continue; }
493508
494- // velocity strength
495- const normSq = ( vx * vx + vy * vy ) / VELOCITY_FACTOR ; // in [0,1]
496-
497509 // coloring
498510 // convert norm to rgb gray scale value
499511 //const val = 155 + Math.floor( normSq * 100.0 );
@@ -517,12 +529,10 @@ function drawParticles(projection, context, width, height) {
517529 const color = colorScale ( normSq ) ;
518530
519531 // If this color doesn't have a bucket yet, create an empty array for it
520- if ( ! colorBuckets [ color ] ) {
521- colorBuckets [ color ] = [ ] ;
522- }
532+ //if (!colorBuckets[color]) { colorBuckets[color] = []; }
523533
524534 // Add the particle to the appropriate color bucket
525- colorBuckets [ color ] . push ( [ x0 , y0 , x1 , y1 ] ) ;
535+ colorBuckets [ color ] . push ( index ) ;
526536 } // age
527537 }
528538
@@ -535,17 +545,46 @@ function drawParticles(projection, context, width, height) {
535545 if ( bucketParticles . length == 0 ) return ;
536546
537547 // draw bucket
538- context . beginPath ( ) ;
539- context . strokeStyle = color ;
540- context . lineWidth = lineWidth ; //particleSize;
548+ //context.beginPath();
549+ //context.strokeStyle = color;
550+ //context.lineWidth = lineWidth; //particleSize;
551+ //bucketParticles.forEach(([x0,y0,x1,y1]) => {
552+ // // line
553+ // context.moveTo(x0, y0);
554+ // context.lineTo(x1, y1);
555+ //});
556+ //context.stroke();
557+
558+ const path = new Path2D ( ) ;
559+ bucketParticles . forEach ( index => {
560+ // initial position
561+ // lon/lat positions
562+ const lon0 = particles [ index ] ;
563+ const lat0 = particles [ index + 1 ] ;
564+
565+ const vx = particles [ index + 2 ] ;
566+ const vy = particles [ index + 3 ] ;
541567
542- bucketParticles . forEach ( ( [ x0 , y0 , x1 , y1 ] ) => {
543- // line
544- context . moveTo ( x0 , y0 ) ;
545- context . lineTo ( x1 , y1 ) ;
568+ // updated position
569+ const lon1 = lon0 + vx ;
570+ const lat1 = lat0 + vy ;
571+
572+ // converted to x/y pixel indexing
573+ ( [ p0 [ 0 ] , p0 [ 1 ] ] = projection ( [ lon0 , lat0 ] ) ) ;
574+ ( [ p1 [ 0 ] , p1 [ 1 ] ] = projection ( [ lon1 , lat1 ] ) ) ;
575+
576+ // check if valid
577+ if ( ! p0 || isNaN ( p0 [ 0 ] ) || isNaN ( p0 [ 1 ] ) ) { return ; }
578+ if ( ! p1 || isNaN ( p1 [ 0 ] ) || isNaN ( p1 [ 1 ] ) ) { return ; }
579+
580+ // add line to path
581+ path . moveTo ( p0 [ 0 ] , p0 [ 1 ] ) ;
582+ path . lineTo ( p1 [ 0 ] , p1 [ 1 ] ) ;
546583 } ) ;
584+ context . strokeStyle = color ;
585+ context . lineWidth = lineWidth ;
586+ context . stroke ( path ) ; // stroke the entire path at once
547587
548- context . stroke ( ) ;
549588 } ) ;
550589
551590 // timing
0 commit comments