@@ -12,17 +12,20 @@ var d3 = require('d3');
12
12
var map1dArray = require ( '../carpet/map_1d_array' ) ;
13
13
var makepath = require ( '../carpet/makepath' ) ;
14
14
var Drawing = require ( '../../components/drawing' ) ;
15
+ var Lib = require ( '../../lib' ) ;
15
16
16
17
var makeCrossings = require ( '../contour/make_crossings' ) ;
17
18
var findAllPaths = require ( '../contour/find_all_paths' ) ;
18
19
var contourPlot = require ( '../contour/plot' ) ;
20
+ var constants = require ( '../contour/constants' ) ;
19
21
var convertToConstraints = require ( './convert_to_constraints' ) ;
20
22
var joinAllPaths = require ( './join_all_paths' ) ;
21
23
var emptyPathinfo = require ( './empty_pathinfo' ) ;
22
24
var mapPathinfo = require ( './map_pathinfo' ) ;
23
25
var lookupCarpet = require ( '../carpet/lookup_carpetid' ) ;
24
26
var closeBoundaries = require ( './close_boundaries' ) ;
25
27
28
+
26
29
module . exports = function plot ( gd , plotinfo , cdcontours ) {
27
30
for ( var i = 0 ; i < cdcontours . length ; i ++ ) {
28
31
plotOne ( gd , plotinfo , cdcontours [ i ] ) ;
@@ -116,20 +119,178 @@ function plotOne(gd, plotinfo, cd) {
116
119
makeFills ( trace , plotGroup , xa , ya , pathinfo , perimeter , ab2p , carpet , carpetcd , contours . coloring , boundaryPath ) ;
117
120
118
121
// Draw contour lines:
119
- makeLines ( plotGroup , pathinfo , contours ) ;
122
+ makeLinesAndLabels ( plotGroup , pathinfo , gd , cd [ 0 ] , contours , plotinfo , carpet ) ;
120
123
121
124
// Clip the boundary of the plot
122
125
Drawing . setClipUrl ( plotGroup , carpet . _clipPathId ) ;
123
126
}
124
127
125
- function makeLines ( plotgroup , pathinfo , contours ) {
128
+ function makeLinesAndLabels ( plotgroup , pathinfo , gd , cd0 , contours , plotinfo , carpet ) {
126
129
var lineContainer = plotgroup . selectAll ( 'g.contourlines' ) . data ( [ 0 ] ) ;
127
130
128
131
lineContainer . enter ( ) . append ( 'g' )
129
132
. classed ( 'contourlines' , true ) ;
130
133
131
- contourPlot . createLines ( lineContainer ,
132
- contours . showlines !== false , pathinfo ) ;
134
+ var showLines = contours . showlines !== false ;
135
+ var showLabels = contours . showlabels ;
136
+ var clipLinesForLabels = showLines && showLabels ;
137
+
138
+ // Even if we're not going to show lines, we need to create them
139
+ // if we're showing labels, because the fill paths include the perimeter
140
+ // so can't be used to position the labels correctly.
141
+ // In this case we'll remove the lines after making the labels.
142
+ var linegroup = contourPlot . createLines ( lineContainer , showLines || showLabels , pathinfo ) ;
143
+
144
+ var lineClip = contourPlot . createLineClip ( lineContainer , clipLinesForLabels ,
145
+ gd . _fullLayout . _defs , cd0 . trace . uid ) ;
146
+
147
+ var labelGroup = plotgroup . selectAll ( 'g.contourlabels' )
148
+ . data ( showLabels ? [ 0 ] : [ ] ) ;
149
+
150
+ labelGroup . exit ( ) . remove ( ) ;
151
+
152
+ labelGroup . enter ( ) . append ( 'g' )
153
+ . classed ( 'contourlabels' , true ) ;
154
+
155
+ if ( showLabels ) {
156
+ var xa = plotinfo . xaxis ;
157
+ var ya = plotinfo . yaxis ;
158
+ var xLen = xa . _length ;
159
+ var yLen = ya . _length ;
160
+ // for simplicity use the xy box for label clipping outline.
161
+ var labelClipPathData = [ [
162
+ [ 0 , 0 ] ,
163
+ [ xLen , 0 ] ,
164
+ [ xLen , yLen ] ,
165
+ [ 0 , yLen ]
166
+ ] ] ;
167
+
168
+
169
+ var labelData = [ ] ;
170
+
171
+ // invalidate the getTextLocation cache in case paths changed
172
+ Lib . clearLocationCache ( ) ;
173
+
174
+ var contourFormat = contourPlot . labelFormatter ( contours , cd0 . t . cb , gd . _fullLayout ) ;
175
+
176
+ var dummyText = Drawing . tester . append ( 'text' )
177
+ . attr ( 'data-notex' , 1 )
178
+ . call ( Drawing . font , contours . labelfont ) ;
179
+
180
+ // for now at least, contourcarpet pushes labels only away from
181
+ // the xy box edges, not the edges of the carpet.
182
+ // TODO: is this OK?
183
+ var bounds = {
184
+ left : 0 ,
185
+ right : xLen ,
186
+ center : xLen / 2 ,
187
+ top : 0 ,
188
+ bottom : yLen ,
189
+ middle : yLen / 2
190
+ } ;
191
+
192
+ var plotDiagonal = Math . sqrt ( xLen * xLen + yLen * yLen ) ;
193
+
194
+ // the path length to use to scale the number of labels to draw:
195
+ var normLength = constants . LABELDISTANCE * plotDiagonal /
196
+ Math . max ( 1 , pathinfo . length / constants . LABELINCREASE ) ;
197
+
198
+ linegroup . each ( function ( d ) {
199
+ var textOpts = contourPlot . calcTextOpts ( d . level , contourFormat , dummyText , gd ) ;
200
+
201
+ d3 . select ( this ) . selectAll ( 'path' ) . each ( function ( pathData ) {
202
+ var path = this ;
203
+ var pathBounds = Lib . getVisibleSegment ( path , bounds , textOpts . height / 2 ) ;
204
+ if ( ! pathBounds ) return ;
205
+
206
+ constrainToCarpet ( path , pathData , d , pathBounds , carpet , textOpts . height ) ;
207
+
208
+ if ( pathBounds . len < ( textOpts . width + textOpts . height ) * constants . LABELMIN ) return ;
209
+
210
+ var maxLabels = Math . min ( Math . ceil ( pathBounds . len / normLength ) ,
211
+ constants . LABELMAX ) ;
212
+
213
+ for ( var i = 0 ; i < maxLabels ; i ++ ) {
214
+ var loc = contourPlot . findBestTextLocation ( path , pathBounds , textOpts ,
215
+ labelData , bounds ) ;
216
+
217
+ if ( ! loc ) break ;
218
+
219
+ contourPlot . addLabelData ( loc , textOpts , labelData , labelClipPathData ) ;
220
+ }
221
+ } ) ;
222
+ } ) ;
223
+
224
+ dummyText . remove ( ) ;
225
+
226
+ contourPlot . drawLabels ( labelGroup , labelData , gd , lineClip ,
227
+ clipLinesForLabels ? labelClipPathData : null ) ;
228
+ }
229
+
230
+ if ( showLabels && ! showLines ) linegroup . remove ( ) ;
231
+ }
232
+
233
+ // figure out if this path goes off the edge of the carpet
234
+ // and shorten the part we call visible to keep labels away from the edge
235
+ function constrainToCarpet ( path , pathData , levelData , pathBounds , carpet , textHeight ) {
236
+ var pathABData ;
237
+ for ( var i = 0 ; i < levelData . pedgepaths . length ; i ++ ) {
238
+ if ( pathData === levelData . pedgepaths [ i ] ) {
239
+ pathABData = levelData . edgepaths [ i ] ;
240
+ }
241
+ }
242
+ if ( ! pathABData ) return ;
243
+
244
+ var aMin = carpet . a [ 0 ] ;
245
+ var aMax = carpet . a [ carpet . a . length - 1 ] ;
246
+ var bMin = carpet . b [ 0 ] ;
247
+ var bMax = carpet . b [ carpet . b . length - 1 ] ;
248
+
249
+ function getOffset ( abPt , pathVector ) {
250
+ var offset = 0 ;
251
+ var edgeVector ;
252
+ var dAB = 0.1 ;
253
+ if ( Math . abs ( abPt [ 0 ] - aMin ) < dAB || Math . abs ( abPt [ 0 ] - aMax ) < dAB ) {
254
+ edgeVector = normalizeVector ( carpet . dxydb_rough ( abPt [ 0 ] , abPt [ 1 ] , dAB ) ) ;
255
+ offset = Math . max ( offset , textHeight * vectorTan ( pathVector , edgeVector ) / 2 ) ;
256
+ }
257
+
258
+ if ( Math . abs ( abPt [ 1 ] - bMin ) < dAB || Math . abs ( abPt [ 1 ] - bMax ) < dAB ) {
259
+ edgeVector = normalizeVector ( carpet . dxyda_rough ( abPt [ 0 ] , abPt [ 1 ] , dAB ) ) ;
260
+ offset = Math . max ( offset , textHeight * vectorTan ( pathVector , edgeVector ) / 2 ) ;
261
+ }
262
+ return offset ;
263
+ }
264
+
265
+ var startVector = getUnitVector ( path , 0 , 1 ) ;
266
+ var endVector = getUnitVector ( path , pathBounds . total , pathBounds . total - 1 ) ;
267
+ var minStart = getOffset ( pathABData [ 0 ] , startVector ) ;
268
+ var maxEnd = pathBounds . total - getOffset ( pathABData [ pathABData . length - 1 ] , endVector ) ;
269
+
270
+ if ( pathBounds . min < minStart ) pathBounds . min = minStart ;
271
+ if ( pathBounds . max > maxEnd ) pathBounds . max = maxEnd ;
272
+
273
+ pathBounds . len = pathBounds . max - pathBounds . min ;
274
+ }
275
+
276
+ function getUnitVector ( path , p0 , p1 ) {
277
+ var pt0 = path . getPointAtLength ( p0 ) ;
278
+ var pt1 = path . getPointAtLength ( p1 ) ;
279
+ var dx = pt1 . x - pt0 . x ;
280
+ var dy = pt1 . y - pt0 . y ;
281
+ var len = Math . sqrt ( dx * dx + dy * dy ) ;
282
+ return [ dx / len , dy / len ] ;
283
+ }
284
+
285
+ function normalizeVector ( v ) {
286
+ var len = Math . sqrt ( v [ 0 ] * v [ 0 ] + v [ 1 ] * v [ 1 ] ) ;
287
+ return [ v [ 0 ] / len , v [ 1 ] / len ] ;
288
+ }
289
+
290
+ function vectorTan ( v0 , v1 ) {
291
+ var cos = Math . abs ( v0 [ 0 ] * v1 [ 0 ] + v0 [ 1 ] * v1 [ 1 ] ) ;
292
+ var sin = Math . sqrt ( 1 - cos * cos ) ;
293
+ return sin / cos ;
133
294
}
134
295
135
296
function makeBackground ( plotgroup , clipsegments , xaxis , yaxis , isConstraint , coloring ) {
0 commit comments