Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

optimizations from my state one #11

Merged
merged 1 commit into from 11 months ago

2 participants

Calvin Metcalf Shawn Allen
Calvin Metcalf

thanks to help from @natevw I was able to optimize the version I did of MA from 8 seconds to <2, these are the optimizations, which mainly involved removing map and forEach functions and replacing them with while loops, the two biggest speedups were

  1. Instead of calculating vector as a map of meta which we reduced, reduce directly on vector.
  2. Instead of calculating theta, from arctan2(dx,dy) and then latter doing sin(theta) and cos(theta), sin(arctan()) and cos(arctan()) can be simplified as
function sinArctan(dx,dy){
  var div = dx/dy;
  return (dy>0)?
    (div/Math.sqrt(1+(div*div))):
    (-div/Math.sqrt(1+(div*div)));
}
function cosArctan(dx,dy){
  var div = dx/dy;
  return (dy>0)?
    (1/Math.sqrt(1+(div*div))):
    (-1/Math.sqrt(1+(div*div)));
}
Shawn Allen shawnbot merged commit 07c965f into from
Shawn Allen shawnbot closed this
Shawn Allen
Owner

This is really, really awesome, Calvin. Thank you!

Calvin Metcalf

no problem :smile:

Calvin Metcalf calvinmetcalf deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

May 16, 2013
Calvin Metcalf optimizations b6238aa
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 71 additions and 55 deletions. Show diff stats Hide diff stats

  1. 126  cartogram.js
126  cartogram.js
@@ -33,18 +33,25 @@
33 33
       topology = copy(topology);
34 34
 
35 35
       // objects are projected into screen coordinates
36  
-      var projectGeometry = projector(projection);
37 36
 
38 37
       // project the arcs into screen space
39  
-      var tf = transformer(topology.transform),
40  
-          projectedArcs = topology.arcs.map(function(arc) {
41  
-            var x = 0, y = 0;
42  
-            return arc.map(function(coord) {
43  
-              coord[0] = (x += coord[0]);
44  
-              coord[1] = (y += coord[1]);
45  
-              return projection(tf(coord));
46  
-            });
47  
-          });
  38
+      var tf = transformer(topology.transform),x,y,len1,i1,out1,len2=topology.arcs.length,i2=0,
  39
+          projectedArcs = new Array(len2);
  40
+          while(i2<len2){
  41
+            x = 0;
  42
+            y = 0;
  43
+            len1 = topology.arcs[i2].length;
  44
+            i1 = 0;
  45
+            out1 = new Array(len1);
  46
+            while(i1<len1){
  47
+              topology.arcs[i2][i1][0] = (x += topology.arcs[i2][i1][0]);
  48
+              topology.arcs[i2][i1][1] = (y += topology.arcs[i2][i1][1]);
  49
+              out1[i1] = projection(tf(topology.arcs[i2][i1]));
  50
+              i1++;
  51
+            }
  52
+            projectedArcs[i2++]=out1;
  53
+            
  54
+          }
48 55
 
49 56
       // path with identity projection
50 57
       var path = d3.geo.path()
@@ -68,12 +75,12 @@
68 75
         return objects;
69 76
       }
70 77
 
71  
-      var i = 0,
72  
-          targetSizeError = 1;
  78
