1
- import { extent , nice , thresholdSturges , ticks } from "d3-array" ;
1
+ import { blur , extent , nice , thresholdSturges , ticks } from "d3-array" ;
2
2
import { slice } from "./array.js" ;
3
3
import ascending from "./ascending.js" ;
4
4
import area from "./area.js" ;
5
5
import constant from "./constant.js" ;
6
6
import contains from "./contains.js" ;
7
7
import noop from "./noop.js" ;
8
8
9
- var cases = [
9
+ const cases = [
10
10
[ ] ,
11
11
[ [ [ 1.0 , 1.5 ] , [ 0.5 , 1.0 ] ] ] ,
12
12
[ [ [ 1.5 , 1.0 ] , [ 1.0 , 1.5 ] ] ] ,
@@ -25,14 +25,20 @@ var cases = [
25
25
[ ]
26
26
] ;
27
27
28
+ const blurEdges = 0.5 ;
29
+
30
+ function clamp ( x , lo , hi ) {
31
+ return x < lo ? lo : x > hi ? hi : x ;
32
+ }
33
+
28
34
export default function ( ) {
29
- var dx = 1 ,
30
- dy = 1 ,
31
- threshold = thresholdSturges ,
32
- smooth = smoothLinear ;
35
+ let dx = 1 ;
36
+ let dy = 1 ;
37
+ let threshold = thresholdSturges ;
38
+ let smooth = smoothLinear ;
33
39
34
40
function contours ( values ) {
35
- var tz = threshold ( values ) ;
41
+ let tz = threshold ( values ) ;
36
42
37
43
// Convert number of thresholds into uniform thresholds.
38
44
if ( ! Array . isArray ( tz ) ) {
@@ -53,17 +59,45 @@ export default function() {
53
59
const v = value == null ? NaN : + value ;
54
60
if ( isNaN ( v ) ) throw new Error ( `invalid value: ${ value } ` ) ;
55
61
56
- var polygons = [ ] ,
57
- holes = [ ] ;
62
+ // Don’t round the corners by clamping values on the edge.
63
+ const bottom = values . slice ( 0 , dx ) ;
64
+ const top = values . slice ( - dx ) ;
65
+ const left = Array . from ( { length : dy } , ( _ , i ) => values [ i * dx ] ) ;
66
+ const right = Array . from ( { length : dy } , ( _ , i ) => values [ i * dx + dx - 1 ] ) ;
67
+ blur ( bottom , blurEdges ) ;
68
+ blur ( top , blurEdges ) ;
69
+ blur ( left , blurEdges ) ;
70
+ blur ( right , blurEdges ) ;
71
+
72
+ function get ( x , y ) {
73
+ const x0 = clamp ( x , 0 , dx - 1 ) ;
74
+ const y0 = clamp ( y , 0 , dy - 1 ) ;
75
+ if ( y < 0 ) return bottom [ x0 ] ;
76
+ if ( y >= dy ) return top [ x0 ] ;
77
+ if ( x < 0 ) return left [ y0 ] ;
78
+ if ( x >= dx ) return right [ y0 ] ;
79
+ return values [ x0 + y0 * dx ] ;
80
+ }
58
81
59
- isorings ( values , v , function ( ring ) {
60
- smooth ( ring , values , v ) ;
61
- if ( area ( ring ) > 0 ) polygons . push ( [ ring ] ) ;
62
- else holes . push ( ring ) ;
82
+ const polygons = [ ] ;
83
+ const holes = [ ] ;
84
+
85
+ isorings ( get , value , function ( ring ) {
86
+ smooth ( ring , get , value ) ;
87
+ const r = [ ] ;
88
+ let x0 , y0 ;
89
+ for ( const point of ring ) {
90
+ const x = clamp ( point [ 0 ] , 0 , dx ) ;
91
+ const y = clamp ( point [ 1 ] , 0 , dy ) ;
92
+ if ( x !== x0 || y !== y0 ) r . push ( [ ( x0 = x ) , ( y0 = y ) ] ) ;
93
+ }
94
+ const a = area ( r ) ;
95
+ if ( a > 0 ) polygons . push ( [ r ] ) ;
96
+ else if ( a < 0 ) holes . push ( r ) ;
63
97
} ) ;
64
98
65
99
holes . forEach ( function ( hole ) {
66
- for ( var i = 0 , n = polygons . length , polygon ; i < n ; ++ i ) {
100
+ for ( let i = 0 , n = polygons . length , polygon ; i < n ; ++ i ) {
67
101
if ( contains ( ( polygon = polygons [ i ] ) [ 0 ] , hole ) !== - 1 ) {
68
102
polygon . push ( hole ) ;
69
103
return ;
@@ -80,51 +114,52 @@ export default function() {
80
114
81
115
// Marching squares with isolines stitched into rings.
82
116
// Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
83
- function isorings ( values , value , callback ) {
84
- var fragmentByStart = new Array ,
85
- fragmentByEnd = new Array ,
86
- x , y , t0 , t1 , t2 , t3 ;
117
+ function isorings ( get , value , callback ) {
118
+ const test = ( x , y ) => above ( get ( x , y ) , value ) ;
119
+ const fragmentByStart = new Array ;
120
+ const fragmentByEnd = new Array ;
121
+ let x , y , t0 , t1 , t2 , t3 ;
87
122
88
123
// Special case for the first row (y = -1, t2 = t3 = 0).
89
- x = y = - 1 ;
90
- t1 = above ( values [ 0 ] , value ) ;
124
+ x = y = - 2 ;
125
+ t1 = test ( - 1 , - 1 ) ;
91
126
cases [ t1 << 1 ] . forEach ( stitch ) ;
92
- while ( ++ x < dx - 1 ) {
93
- t0 = t1 , t1 = above ( values [ x + 1 ] , value ) ;
127
+ while ( ++ x < dx ) {
128
+ t0 = t1 , t1 = test ( x + 1 , - 1 ) ;
94
129
cases [ t0 | t1 << 1 ] . forEach ( stitch ) ;
95
130
}
96
131
cases [ t1 << 0 ] . forEach ( stitch ) ;
97
132
98
133
// General case for the intermediate rows.
99
- while ( ++ y < dy - 1 ) {
100
- x = - 1 ;
101
- t1 = above ( values [ y * dx + dx ] , value ) ;
102
- t2 = above ( values [ y * dx ] , value ) ;
134
+ while ( ++ y < dy ) {
135
+ x = - 2 ;
136
+ t1 = test ( x + 1 , y + 1 ) ;
137
+ t2 = test ( x + 1 , y ) ;
103
138
cases [ t1 << 1 | t2 << 2 ] . forEach ( stitch ) ;
104
- while ( ++ x < dx - 1 ) {
105
- t0 = t1 , t1 = above ( values [ y * dx + dx + x + 1 ] , value ) ;
106
- t3 = t2 , t2 = above ( values [ y * dx + x + 1 ] , value ) ;
139
+ while ( ++ x < dx ) {
140
+ t0 = t1 , t1 = test ( x + 1 , y + 1 ) ;
141
+ t3 = t2 , t2 = test ( x + 1 , y ) ;
107
142
cases [ t0 | t1 << 1 | t2 << 2 | t3 << 3 ] . forEach ( stitch ) ;
108
143
}
109
144
cases [ t1 | t2 << 3 ] . forEach ( stitch ) ;
110
145
}
111
146
112
147
// Special case for the last row (y = dy - 1, t0 = t1 = 0).
113
- x = - 1 ;
114
- t2 = values [ y * dx ] >= value ;
148
+ x = - 2 ;
149
+ t2 = test ( x , y ) ;
115
150
cases [ t2 << 2 ] . forEach ( stitch ) ;
116
- while ( ++ x < dx - 1 ) {
117
- t3 = t2 , t2 = above ( values [ y * dx + x + 1 ] , value ) ;
151
+ while ( ++ x < dx ) {
152
+ t3 = t2 , t2 = test ( x + 1 , y ) ;
118
153
cases [ t2 << 2 | t3 << 3 ] . forEach ( stitch ) ;
119
154
}
120
155
cases [ t2 << 3 ] . forEach ( stitch ) ;
121
156
122
157
function stitch ( line ) {
123
- var start = [ line [ 0 ] [ 0 ] + x , line [ 0 ] [ 1 ] + y ] ,
124
- end = [ line [ 1 ] [ 0 ] + x , line [ 1 ] [ 1 ] + y ] ,
125
- startIndex = index ( start ) ,
126
- endIndex = index ( end ) ,
127
- f , g ;
158
+ const start = [ line [ 0 ] [ 0 ] + x , line [ 0 ] [ 1 ] + y ] ;
159
+ const end = [ line [ 1 ] [ 0 ] + x , line [ 1 ] [ 1 ] + y ] ;
160
+ const startIndex = index ( start ) ;
161
+ const endIndex = index ( end ) ;
162
+ let f , g ;
128
163
if ( f = fragmentByEnd [ startIndex ] ) {
129
164
if ( g = fragmentByStart [ endIndex ] ) {
130
165
delete fragmentByEnd [ f . end ] ;
@@ -165,27 +200,24 @@ export default function() {
165
200
return point [ 0 ] * 2 + point [ 1 ] * ( dx + 1 ) * 4 ;
166
201
}
167
202
168
- function smoothLinear ( ring , values , value ) {
203
+ function smoothLinear ( ring , get , value ) {
169
204
ring . forEach ( function ( point ) {
170
- var x = point [ 0 ] ,
171
- y = point [ 1 ] ,
172
- xt = x | 0 ,
173
- yt = y | 0 ,
174
- v1 = valid ( values [ yt * dx + xt ] ) ;
175
- if ( x > 0 && x < dx && xt === x ) {
176
- point [ 0 ] = smooth1 ( x , valid ( values [ yt * dx + xt - 1 ] ) , v1 , value ) ;
177
- }
178
- if ( y > 0 && y < dy && yt === y ) {
179
- point [ 1 ] = smooth1 ( y , valid ( values [ ( yt - 1 ) * dx + xt ] ) , v1 , value ) ;
180
- }
205
+ const x = point [ 0 ] ;
206
+ const y = point [ 1 ] ;
207
+ const xt = x | 0 ;
208
+ const yt = y | 0 ;
209
+ const v1 = valid ( get ( xt , yt ) ) ;
210
+ if ( x > 0 && x < dx && xt === x ) point [ 0 ] = smooth1 ( x , valid ( get ( xt - 1 , yt ) ) , v1 , value ) ;
211
+ if ( y > 0 && y < dy && yt === y ) point [ 1 ] = smooth1 ( y , valid ( get ( xt , yt - 1 ) ) , v1 , value ) ;
181
212
} ) ;
182
213
}
183
214
184
215
contours . contour = contour ;
185
216
186
217
contours . size = function ( _ ) {
187
218
if ( ! arguments . length ) return [ dx , dy ] ;
188
- var _0 = Math . floor ( _ [ 0 ] ) , _1 = Math . floor ( _ [ 1 ] ) ;
219
+ const _0 = Math . floor ( _ [ 0 ] ) ;
220
+ const _1 = Math . floor ( _ [ 1 ] ) ;
189
221
if ( ! ( _0 >= 0 && _1 >= 0 ) ) throw new Error ( "invalid size" ) ;
190
222
return dx = _0 , dy = _1 , contours ;
191
223
} ;
0 commit comments