@@ -41,21 +41,25 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
4141
4242 // BUILD LINES AND FILLS
4343 var prevpath = '' ,
44- tozero , tonext , nexttonext ;
44+ ownFillEl3 , ownFillDir , tonext , nexttonext ;
4545
4646 scattertraces . each ( function ( d ) {
4747 var trace = d [ 0 ] . trace ,
4848 line = trace . line ,
4949 tr = d3 . select ( this ) ;
5050 if ( trace . visible !== true ) return ;
5151
52+ ownFillDir = trace . fill . charAt ( trace . fill . length - 1 ) ;
53+ if ( ownFillDir !== 'x' && ownFillDir !== 'y' ) ownFillDir = '' ;
54+
5255 d [ 0 ] . node3 = tr ; // store node for tweaking by selectPoints
5356
5457 arraysToCalcdata ( d ) ;
5558
5659 if ( ! subTypes . hasLines ( trace ) && trace . fill === 'none' ) return ;
5760
5861 var thispath ,
62+ thisrevpath ,
5963 // fullpath is all paths for this curve, joined together straight
6064 // across gaps, for filling
6165 fullpath = '' ,
@@ -67,12 +71,12 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
6771 // make the fill-to-zero path now, so it shows behind the line
6872 // fill to next puts the fill associated with one trace
6973 // grouped with the previous
70- if ( trace . fill . substr ( 0 , 6 ) === 'tozero' ||
74+ if ( trace . fill . substr ( 0 , 6 ) === 'tozero' || trace . fill === 'toself' ||
7175 ( trace . fill . substr ( 0 , 2 ) === 'to' && ! prevpath ) ) {
72- tozero = tr . append ( 'path' )
76+ ownFillEl3 = tr . append ( 'path' )
7377 . classed ( 'js-fill' , true ) ;
7478 }
75- else tozero = null ;
79+ else ownFillEl3 = null ;
7680
7781 // make the fill-to-next path now for the NEXT trace, so it shows
7882 // behind both lines.
@@ -91,7 +95,15 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
9195 }
9296 else if ( line . shape === 'spline' ) {
9397 pathfn = revpathbase = function ( pts ) {
94- return Drawing . smoothopen ( pts , line . smoothing ) ;
98+ var pLast = pts [ pts . length - 1 ] ;
99+ if ( pts [ 0 ] [ 0 ] === pLast [ 0 ] && pts [ 0 ] [ 1 ] === pLast [ 1 ] ) {
100+ // identical start and end points: treat it as a
101+ // closed curve so we don't get a kink
102+ return Drawing . smoothclosed ( pts . slice ( 1 ) , line . smoothing ) ;
103+ }
104+ else {
105+ return Drawing . smoothopen ( pts , line . smoothing ) ;
106+ }
95107 } ;
96108 }
97109 else {
@@ -102,7 +114,7 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
102114
103115 revpathfn = function ( pts ) {
104116 // note: this is destructive (reverses pts in place) so can't use pts after this
105- return 'L' + revpathbase ( pts . reverse ( ) ) . substr ( 1 ) ;
117+ return revpathbase ( pts . reverse ( ) ) ;
106118 } ;
107119
108120 var segments = linePoints ( d , {
@@ -121,27 +133,58 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
121133 for ( var i = 0 ; i < segments . length ; i ++ ) {
122134 var pts = segments [ i ] ;
123135 thispath = pathfn ( pts ) ;
124- fullpath += fullpath ? ( 'L' + thispath . substr ( 1 ) ) : thispath ;
125- revpath = revpathfn ( pts ) + revpath ;
136+ thisrevpath = revpathfn ( pts ) ;
137+ if ( ! fullpath ) {
138+ fullpath = thispath ;
139+ revpath = thisrevpath ;
140+ }
141+ else if ( ownFillDir ) {
142+ fullpath += 'L' + thispath . substr ( 1 ) ;
143+ revpath = thisrevpath + ( 'L' + revpath . substr ( 1 ) ) ;
144+ }
145+ else {
146+ fullpath += 'Z' + thispath ;
147+ revpath = thisrevpath + 'Z' + revpath ;
148+ }
126149 if ( subTypes . hasLines ( trace ) && pts . length > 1 ) {
127150 tr . append ( 'path' ) . classed ( 'js-line' , true ) . attr ( 'd' , thispath ) ;
128151 }
129152 }
130- if ( tozero ) {
153+ if ( ownFillEl3 ) {
131154 if ( pt0 && pt1 ) {
132- if ( trace . fill . charAt ( trace . fill . length - 1 ) === 'y' ) {
133- pt0 [ 1 ] = pt1 [ 1 ] = ya . c2p ( 0 , true ) ;
155+ if ( ownFillDir ) {
156+ if ( ownFillDir === 'y' ) {
157+ pt0 [ 1 ] = pt1 [ 1 ] = ya . c2p ( 0 , true ) ;
158+ }
159+ else if ( ownFillDir === 'x' ) {
160+ pt0 [ 0 ] = pt1 [ 0 ] = xa . c2p ( 0 , true ) ;
161+ }
162+
163+ // fill to zero: full trace path, plus extension of
164+ // the endpoints to the appropriate axis
165+ ownFillEl3 . attr ( 'd' , fullpath + 'L' + pt1 + 'L' + pt0 + 'Z' ) ;
134166 }
135- else pt0 [ 0 ] = pt1 [ 0 ] = xa . c2p ( 0 , true ) ;
136-
137- // fill to zero: full trace path, plus extension of
138- // the endpoints to the appropriate axis
139- tozero . attr ( 'd' , fullpath + 'L' + pt1 + 'L' + pt0 + 'Z' ) ;
167+ // fill to self: just join the path to itself
168+ else ownFillEl3 . attr ( 'd' , fullpath + 'Z' ) ;
140169 }
141170 }
142171 else if ( trace . fill . substr ( 0 , 6 ) === 'tonext' && fullpath && prevpath ) {
143172 // fill to next: full trace path, plus the previous path reversed
144- tonext . attr ( 'd' , fullpath + prevpath + 'Z' ) ;
173+ if ( trace . fill === 'tonext' ) {
174+ // tonext: for use by concentric shapes, like manually constructed
175+ // contours, we just add the two paths closed on themselves.
176+ // This makes strange results if one path is *not* entirely
177+ // inside the other, but then that is a strange usage.
178+ tonext . attr ( 'd' , fullpath + 'Z' + prevpath + 'Z' ) ;
179+ }
180+ else {
181+ // tonextx/y: for now just connect endpoints with lines. This is
182+ // the correct behavior if the endpoints are at the same value of
183+ // y/x, but if they *aren't*, we should ideally do more complicated
184+ // things depending on whether the new endpoint projects onto the
185+ // existing curve or off the end of it
186+ tonext . attr ( 'd' , fullpath + 'L' + prevpath . substr ( 1 ) + 'Z' ) ;
187+ }
145188 }
146189 prevpath = revpath ;
147190 }
0 commit comments