@@ -12,17 +12,20 @@ var d3 = require('d3');
1212var map1dArray = require ( '../carpet/map_1d_array' ) ;
1313var makepath = require ( '../carpet/makepath' ) ;
1414var Drawing = require ( '../../components/drawing' ) ;
15+ var Lib = require ( '../../lib' ) ;
1516
1617var makeCrossings = require ( '../contour/make_crossings' ) ;
1718var findAllPaths = require ( '../contour/find_all_paths' ) ;
1819var contourPlot = require ( '../contour/plot' ) ;
20+ var constants = require ( '../contour/constants' ) ;
1921var convertToConstraints = require ( './convert_to_constraints' ) ;
2022var joinAllPaths = require ( './join_all_paths' ) ;
2123var emptyPathinfo = require ( './empty_pathinfo' ) ;
2224var mapPathinfo = require ( './map_pathinfo' ) ;
2325var lookupCarpet = require ( '../carpet/lookup_carpetid' ) ;
2426var closeBoundaries = require ( './close_boundaries' ) ;
2527
28+
2629module . exports = function plot ( gd , plotinfo , cdcontours ) {
2730 for ( var i = 0 ; i < cdcontours . length ; i ++ ) {
2831 plotOne ( gd , plotinfo , cdcontours [ i ] ) ;
@@ -116,20 +119,178 @@ function plotOne(gd, plotinfo, cd) {
116119 makeFills ( trace , plotGroup , xa , ya , pathinfo , perimeter , ab2p , carpet , carpetcd , contours . coloring , boundaryPath ) ;
117120
118121 // Draw contour lines:
119- makeLines ( plotGroup , pathinfo , contours ) ;
122+ makeLinesAndLabels ( plotGroup , pathinfo , gd , cd [ 0 ] , contours , plotinfo , carpet ) ;
120123
121124 // Clip the boundary of the plot
122125 Drawing . setClipUrl ( plotGroup , carpet . _clipPathId ) ;
123126}
124127
125- function makeLines ( plotgroup , pathinfo , contours ) {
128+ function makeLinesAndLabels ( plotgroup , pathinfo , gd , cd0 , contours , plotinfo , carpet ) {
126129 var lineContainer = plotgroup . selectAll ( 'g.contourlines' ) . data ( [ 0 ] ) ;
127130
128131 lineContainer . enter ( ) . append ( 'g' )
129132 . classed ( 'contourlines' , true ) ;
130133
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 ;
133294}
134295
135296function makeBackground ( plotgroup , clipsegments , xaxis , yaxis , isConstraint , coloring ) {
0 commit comments