99
1010namespace OxyPlot . Series
1111{
12- using System . Linq ;
12+ using System ;
1313 using System . Collections . Generic ;
14+ using System . Linq ;
1415
1516 /// <summary>
1617 /// Represents an area series that fills the polygon defined by two sets of points or one set of points and a constant.
@@ -189,10 +190,9 @@ public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpo
189190 /// <param name="rc">The rendering context.</param>
190191 public override void Render ( IRenderContext rc )
191192 {
192- var dataPoints1 = this . ActualPoints ;
193- var dataPoints2 = this . ActualPoints2 ;
194- int n0 = dataPoints1 . Count ;
195- if ( n0 == 0 )
193+ var actualPoints = this . ActualPoints ;
194+ var actualPoints2 = this . ActualPoints2 ;
195+ if ( actualPoints . Count == 0 )
196196 {
197197 return ;
198198 }
@@ -204,83 +204,146 @@ public override void Render(IRenderContext rc)
204204 var clippingRect = this . GetClippingRect ( ) ;
205205 rc . SetClip ( clippingRect ) ;
206206
207- // Transform all points to screen coordinates
208- IList < ScreenPoint > pts0 = new ScreenPoint [ n0 ] ;
209- for ( int i = 0 ; i < n0 ; i ++ )
207+ // Manage NaN's
208+ var chunkedListsOfPoints = this . Split ( actualPoints , p => double . IsNaN ( p . Y ) ) ;
209+ var chunkedListsOfPoints2 = this . Split ( actualPoints2 , p => double . IsNaN ( p . Y ) ) ;
210+
211+ for ( int chunkIndex = 0 ; chunkIndex < chunkedListsOfPoints . Count ( ) ; chunkIndex ++ )
210212 {
211- pts0 [ i ] = this . XAxis . Transform ( dataPoints1 [ i ] . X , dataPoints1 [ i ] . Y , this . YAxis ) ;
213+ var chunkActualPoints = chunkedListsOfPoints . ElementAt ( chunkIndex ) . ToList ( ) ;
214+
215+ // Transform all points to screen coordinates
216+ int n0 = chunkActualPoints . Count ;
217+ IList < ScreenPoint > pts0 = new ScreenPoint [ n0 ] ;
218+ for ( int i = 0 ; i < n0 ; i ++ )
219+ {
220+ pts0 [ i ] = this . XAxis . Transform ( chunkActualPoints [ i ] . X , chunkActualPoints [ i ] . Y , this . YAxis ) ;
221+ }
222+
223+ if ( this . Smooth )
224+ {
225+ var rpts0 = ScreenPointHelper . ResamplePoints ( pts0 , this . MinimumSegmentLength ) ;
226+ pts0 = CanonicalSplineHelper . CreateSpline ( rpts0 , 0.5 , null , false , 0.25 ) ;
227+ }
228+
229+ var dashArray = this . ActualDashArray ;
230+
231+ // draw the clipped lines
232+ rc . DrawClippedLine (
233+ clippingRect ,
234+ pts0 ,
235+ minDistSquared ,
236+ this . GetSelectableColor ( this . ActualColor ) ,
237+ this . StrokeThickness ,
238+ dashArray ,
239+ this . LineJoin ,
240+ false ) ;
212241 }
213242
214- int n1 = dataPoints2 . Count ;
215- IList < ScreenPoint > pts1 = new ScreenPoint [ n1 ] ;
216- for ( int i = 0 ; i < n1 ; i ++ )
243+ for ( int chunkIndex = 0 ; chunkIndex < chunkedListsOfPoints2 . Count ( ) ; chunkIndex ++ )
217244 {
218- int j = this . Reverse2 ? n1 - 1 - i : i ;
219- pts1 [ j ] = this . XAxis . Transform ( dataPoints2 [ i ] . X , dataPoints2 [ i ] . Y , this . YAxis ) ;
245+ var chunkActualPoints2 = chunkedListsOfPoints2 . ElementAt ( chunkIndex ) . ToList ( ) ;
246+
247+ // Transform all points to screen coordinates
248+ int n1 = chunkActualPoints2 . Count ;
249+ IList < ScreenPoint > pts1 = new ScreenPoint [ n1 ] ;
250+ for ( int i = 0 ; i < n1 ; i ++ )
251+ {
252+ int j = this . Reverse2 ? n1 - 1 - i : i ;
253+ pts1 [ j ] = this . XAxis . Transform ( chunkActualPoints2 [ i ] . X , chunkActualPoints2 [ i ] . Y , this . YAxis ) ;
254+ }
255+
256+ if ( this . Smooth )
257+ {
258+ var rpts1 = ScreenPointHelper . ResamplePoints ( pts1 , this . MinimumSegmentLength ) ;
259+ pts1 = CanonicalSplineHelper . CreateSpline ( rpts1 , 0.5 , null , false , 0.25 ) ;
260+ }
261+
262+ var dashArray = this . ActualDashArray ;
263+
264+ // draw the clipped lines
265+ rc . DrawClippedLine (
266+ clippingRect ,
267+ pts1 ,
268+ minDistSquared ,
269+ this . GetSelectableColor ( this . ActualColor2 ) ,
270+ this . StrokeThickness ,
271+ dashArray ,
272+ this . LineJoin ,
273+ false ) ;
220274 }
221275
222- if ( this . Smooth )
276+ if ( chunkedListsOfPoints . Count ( ) != chunkedListsOfPoints2 . Count ( ) )
223277 {
224- var rpts0 = ScreenPointHelper . ResamplePoints ( pts0 , this . MinimumSegmentLength ) ;
225- var rpts1 = ScreenPointHelper . ResamplePoints ( pts1 , this . MinimumSegmentLength ) ;
226-
227- pts0 = CanonicalSplineHelper . CreateSpline ( rpts0 , 0.5 , null , false , 0.25 ) ;
228- pts1 = CanonicalSplineHelper . CreateSpline ( rpts1 , 0.5 , null , false , 0.25 ) ;
278+ rc . ResetClip ( ) ;
279+ return ;
229280 }
230281
231- var dashArray = this . ActualDashArray ;
232-
233- // draw the clipped lines
234- rc . DrawClippedLine (
235- clippingRect ,
236- pts0 ,
237- minDistSquared ,
238- this . GetSelectableColor ( this . ActualColor ) ,
239- this . StrokeThickness ,
240- dashArray ,
241- this . LineJoin ,
242- false ) ;
243- rc . DrawClippedLine (
244- clippingRect ,
245- pts1 ,
246- minDistSquared ,
247- this . GetSelectableColor ( this . ActualColor2 ) ,
248- this . StrokeThickness ,
249- dashArray ,
250- this . LineJoin ,
251- false ) ;
252-
253- // combine the two lines and draw the clipped area
254- var pts = new List < ScreenPoint > ( ) ;
255- pts . AddRange ( pts1 ) ;
256- pts . AddRange ( pts0 ) ;
282+ // Draw the fill
283+ for ( int chunkIndex = 0 ; chunkIndex < chunkedListsOfPoints . Count ( ) ; chunkIndex ++ )
284+ {
285+ var chunkActualPoints = chunkedListsOfPoints . ElementAt ( chunkIndex ) . ToList ( ) ;
286+ var chunkActualPoints2 = chunkedListsOfPoints2 . ElementAt ( chunkIndex ) . ToList ( ) ;
257287
258- // pts = SutherlandHodgmanClipping.ClipPolygon(clippingRect, pts);
259- rc . DrawClippedPolygon ( clippingRect , pts , minDistSquared , this . GetSelectableFillColor ( this . ActualFill ) , OxyColors . Undefined ) ;
260-
261- var markerSizes = new [ ] { this . MarkerSize } ;
262-
263- // draw the markers on top
264- rc . DrawMarkers (
265- clippingRect ,
266- pts0 ,
267- this . MarkerType ,
268- null ,
269- markerSizes ,
270- this . MarkerFill ,
271- this . MarkerStroke ,
272- this . MarkerStrokeThickness ,
273- 1 ) ;
274- rc . DrawMarkers (
275- clippingRect ,
276- pts1 ,
277- this . MarkerType ,
278- null ,
279- markerSizes ,
280- this . MarkerFill ,
281- this . MarkerStroke ,
282- this . MarkerStrokeThickness ,
283- 1 ) ;
288+ // Transform all points to screen coordinates
289+ int n0 = chunkActualPoints . Count ;
290+ IList < ScreenPoint > pts0 = new ScreenPoint [ n0 ] ;
291+ for ( int i = 0 ; i < n0 ; i ++ )
292+ {
293+ pts0 [ i ] = this . XAxis . Transform ( chunkActualPoints [ i ] . X , chunkActualPoints [ i ] . Y , this . YAxis ) ;
294+ }
295+
296+ int n1 = chunkActualPoints2 . Count ;
297+ IList < ScreenPoint > pts1 = new ScreenPoint [ n1 ] ;
298+ for ( int i = 0 ; i < n1 ; i ++ )
299+ {
300+ int j = this . Reverse2 ? n1 - 1 - i : i ;
301+ pts1 [ j ] = this . XAxis . Transform ( chunkActualPoints2 [ i ] . X , chunkActualPoints2 [ i ] . Y , this . YAxis ) ;
302+ }
303+
304+ if ( this . Smooth )
305+ {
306+ var rpts0 = ScreenPointHelper . ResamplePoints ( pts0 , this . MinimumSegmentLength ) ;
307+ var rpts1 = ScreenPointHelper . ResamplePoints ( pts1 , this . MinimumSegmentLength ) ;
308+
309+ pts0 = CanonicalSplineHelper . CreateSpline ( rpts0 , 0.5 , null , false , 0.25 ) ;
310+ pts1 = CanonicalSplineHelper . CreateSpline ( rpts1 , 0.5 , null , false , 0.25 ) ;
311+ }
312+
313+ var dashArray = this . ActualDashArray ;
314+
315+ // combine the two lines and draw the clipped area
316+ var pts = new List < ScreenPoint > ( ) ;
317+ pts . AddRange ( pts1 ) ;
318+ pts . AddRange ( pts0 ) ;
319+
320+ // pts = SutherlandHodgmanClipping.ClipPolygon(clippingRect, pts);
321+ rc . DrawClippedPolygon ( clippingRect , pts , minDistSquared , this . GetSelectableFillColor ( this . ActualFill ) , OxyColors . Undefined ) ;
322+
323+ var markerSizes = new [ ] { this . MarkerSize } ;
324+
325+ // draw the markers on top
326+ rc . DrawMarkers (
327+ clippingRect ,
328+ pts0 ,
329+ this . MarkerType ,
330+ null ,
331+ markerSizes ,
332+ this . MarkerFill ,
333+ this . MarkerStroke ,
334+ this . MarkerStrokeThickness ,
335+ 1 ) ;
336+ rc . DrawMarkers (
337+ clippingRect ,
338+ pts1 ,
339+ this . MarkerType ,
340+ null ,
341+ markerSizes ,
342+ this . MarkerFill ,
343+ this . MarkerStroke ,
344+ this . MarkerStrokeThickness ,
345+ 1 ) ;
346+ }
284347
285348 rc . ResetClip ( ) ;
286349 }
@@ -366,5 +429,23 @@ private IEnumerable<DataPoint> GetConstantPoints2()
366429 yield return new DataPoint ( x1 , this . ConstantY2 ) ;
367430 }
368431 }
432+
433+ /// <summary>
434+ /// Split an IEnumerable<typeparamref name="T"/> into chunks (sub-lists), based on a split condition
435+ /// (input items with splitCondition == true will not be included in output)
436+ /// </summary>
437+ /// <typeparam name="T">The type of the input list item</typeparam>
438+ /// <param name="source">The input list</param>
439+ /// <param name="splitCondition">The split condition</param>
440+ /// <returns>A collection of a collection of <typeparamref name="T"/> items</returns>
441+ private IEnumerable < IEnumerable < T > > Split < T > ( IEnumerable < T > source , Func < T , bool > splitCondition )
442+ {
443+ source = source . SkipWhile ( splitCondition ) ;
444+ while ( source . Any ( ) )
445+ {
446+ yield return source . TakeWhile ( x => ! splitCondition ( x ) ) ;
447+ source = source . SkipWhile ( x => ! splitCondition ( x ) ) . SkipWhile ( splitCondition ) ;
448+ }
449+ }
369450 }
370451}
0 commit comments