@@ -5,6 +5,25 @@ var Queue = require('tinyqueue');
55module . exports = polylabel ;
66module . exports . default = polylabel ;
77
8+ function getFitnessFunc ( centroid , polygonSize ) {
9+ var maxSize = Math . max ( polygonSize [ 0 ] , polygonSize [ 1 ] ) ;
10+
11+ return ( function ( cellCenter , distancePolygon ) {
12+ if ( distancePolygon <= 0 ) {
13+ return distancePolygon ;
14+ }
15+
16+ var d = [
17+ cellCenter [ 0 ] - centroid [ 0 ] ,
18+ cellCenter [ 1 ] - centroid [ 1 ]
19+ ] ;
20+
21+ var distanceCentroid = Math . sqrt ( ( d [ 0 ] * d [ 0 ] ) + ( d [ 1 ] * d [ 1 ] ) ) ;
22+
23+ return distancePolygon * ( 1 - distanceCentroid / maxSize ) ;
24+ } ) ;
25+ }
26+
827function polylabel ( polygon , precision , debug ) {
928 precision = precision || 1.0 ;
1029
@@ -26,21 +45,20 @@ function polylabel(polygon, precision, debug) {
2645 if ( cellSize === 0 ) return [ minX , minY ] ;
2746
2847 // a priority queue of cells in order of their "potential" (max distance to polygon)
29- var cellQueue = new Queue ( undefined , compareMax ) ;
48+ var cellQueue = new Queue ( null , compareMax ) ;
49+
50+ var centroid = getCentroid ( polygon ) ;
51+ var fitnessFunc = getFitnessFunc ( centroid , [ width , height ] ) ;
3052
3153 // cover polygon with initial cells
3254 for ( var x = minX ; x < maxX ; x += cellSize ) {
3355 for ( var y = minY ; y < maxY ; y += cellSize ) {
34- cellQueue . push ( new Cell ( x + h , y + h , h , polygon ) ) ;
56+ cellQueue . push ( new Cell ( x + h , y + h , h , polygon , fitnessFunc ) ) ;
3557 }
3658 }
3759
3860 // take centroid as the first best guess
39- var bestCell = getCentroidCell ( polygon ) ;
40-
41- // special case for rectangular polygons
42- var bboxCell = new Cell ( minX + width / 2 , minY + height / 2 , 0 , polygon ) ;
43- if ( bboxCell . d > bestCell . d ) bestCell = bboxCell ;
61+ var bestCell = new Cell ( centroid [ 0 ] , centroid [ 1 ] , 0 , polygon , fitnessFunc ) ;
4462
4563 var numProbes = cellQueue . length ;
4664
@@ -49,20 +67,20 @@ function polylabel(polygon, precision, debug) {
4967 var cell = cellQueue . pop ( ) ;
5068
5169 // update the best cell if we found a better one
52- if ( cell . d > bestCell . d ) {
70+ if ( cell . fitness > bestCell . fitness ) {
5371 bestCell = cell ;
5472 if ( debug ) console . log ( 'found best %d after %d probes' , Math . round ( 1e4 * cell . d ) / 1e4 , numProbes ) ;
5573 }
5674
5775 // do not drill down further if there's no chance of a better solution
58- if ( cell . max - bestCell . d <= precision ) continue ;
76+ if ( cell . maxFitness - bestCell . fitness <= precision ) continue ;
5977
6078 // split the cell into four cells
6179 h = cell . h / 2 ;
62- cellQueue . push ( new Cell ( cell . x - h , cell . y - h , h , polygon ) ) ;
63- cellQueue . push ( new Cell ( cell . x + h , cell . y - h , h , polygon ) ) ;
64- cellQueue . push ( new Cell ( cell . x - h , cell . y + h , h , polygon ) ) ;
65- cellQueue . push ( new Cell ( cell . x + h , cell . y + h , h , polygon ) ) ;
80+ cellQueue . push ( new Cell ( cell . x - h , cell . y - h , h , polygon , fitnessFunc ) ) ;
81+ cellQueue . push ( new Cell ( cell . x + h , cell . y - h , h , polygon , fitnessFunc ) ) ;
82+ cellQueue . push ( new Cell ( cell . x - h , cell . y + h , h , polygon , fitnessFunc ) ) ;
83+ cellQueue . push ( new Cell ( cell . x + h , cell . y + h , h , polygon , fitnessFunc ) ) ;
6684 numProbes += 4 ;
6785 }
6886
@@ -75,15 +93,16 @@ function polylabel(polygon, precision, debug) {
7593}
7694
7795function compareMax ( a , b ) {
78- return b . max - a . max ;
96+ return b . maxFitness - a . maxFitness ;
7997}
8098
81- function Cell ( x , y , h , polygon ) {
99+ function Cell ( x , y , h , polygon , fitnessFunc ) {
82100 this . x = x ; // cell center x
83101 this . y = y ; // cell center y
84102 this . h = h ; // half the cell size
85103 this . d = pointToPolygonDist ( x , y , polygon ) ; // distance from cell center to polygon
86- this . max = this . d + this . h * Math . SQRT2 ; // max distance to polygon within a cell
104+ this . fitness = fitnessFunc ( [ x , y ] , this . d ) ;
105+ this . maxFitness = fitnessFunc ( [ x , y ] , this . d + this . h * Math . SQRT2 ) ; // max distance to polygon within a cell
87106}
88107
89108// signed distance from point to polygon outline (negative if point is outside)
@@ -109,7 +128,7 @@ function pointToPolygonDist(x, y, polygon) {
109128}
110129
111130// get polygon centroid
112- function getCentroidCell ( polygon ) {
131+ function getCentroid ( polygon ) {
113132 var area = 0 ;
114133 var x = 0 ;
115134 var y = 0 ;
@@ -123,8 +142,7 @@ function getCentroidCell(polygon) {
123142 y += ( a [ 1 ] + b [ 1 ] ) * f ;
124143 area += f * 3 ;
125144 }
126- if ( area === 0 ) return new Cell ( points [ 0 ] [ 0 ] , points [ 0 ] [ 1 ] , 0 , polygon ) ;
127- return new Cell ( x / area , y / area , 0 , polygon ) ;
145+ return ( area === 0 ) ? points [ 0 ] : [ x / area , y / area ] ;
128146}
129147
130148// get squared distance from a point to a segment
0 commit comments