@@ -41,21 +41,25 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
41
41
42
42
// BUILD LINES AND FILLS
43
43
var prevpath = '' ,
44
- tozero , tonext , nexttonext ;
44
+ ownFillEl3 , ownFillDir , tonext , nexttonext ;
45
45
46
46
scattertraces . each ( function ( d ) {
47
47
var trace = d [ 0 ] . trace ,
48
48
line = trace . line ,
49
49
tr = d3 . select ( this ) ;
50
50
if ( trace . visible !== true ) return ;
51
51
52
+ ownFillDir = trace . fill . charAt ( trace . fill . length - 1 ) ;
53
+ if ( ownFillDir !== 'x' && ownFillDir !== 'y' ) ownFillDir = '' ;
54
+
52
55
d [ 0 ] . node3 = tr ; // store node for tweaking by selectPoints
53
56
54
57
arraysToCalcdata ( d ) ;
55
58
56
59
if ( ! subTypes . hasLines ( trace ) && trace . fill === 'none' ) return ;
57
60
58
61
var thispath ,
62
+ thisrevpath ,
59
63
// fullpath is all paths for this curve, joined together straight
60
64
// across gaps, for filling
61
65
fullpath = '' ,
@@ -67,12 +71,12 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
67
71
// make the fill-to-zero path now, so it shows behind the line
68
72
// fill to next puts the fill associated with one trace
69
73
// grouped with the previous
70
- if ( trace . fill . substr ( 0 , 6 ) === 'tozero' ||
74
+ if ( trace . fill . substr ( 0 , 6 ) === 'tozero' || trace . fill === 'toself' ||
71
75
( trace . fill . substr ( 0 , 2 ) === 'to' && ! prevpath ) ) {
72
- tozero = tr . append ( 'path' )
76
+ ownFillEl3 = tr . append ( 'path' )
73
77
. classed ( 'js-fill' , true ) ;
74
78
}
75
- else tozero = null ;
79
+ else ownFillEl3 = null ;
76
80
77
81
// make the fill-to-next path now for the NEXT trace, so it shows
78
82
// behind both lines.
@@ -91,7 +95,15 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
91
95
}
92
96
else if ( line . shape === 'spline' ) {
93
97
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
+ }
95
107
} ;
96
108
}
97
109
else {
@@ -102,7 +114,7 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
102
114
103
115
revpathfn = function ( pts ) {
104
116
// 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 ( ) ) ;
106
118
} ;
107
119
108
120
var segments = linePoints ( d , {
@@ -121,27 +133,58 @@ module.exports = function plot(gd, plotinfo, cdscatter) {
121
133
for ( var i = 0 ; i < segments . length ; i ++ ) {
122
134
var pts = segments [ i ] ;
123
135
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
+ }
126
149
if ( subTypes . hasLines ( trace ) && pts . length > 1 ) {
127
150
tr . append ( 'path' ) . classed ( 'js-line' , true ) . attr ( 'd' , thispath ) ;
128
151
}
129
152
}
130
- if ( tozero ) {
153
+ if ( ownFillEl3 ) {
131
154
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' ) ;
134
166
}
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' ) ;
140
169
}
141
170
}
142
171
else if ( trace . fill . substr ( 0 , 6 ) === 'tonext' && fullpath && prevpath ) {
143
172
// 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
+ }
145
188
}
146
189
prevpath = revpath ;
147
190
}
0 commit comments