+     var i = 0;
73 79
       while (i++ < iterations) {
74  
-        var areas = objects.map(path.area),
75  
-            totalArea = d3.sum(areas),
76  
-            sizeErrors = [],
  80
+        var areas = objects.map(path.area);
  81
+            var totalArea = d3.sum(areas),
  82
+            sizeErrorsTot =0,
  83
+            sizeErrorsNum=0,
77 84
             meta = objects.map(function(o, j) {
78 85
               var area = Math.abs(areas[j]), // XXX: why do we have negative areas?
79 86
                   v = +values[j],
@@ -81,7 +88,8 @@
81 88
                   radius = Math.sqrt(area / Math.PI),
82 89
                   mass = Math.sqrt(desired / Math.PI) - radius,
83 90
                   sizeError = Math.max(area, desired) / Math.min(area, desired);
84  
-              sizeErrors.push(sizeError);
  91
+              sizeErrorsTot+=sizeError;
  92
+              sizeErrorsNum++;
85 93
               // console.log(o.id, "@", j, "area:", area, "value:", v, "->", desired, radius, mass, sizeError);
86 94
               return {
87 95
                 id:         o.id,
@@ -95,51 +103,49 @@
95 103
               };
96 104
             });
97 105
 
98  
-        var sizeError = d3.mean(sizeErrors),
  106
+        var sizeError = sizeErrorsTot/sizeErrorsNum,
99 107
             forceReductionFactor = 1 / (1 + sizeError);
100 108
 
101 109
         // console.log("meta:", meta);
102 110
         // console.log("  total area:", totalArea);
103 111
         // console.log("  force reduction factor:", forceReductionFactor, "mean error:", sizeError);
104 112
 
105  
-        projectedArcs.forEach(function(arc) {
106  
-          arc.forEach(function(coord) {
107  
-            // create an array of vectors: [x, y]
108  
-            var vectors = meta.map(function(d) {
109  
-              var centroid =  d.centroid,
110  
-                  mass =      d.mass,
111  
-                  radius =    d.radius,
112  
-                  theta =     angle(centroid, coord),
113  
-                  dist =      distance(centroid, coord),
114  
-                  Fij = (dist > radius)
115  
-                    ? mass * radius / dist
116  
-                    : mass *
117  
-                      (Math.pow(dist, 2) / Math.pow(radius, 2)) *
118  
-                      (4 - 3 * dist / radius);
119  
-              return [
120  
-                Fij * Math.cos(theta),
121  
-                Fij * Math.sin(theta)
122  
-              ];
123  
-            });
124  
-
125  
-            // using Fij and angles, calculate vector sum
126  
-            var delta = vectors.reduce(function(a, b) {
127  
-              return [
128  
-                a[0] + b[0],
129  
-                a[1] + b[1]
130  
-              ];
131  
-            }, [0, 0]);
132  
-
133  
-            delta[0] *= forceReductionFactor;
134  
-            delta[1] *= forceReductionFactor;
135  
-
136  
-            coord[0] += delta[0];
137  
-            coord[1] += delta[1];
138  
-          });
139  
-        });
  113
+        var len1,i1,delta,len2=projectedArcs.length,i2=0,delta,len3,i3,centroid,mass,radius,rSquared,dx,dy,distSquared,dist,Fij;
  114
+        while(i2<len2){
  115
+            len1=projectedArcs[i2].length;
  116
+            i1=0;
  117
+            while(i1<len1){
  118
+              // create an array of vectors: [x, y]
  119
+              delta = [0,0];
  120
+              len3 = meta.length;
  121
+              i3=0;
  122
+              while(i3<len3) {
  123
+                centroid =  meta[i3].centroid;
  124
+                mass =      meta[i3].mass;
  125
+                radius =    meta[i3].radius;
  126
+                rSquared = (radius*radius);
  127
+                dx = projectedArcs[i2][i1][0] - centroid[0];
  128
+                dy = projectedArcs[i2][i1][1] - centroid[1];
  129
+                distSquared = dx * dx + dy * dy;
  130
+                dist=Math.sqrt(distSquared);
  131
+                Fij = (dist > radius)
  132
+                  ? mass * radius / dist
  133
+                  : mass *
  134
+                    (distSquared / rSquared) *
  135
+                    (4 - 3 * dist / radius);
  136
+                delta[0]+=(Fij * cosArctan(dy,dx));
  137
+                delta[1]+=(Fij * sinArctan(dy,dx));
  138
+                i3++;
  139
+              }
  140
+            projectedArcs[i2][i1][0] += (delta[0]*forceReductionFactor);
  141
+            projectedArcs[i2][i1][1] += (delta[1]*forceReductionFactor);
  142
+            i1++;
  143
+          }
  144
+          i2++;
  145
+        }
140 146
 
141 147
         // break if we hit the target size error
142  
-        if (sizeError <= targetSizeError) break;
  148
+        if (sizeError <= 1) break;
143 149
       }
144 150
 
145 151
       return {
@@ -265,7 +271,18 @@
265 271
       return types[geom.type](geom.coordinates);
266 272
     };
267 273
   }
268  
-
  274
+  function cosArctan(dx,dy){
  275
+    var div = dx/dy;
  276
+    return (dy>0)?
  277
+      (1/Math.sqrt(1+(div*div))):
  278
+      (-1/Math.sqrt(1+(div*div)));
  279
+  }
  280
+  function sinArctan(dx,dy){
  281
+    var div = dx/dy;
  282
+    return (dy>0)?
  283
+      (div/Math.sqrt(1+(div*div))):
  284
+      (-div/Math.sqrt(1+(div*div)));
  285
+  }
269 286
   function copy(o) {
270 287
     return (o instanceof Array)
271 288
       ? o.map(copy)
@@ -304,7 +321,6 @@
304 321
       o.coordinates = geometryType[o.type](o.arcs);
305 322
       return o;
306 323
     }
307  
-
308 324
     var geometryType = {
309 325
       LineString: line,
310 326
       MultiLineString: polygon,
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.