| (function() { | 
 |  | 
 | d3.labeler = function() { | 
 |   var lab = [], | 
 |       anc = [], | 
 |       w = 1, // box width | 
 |       h = 1, // box width | 
 |       update, | 
 |       labeler = {}; | 
 |  | 
 |   var max_move = 5.0/20, | 
 |       max_angle = 0.5, | 
 |       acc = 0; | 
 |       rej = 0; | 
 |  | 
 |   // weights | 
 |   var w_len = 0.2, // leader line length  | 
 |       w_inter = 1.0, // leader line intersection | 
 |       w_lab2 = 30.0, // label-label overlap | 
 |       w_lab_anc = 30.0; // label-anchor overlap | 
 |       w_orient = 3.0; // orientation bias | 
 |  | 
 |   // booleans for user defined functions | 
 |   var user_energy = false, | 
 |       user_schedule = false; | 
 |  | 
 |   var user_defined_energy,  | 
 |       user_defined_schedule; | 
 |  | 
 |   energy = function(index) { | 
 |   // energy function, tailored for label placement | 
 |  | 
 |       var m = lab.length,  | 
 |           ener = 0, | 
 |           dx = lab[index].x - anc[index].x, | 
 |           dy = anc[index].y - lab[index].y, | 
 |           dist = Math.sqrt(dx * dx + dy * dy), | 
 |           overlap = true, | 
 |           amount = 0 | 
 |           theta = 0; | 
 |  | 
 |       // penalty for length of leader line | 
 |       if (dist > 0) ener += dist * w_len; | 
 |  | 
 |       // label orientation bias | 
 |       dx /= dist; | 
 |       dy /= dist; | 
 |       if (dx > 0 && dy > 0) { ener += 0 * w_orient; } | 
 |       else if (dx < 0 && dy > 0) { ener += 1 * w_orient; } | 
 |       else if (dx < 0 && dy < 0) { ener += 2 * w_orient; } | 
 |       else { ener += 3 * w_orient; } | 
 |  | 
 |       var x21 = lab[index].x, | 
 |           y21 = lab[index].y - lab[index].height + 2.0, | 
 |           x22 = lab[index].x + lab[index].width, | 
 |           y22 = lab[index].y + 2.0; | 
 |       var x11, x12, y11, y12, x_overlap, y_overlap, overlap_area; | 
 |  | 
 |       for (var i = 0; i < m; i++) { | 
 |         if (i != index) { | 
 |  | 
 |           // penalty for intersection of leader lines | 
 |           overlap = intersect(anc[index].x, lab[index].x, anc[i].x, lab[i].x, | 
 |                           anc[index].y, lab[index].y, anc[i].y, lab[i].y); | 
 |           if (overlap) ener += w_inter; | 
 |  | 
 |           // penalty for label-label overlap | 
 |           x11 = lab[i].x; | 
 |           y11 = lab[i].y - lab[i].height + 2.0; | 
 |           x12 = lab[i].x + lab[i].width; | 
 |           y12 = lab[i].y + 2.0; | 
 |           x_overlap = Math.max(0, Math.min(x12,x22) - Math.max(x11,x21)); | 
 |           y_overlap = Math.max(0, Math.min(y12,y22) - Math.max(y11,y21)); | 
 |           overlap_area = x_overlap * y_overlap; | 
 |           ener += (overlap_area * w_lab2); | 
 |           } | 
 |  | 
 |           // penalty for label-anchor overlap | 
 |           x11 = anc[i].x - anc[i].r; | 
 |           y11 = anc[i].y - anc[i].r; | 
 |           x12 = anc[i].x + anc[i].r; | 
 |           y12 = anc[i].y + anc[i].r; | 
 |           x_overlap = Math.max(0, Math.min(x12,x22) - Math.max(x11,x21)); | 
 |           y_overlap = Math.max(0, Math.min(y12,y22) - Math.max(y11,y21)); | 
 |           overlap_area = x_overlap * y_overlap; | 
 |           ener += (overlap_area * w_lab_anc); | 
 |  | 
 |       } | 
 |       return ener; | 
 |   }; | 
 |  | 
 |   mcmove = function(currT) { | 
 |   // Monte Carlo translation move | 
 |  | 
 |       // select a random label | 
 |       var i = Math.floor(Math.random() * lab.length);  | 
 |  | 
 |       // save old coordinates | 
 |       var x_old = lab[i].x; | 
 |       var y_old = lab[i].y; | 
 |  | 
 |       // old energy | 
 |       var old_energy; | 
 |       if (user_energy) {old_energy = user_defined_energy(i, lab, anc)} | 
 |       else {old_energy = energy(i)} | 
 |  | 
 |       // random translation | 
 |       lab[i].x += (Math.random() - 0.5) * max_move; | 
 |       lab[i].y += (Math.random() - 0.5) * max_move; | 
 |  | 
 |       // hard wall boundaries | 
 |       if (lab[i].x > w) lab[i].x = x_old; | 
 |       if (lab[i].x < 0) lab[i].x = x_old; | 
 |       if (lab[i].y > h) lab[i].y = y_old; | 
 |       if (lab[i].y < 0) lab[i].y = y_old; | 
 |  | 
 |       // new energy | 
 |       var new_energy; | 
 |       if (user_energy) {new_energy = user_defined_energy(i, lab, anc)} | 
 |       else {new_energy = energy(i)} | 
 |  | 
 |       // delta E | 
 |       var delta_energy = new_energy - old_energy; | 
 |  | 
 |       if (Math.random() < Math.exp(-delta_energy / currT)) { | 
 |         acc += 1; | 
 |       } else { | 
 |         // move back to old coordinates | 
 |         lab[i].x = x_old; | 
 |         lab[i].y = y_old; | 
 |         rej += 1; | 
 |       } | 
 |  | 
 |   }; | 
 |  | 
 |   mcrotate = function(currT) { | 
 |   // Monte Carlo rotation move | 
 |  | 
 |       // select a random label | 
 |       var i = Math.floor(Math.random() * lab.length);  | 
 |  | 
 |       // save old coordinates | 
 |       var x_old = lab[i].x; | 
 |       var y_old = lab[i].y; | 
 |  | 
 |       // old energy | 
 |       var old_energy; | 
 |       if (user_energy) {old_energy = user_defined_energy(i, lab, anc)} | 
 |       else {old_energy = energy(i)} | 
 |  | 
 |       // random angle | 
 |       var angle = (Math.random() - 0.5) * max_angle; | 
 |  | 
 |       var s = Math.sin(angle); | 
 |       var c = Math.cos(angle); | 
 |  | 
 |       // translate label (relative to anchor at origin): | 
 |       lab[i].x -= anc[i].x | 
 |       lab[i].y -= anc[i].y | 
 |  | 
 |       // rotate label | 
 |       var x_new = lab[i].x * c - lab[i].y * s, | 
 |           y_new = lab[i].x * s + lab[i].y * c; | 
 |  | 
 |       // translate label back | 
 |       lab[i].x = x_new + anc[i].x | 
 |       lab[i].y = y_new + anc[i].y | 
 |  | 
 |       // hard wall boundaries | 
 |       if (lab[i].x > w) lab[i].x = x_old; | 
 |       if (lab[i].x < 0) lab[i].x = x_old; | 
 |       if (lab[i].y > h) lab[i].y = y_old; | 
 |       if (lab[i].y < 0) lab[i].y = y_old; | 
 |  | 
 |       // new energy | 
 |       var new_energy; | 
 |       if (user_energy) {new_energy = user_defined_energy(i, lab, anc)} | 
 |       else {new_energy = energy(i)} | 
 |  | 
 |       // delta E | 
 |       var delta_energy = new_energy - old_energy; | 
 |  | 
 |       if (Math.random() < Math.exp(-delta_energy / currT)) { | 
 |         acc += 1; | 
 |       } else { | 
 |         // move back to old coordinates | 
 |         lab[i].x = x_old; | 
 |         lab[i].y = y_old; | 
 |         rej += 1; | 
 |       } | 
 |        | 
 |   }; | 
 |  | 
 |   intersect = function(x1, x2, x3, x4, y1, y2, y3, y4) { | 
 |   // returns true if two lines intersect, else false | 
 |   // from http://paulbourke.net/geometry/lineline2d/ | 
 |  | 
 |     var mua, mub; | 
 |     var denom, numera, numerb; | 
 |  | 
 |     denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); | 
 |     numera = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); | 
 |     numerb = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); | 
 |  | 
 |     /* Is the intersection along the the segments */ | 
 |     mua = numera / denom; | 
 |     mub = numerb / denom; | 
 |     if (!(mua < 0 || mua > 1 || mub < 0 || mub > 1)) { | 
 |         return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   cooling_schedule = function(currT, initialT, nsweeps) { | 
 |   // linear cooling | 
 |     return (currT - (initialT / nsweeps)); | 
 |   } | 
 |  | 
 |   labeler.start2 = function(nsweeps) { | 
 |   // main simulated annealing function | 
 |       var m = lab.length, | 
 |           currT = 1.0, | 
 |           initialT = 1.0; | 
 |  | 
 |       for (var i = 0; i < nsweeps; i++) { | 
 |         for (var j = 0; j < m; j++) {  | 
 |           if (Math.random() < 0.5) { mcmove(currT); } | 
 |           else { mcrotate(currT); } | 
 |         } | 
 |         currT = cooling_schedule(currT, initialT, nsweeps); | 
 |       } | 
 |   }; | 
 |  | 
 |   labeler.start = function(nsweeps) { | 
 |   // main simulated annealing function | 
 |       if(nsweeps <= 0) | 
 |           return; | 
 |  | 
 |       var m = lab.length, | 
 |           currT = 1.0, | 
 |           initialT = 1.0; | 
 |  | 
 |       for (var j = 0; j < m; j++) {  | 
 |           if (Math.random() < 0.5) { mcmove(currT); } | 
 |           else { mcrotate(currT); } | 
 |       } | 
 |       update(); | 
 |       currT = cooling_schedule(currT, initialT, nsweeps); | 
 |       setTimeout(labeler.start(nsweeps-1), 2); | 
 |   }; | 
 |  | 
 |   labeler.width = function(x) { | 
 |   // users insert graph width | 
 |     if (!arguments.length) return w; | 
 |     w = x; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.height = function(x) { | 
 |   // users insert graph height | 
 |     if (!arguments.length) return h; | 
 |     h = x;     | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.label = function(x) { | 
 |   // users insert label positions | 
 |     if (!arguments.length) return lab; | 
 |     lab = x; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.update = function(x) { | 
 |   // users insert label positions | 
 |     if (!arguments.length) return null; | 
 |     update = x; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.anchor = function(x) { | 
 |   // users insert anchor positions | 
 |     if (!arguments.length) return anc; | 
 |     anc = x; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.alt_energy = function(x) { | 
 |   // user defined energy | 
 |     if (!arguments.length) return energy; | 
 |     user_defined_energy = x; | 
 |     user_energy = true; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   labeler.alt_schedule = function(x) { | 
 |   // user defined cooling_schedule | 
 |     if (!arguments.length) return  cooling_schedule; | 
 |     user_defined_schedule = x; | 
 |     user_schedule = true; | 
 |     return labeler; | 
 |   }; | 
 |  | 
 |   return labeler; | 
 | }; | 
 |  | 
 | })(); | 
 |  |