1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
10 // Namespace: DataVisualization.Charting.ChartTypes
12 // Classes: RangeChart, SplineRangeChart
14 // Purpose: Provides 2D/3D drawing and hit testing functionality
15 // for the Range and SplineRange charts. The Range chart
16 // displays a range of data by plotting two Y values per
17 // data point, with each Y value being drawn as a line
18 // chart. The range between the Y values can then be
19 // filled with color. Spline Range chart changes the
20 // default tension of the lines between data points.
22 // Reviewed: AG - Aug 6, 2002
23 // AG - Microsoft 6, 2007
25 //===================================================================
27 #region Used namespaces
30 using System.Resources;
31 using System.Reflection;
32 using System.Collections;
34 using System.Drawing.Drawing2D;
37 using System.Windows.Forms.DataVisualization.Charting;
38 using System.Windows.Forms.DataVisualization.Charting.Data;
39 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
40 using System.Windows.Forms.DataVisualization.Charting.Utilities;
41 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
44 using System.Web.UI.DataVisualization.Charting;
46 using System.Web.UI.DataVisualization.Charting.ChartTypes;
47 using System.Web.UI.DataVisualization.Charting.Data;
48 using System.Web.UI.DataVisualization.Charting.Utilities;
54 namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
56 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
60 /// SplineRangeChart class extends the RangeChart class by
61 /// providing a different initial tension for the lines.
63 internal class SplineRangeChart : RangeChart
68 /// Default constructor.
70 public SplineRangeChart()
72 // Set default line tension
73 base.lineTension = 0.5f;
78 #region IChartType interface implementation
83 public override string Name { get{ return ChartTypeNames.SplineRange;}}
86 /// Gets chart type image.
88 /// <param name="registry">Chart types registry object.</param>
89 /// <returns>Chart type image.</returns>
90 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
92 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
97 #region Default tension method
100 /// Gets default line tension.
102 /// <returns>Default line tension.</returns>
103 override protected float GetDefaultTension()
109 /// Checks if line tension is supported by the chart type.
111 /// <returns>True if line tension is supported.</returns>
112 protected override bool IsLineTensionSupported()
121 /// RangeChart class provides 2D/3D drawing and hit testing
122 /// functionality for the Range and SplineRange charts. The
123 /// only difference of the SplineRange chart is the default
124 /// tension of the line.
126 /// SplineChart base class provides most of the functionality
127 /// like drawing lines, labels and markers.
129 internal class RangeChart : SplineChart
134 /// Fields used to fill area with gradient
136 internal bool gradientFill = false;
139 /// Shape of the low values
141 internal GraphicsPath areaBottomPath = new GraphicsPath();
144 /// Coordinates of the area path
146 protected GraphicsPath areaPath = null;
149 /// Reference to the current series object
151 private Series _series = null;
154 /// Array of low line values
156 internal PointF[] lowPoints = null;
159 /// Check if series are indexed based
161 internal bool indexedBasedX = false;
164 /// Secondary Y coordinate that should be used for bottom line of the range (left point)
166 private float _thirdPointY2Value = float.NaN;
169 /// Secondary Y coordinate that should be used for bottom line of the range (right point)
171 private float _fourthPointY2Value = float.NaN;
178 /// Default constructor.
182 this.drawOutsideLines = true;
187 #region IChartType interface implementation
192 public override string Name { get{ return ChartTypeNames.Range;}}
195 /// Number of supported Y value(s) per point
197 override public int YValuesPerPoint { get { return 2; } }
200 /// Indicates that extra Y values are connected to the scale of the Y axis
202 override public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
205 /// How to draw series/points in legend:
206 /// Filled rectangle, Line or Marker
208 /// <param name="series">Legend item series.</param>
209 /// <returns>Legend item style.</returns>
210 override public LegendImageStyle GetLegendImageStyle(Series series)
212 return LegendImageStyle.Rectangle;
216 /// Gets chart type image.
218 /// <param name="registry">Chart types registry object.</param>
219 /// <returns>Chart type image.</returns>
220 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
222 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
227 #region Default tension method
230 /// Gets default line tension.
232 /// <returns>Line tension.</returns>
233 override protected float GetDefaultTension()
239 /// Checks if line tension is supported by the chart type.
241 /// <returns>True if line tension is supported.</returns>
242 protected override bool IsLineTensionSupported()
249 #region Painting and Selection related methods
252 /// Fills last series area with gradient.
253 /// If gradient is used as a back color of the series it must be drawn
254 /// at the same time.
256 /// <param name="graph">The Chart Graphics object.</param>
257 private void FillLastSeriesGradient(ChartGraphics graph)
259 // Add last line in the path
263 areaPath.GetLastPoint().X,
264 areaPath.GetLastPoint().Y,
265 areaPath.GetLastPoint().X,
266 areaBottomPath.GetLastPoint().Y);
269 // Fill whole area with gradient
270 if(gradientFill && areaPath != null)
273 graph.SetClip( Area.PlotAreaPosition.ToRectangleF() );
275 // Create new path from high/low lines
276 using (GraphicsPath gradientPath = new GraphicsPath())
278 gradientPath.AddPath(areaPath, true);
279 areaBottomPath.Reverse();
280 gradientPath.AddPath(areaBottomPath, true);
283 using (Brush areaGradientBrush = graph.GetGradientBrush(gradientPath.GetBounds(), this._series.Color, this._series.BackSecondaryColor, this._series.BackGradientStyle))
285 // Fill area with gradient
286 graph.FillPath(areaGradientBrush, gradientPath);
287 gradientFill = false;
300 // Reset bottom area path
301 areaBottomPath.Reset();
305 /// This method recalculates position of the end points of lines. This method
306 /// is used from Paint or Select method.
308 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
309 /// <param name="graph">The Chart Graphics object.</param>
310 /// <param name="common">The Common elements object.</param>
311 /// <param name="area">Chart area for this chart.</param>
312 /// <param name="seriesToDraw">Chart series to draw.</param>
313 protected override void ProcessChartType(
316 CommonElements common,
318 Series seriesToDraw )
320 gradientFill = false;
323 // Check if series is indexed based
324 indexedBasedX = ChartHelper.IndexedSeries(common, area.GetSeriesFromChartType(this.Name).ToArray());
327 base.ProcessChartType(selection, graph, common, area, seriesToDraw);
329 // Fill gradient fro the previous series
330 FillLastSeriesGradient(graph);
334 /// Override line drawing method to fill the range and draw lines.
336 /// <param name="graph">Graphics object.</param>
337 /// <param name="common">The Common elements object.</param>
338 /// <param name="point">Point to draw the line for.</param>
339 /// <param name="series">Point series.</param>
340 /// <param name="points">Array of oints coordinates.</param>
341 /// <param name="pointIndex">Index of point to draw.</param>
342 /// <param name="tension">Line tension.</param>
343 override protected void DrawLine(
345 CommonElements common,
352 // Two Y values required
353 if(point.YValues.Length < 2)
355 throw(new InvalidOperationException( SR.ExceptionChartTypeRequiresYValues( this.Name, "2" )));
358 // Start drawing from the second point
364 // Do nothing for the low values line
365 if(this.YValueIndex == 1)
370 // Check if its a beginning of a new series
371 if(this._series != null)
373 if(this._series.Name != series.Name)
375 // Fill gradient from the previous series
376 FillLastSeriesGradient(graph);
377 this._series = series;
379 areaBottomPath.Reset();
384 this._series = series;
387 // Fill array of lower points of the range
388 if(lowPoints == null)
390 this.YValueIndex = 1;
391 lowPoints = GetPointsPosition(graph, series, indexedBasedX);
392 this.YValueIndex = 0;
395 // Calculate points position
396 PointF highPoint1 = points[pointIndex-1];
397 PointF highPoint2 = points[pointIndex];
398 PointF lowPoint1 = lowPoints[pointIndex-1];
399 PointF lowPoint2 = lowPoints[pointIndex];
402 Brush areaBrush = null;
403 if( point.BackHatchStyle != ChartHatchStyle.None )
405 areaBrush = graph.GetHatchBrush( point.BackHatchStyle, point.Color, point.BackSecondaryColor);
407 else if( point.BackGradientStyle != GradientStyle.None )
409 this.gradientFill = true;
410 this._series = point.series;
412 else if( point.BackImage.Length > 0 && point.BackImageWrapMode != ChartImageWrapMode.Unscaled && point.BackImageWrapMode != ChartImageWrapMode.Scaled)
414 areaBrush = graph.GetTextureBrush(point.BackImage, point.BackImageTransparentColor, point.BackImageWrapMode, point.Color );
418 areaBrush = new SolidBrush(point.Color);
421 // Calculate data point area segment path
422 GraphicsPath path = new GraphicsPath();
423 path.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
424 if(this.lineTension == 0)
426 path.AddLine(points[pointIndex-1], points[pointIndex]);
430 path.AddCurve(points, pointIndex-1, 1, this.lineTension);
433 path.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
435 // Because of SVG Rendering order of points in the close shape
436 // has to be respected.
437 if( graph.ActiveRenderingType == RenderingType.Svg )
439 using (GraphicsPath pathReverse = new GraphicsPath())
441 // Add curve to the new graphics path
442 if (this.lineTension == 0)
444 path.AddLine(lowPoints[pointIndex - 1], lowPoints[pointIndex]);
448 pathReverse.AddCurve(lowPoints, pointIndex - 1, 1, this.lineTension);
450 // Convert to polygon
451 pathReverse.Flatten();
453 // Reversed points order in the aray
454 PointF[] pointsReversed = pathReverse.PathPoints;
455 PointF[] pointF = new PointF[pointsReversed.Length];
456 int pntIndex = pointsReversed.Length - 1;
457 foreach (PointF pp in pointsReversed)
459 pointF[pntIndex] = pp;
463 // Path can not have polygon width two points
464 if (pointF.Length == 2)
466 PointF[] newPointF = new PointF[3];
467 newPointF[0] = pointF[0];
468 newPointF[1] = pointF[1];
469 newPointF[2] = pointF[1];
473 // Add Polygon to the path
474 path.AddPolygon(pointF);
480 if(this.lineTension == 0)
482 path.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
486 path.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
491 // Check if bottom line is partialy in the data scaleView
494 double xValue = (indexedSeries) ? pointIndex + 1 : series.Points[pointIndex].XValue;
495 double xPrevValue = (indexedSeries) ? pointIndex : series.Points[pointIndex - 1].XValue;
496 if(xPrevValue < hAxisMin || xPrevValue > hAxisMax ||
497 xValue > hAxisMax || xValue < hAxisMin ||
498 series.Points[pointIndex-1].YValues[1] < vAxisMin || series.Points[pointIndex-1].YValues[1] > vAxisMax ||
499 series.Points[pointIndex].YValues[1] < vAxisMin || series.Points[pointIndex].YValues[1] > vAxisMax )
501 // Set clipping region for bottom line drawing
502 graph.SetClip( Area.PlotAreaPosition.ToRectangleF() );
503 clipRegionSet = true;
508 if(series.ShadowColor != Color.Empty && series.ShadowOffset != 0)
510 if(point.Color != Color.Empty && point.Color != Color.Transparent)
512 // Translate drawing matrix
513 Matrix translateMatrix = graph.Transform.Clone();
514 translateMatrix.Translate(series.ShadowOffset, series.ShadowOffset);
515 Matrix oldMatrix = graph.Transform;
516 graph.Transform = translateMatrix;
518 Region shadowRegion = new Region(path);
519 using (Brush shadowBrush = new SolidBrush((series.ShadowColor.A != 255) ? series.ShadowColor : Color.FromArgb(point.Color.A / 2, series.ShadowColor)))
521 Region clipRegion = null;
522 if (!graph.IsClipEmpty && !graph.Clip.IsInfinite(graph.Graphics))
524 clipRegion = graph.Clip;
525 clipRegion.Translate(series.ShadowOffset + 1, series.ShadowOffset + 1);
526 graph.Clip = clipRegion;
531 graph.FillRegion(shadowBrush, shadowRegion);
533 // Draw leftmost and rightmost vertical lines
534 using (Pen areaLinePen = new Pen(shadowBrush, 1))
538 graph.DrawLine(areaLinePen, highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
540 if (pointIndex == series.Points.Count - 1)
542 graph.DrawLine(areaLinePen, highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
546 // Restore graphics parameters
547 graph.Transform = oldMatrix;
549 // Draw high and low line shadows
550 this.drawShadowOnly = true;
551 base.DrawLine(graph, common, point, series, points, pointIndex, tension);
552 this.YValueIndex = 1;
553 base.DrawLine(graph, common, point, series, lowPoints, pointIndex, tension);
554 this.YValueIndex = 0;
555 this.drawShadowOnly = false;
557 // Restore clip region
558 if (clipRegion != null)
560 clipRegion = graph.Clip;
561 clipRegion.Translate(-(series.ShadowOffset + 1), -(series.ShadowOffset + 1));
562 graph.Clip = clipRegion;
571 // Turn off anti aliasing and fill area
572 SmoothingMode oldMode = graph.SmoothingMode;
573 graph.SmoothingMode = SmoothingMode.None;
574 path.CloseAllFigures();
575 graph.FillPath(areaBrush, path);
576 graph.SmoothingMode = oldMode;
578 // Draw top and bottom lines, because anti aliasing is not working for the FillPath method
579 if(graph.SmoothingMode != SmoothingMode.None)
581 Pen areaLinePen = new Pen(areaBrush, 1);
583 // This code is introduce because of problem
584 // with Svg and Hatch Color
585 HatchBrush hatchBrush = areaBrush as HatchBrush;
586 if( hatchBrush != null )
588 areaLinePen.Color = hatchBrush.ForegroundColor;
593 graph.DrawLine(areaLinePen, highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
595 if(pointIndex == series.Points.Count - 1)
597 graph.DrawLine(areaLinePen, highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
600 if(this.lineTension == 0)
602 graph.DrawLine(areaLinePen, points[pointIndex - 1], points[pointIndex]);
606 graph.DrawCurve(areaLinePen, points, pointIndex-1, 1, this.lineTension);
609 if(this.lineTension == 0)
611 graph.DrawLine(areaLinePen, lowPoints[pointIndex - 1], lowPoints[pointIndex]);
615 graph.DrawCurve(areaLinePen, lowPoints, pointIndex-1, 1, this.lineTension);
623 areaPath = new GraphicsPath();
624 areaPath.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
627 // Add line to the gradient path
628 if(this.lineTension == 0)
630 areaPath.AddLine(points[pointIndex-1], points[pointIndex]);
634 areaPath.AddCurve(points, pointIndex-1, 1, this.lineTension);
637 if(this.lineTension == 0)
639 areaBottomPath.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
643 areaBottomPath.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
646 // Draw range High and Low border lines
647 if((point.BorderWidth > 0 &&
648 point.BorderDashStyle != ChartDashStyle.NotSet &&
649 point.BorderColor != Color.Empty) ||
650 areaBrush is SolidBrush)
652 this.useBorderColor = true;
653 this.disableShadow = true;
654 base.DrawLine(graph, common, point, series, points, pointIndex, tension);
655 this.YValueIndex = 1;
656 base.DrawLine(graph, common, point, series, lowPoints, pointIndex, tension);
657 this.YValueIndex = 0;
658 this.useBorderColor = false;
659 this.disableShadow = false;
662 if( common.ProcessModeRegions )
664 //**************************************************************
665 //** Add area for the inside of the area
666 //**************************************************************
668 path.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
669 if(this.lineTension == 0)
671 path.AddLine(points[pointIndex-1], points[pointIndex]);
675 path.AddCurve(points, pointIndex-1, 1, this.lineTension);
677 path.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
678 if(this.lineTension == 0)
680 path.AddLine(lowPoints[pointIndex-1], lowPoints[pointIndex]);
684 path.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
687 // Create grapics path object dor the curved area
688 GraphicsPath mapAreaPath = new GraphicsPath();
689 mapAreaPath.AddLine(highPoint1.X, lowPoint1.Y, highPoint1.X, highPoint1.Y);
690 if(this.lineTension == 0)
692 mapAreaPath.AddLine(points[pointIndex - 1], points[pointIndex]);
696 mapAreaPath.AddCurve(points, pointIndex-1, 1, this.lineTension);
697 mapAreaPath.Flatten();
699 mapAreaPath.AddLine(highPoint2.X, highPoint2.Y, highPoint2.X, lowPoint2.Y);
700 if(this.lineTension == 0)
702 mapAreaPath.AddLine(lowPoints[pointIndex - 1], lowPoints[pointIndex]);
706 mapAreaPath.AddCurve(lowPoints, pointIndex-1, 1, this.lineTension);
707 mapAreaPath.Flatten();
710 // Allocate array of floats
711 PointF pointNew = PointF.Empty;
712 float[] coord = new float[mapAreaPath.PointCount * 2];
713 PointF[] pathPoints = mapAreaPath.PathPoints;
714 for( int i = 0; i < mapAreaPath.PointCount; i++ )
716 pointNew = graph.GetRelativePoint( pathPoints[i] );
717 coord[2*i] = pointNew.X;
718 coord[2*i + 1] = pointNew.Y;
721 common.HotRegionsList.AddHotRegion(
731 if (areaBrush != null)
744 #region 3D painting and selection methods
747 /// Draws a 3D surface connecting the two specified points in 2D space.
748 /// Used to draw Line based charts.
750 /// <param name="area">Chart area reference.</param>
751 /// <param name="graph">Chart graphics.</param>
752 /// <param name="matrix">Coordinates transformation matrix.</param>
753 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
754 /// <param name="prevDataPointEx">Previous data point object.</param>
755 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
756 /// <param name="depth">Depth of the 3D surface.</param>
757 /// <param name="points">Array of points.</param>
758 /// <param name="pointIndex">Index of point to draw.</param>
759 /// <param name="pointLoopIndex">Index of points loop.</param>
760 /// <param name="tension">Line tension.</param>
761 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
762 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
763 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
764 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
765 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
766 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
767 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
768 protected override GraphicsPath Draw3DSurface(
772 LightStyle lightStyle,
773 DataPoint3D prevDataPointEx,
780 DrawingOperationTypes operationType,
782 float bottomDarkening,
783 PointF thirdPointPosition,
784 PointF fourthPointPosition,
787 // Create graphics path for selection
788 GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
789 ? new GraphicsPath() : null;
792 //****************************************************************
793 //** Find line first and second points.
794 //****************************************************************
796 // Check if points are drawn from sides to center (do only once)
797 if(centerPointIndex == int.MaxValue)
799 centerPointIndex = GetCenterPointIndex(points);
802 //************************************************************
803 //** Find line first & second points
804 //************************************************************
805 DataPoint3D secondPoint = (DataPoint3D)points[pointIndex];
806 int pointArrayIndex = pointIndex;
807 DataPoint3D firstPoint = ChartGraphics.FindPointByIndex(
809 secondPoint.index - 1,
810 (this.multiSeries) ? secondPoint : null,
811 ref pointArrayIndex);
813 //****************************************************************
814 //** Switch first and second points.
815 //****************************************************************
816 bool reversed = false;
817 if(firstPoint.index > secondPoint.index)
819 DataPoint3D tempPoint = firstPoint;
820 firstPoint = secondPoint;
821 secondPoint = tempPoint;
826 // Points can be drawn from sides to the center.
827 // In this case can't use index in the list to find first point.
828 // Use point series and real point index to find the first point.
829 // Get required point index
830 if(matrix.Perspective != 0 && centerPointIndex != int.MaxValue)
832 pointArrayIndex = pointIndex;
833 if( pointIndex != (centerPointIndex + 1))
835 firstPoint = ChartGraphics.FindPointByIndex(points, secondPoint.index - 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
839 if (!area.ReverseSeriesOrder)
841 secondPoint = ChartGraphics.FindPointByIndex(points, firstPoint.index + 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
845 firstPoint = secondPoint;
846 secondPoint = ChartGraphics.FindPointByIndex(points, secondPoint.index - 1, (this.multiSeries) ? secondPoint : null, ref pointArrayIndex);
851 // Check if points are not null
852 if(firstPoint == null || secondPoint == null)
858 // Area point is drawn as one segment
859 return Draw3DSurface( firstPoint, secondPoint, reversed,
860 area, graph, matrix, lightStyle, prevDataPointEx,
861 positionZ, depth, points, pointIndex, pointLoopIndex,
862 tension, operationType, LineSegmentType.Single,
863 topDarkening, bottomDarkening,
864 thirdPointPosition, fourthPointPosition,
870 /// Draws a 3D surface connecting the two specified points in 2D space.
871 /// Used to draw Line based charts.
873 /// <param name="firstPoint">First data point.</param>
874 /// <param name="secondPoint">Second data point.</param>
875 /// <param name="reversed">Points are in reversed order.</param>
876 /// <param name="area">Chart area reference.</param>
877 /// <param name="graph">Chart graphics.</param>
878 /// <param name="matrix">Coordinates transformation matrix.</param>
879 /// <param name="lightStyle">LightStyle style (None, Simplistic, Realistic).</param>
880 /// <param name="prevDataPointEx">Previous data point object.</param>
881 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
882 /// <param name="depth">Depth of the 3D surface.</param>
883 /// <param name="points">Array of points.</param>
884 /// <param name="pointIndex">Index of point to draw.</param>
885 /// <param name="pointLoopIndex">Index of points loop.</param>
886 /// <param name="tension">Line tension.</param>
887 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
888 /// <param name="surfaceSegmentType">Define surface segment type if it consists of several segments.</param>
889 /// <param name="topDarkening">Darkenning scale for top surface. 0 - None.</param>
890 /// <param name="bottomDarkening">Darkenning scale for bottom surface. 0 - None.</param>
891 /// <param name="thirdPointPosition">Position where the third point is actually located or float.NaN if same as in "firstPoint".</param>
892 /// <param name="fourthPointPosition">Position where the fourth point is actually located or float.NaN if same as in "secondPoint".</param>
893 /// <param name="clippedSegment">Indicates that drawn segment is 3D clipped. Only top/bottom should be drawn.</param>
894 /// <param name="clipOnTop">Indicates that top segment line should be clipped to the pkot area.</param>
895 /// <param name="clipOnBottom">Indicates that bottom segment line should be clipped to the pkot area.</param>
896 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
897 protected override GraphicsPath Draw3DSurface(
898 DataPoint3D firstPoint,
899 DataPoint3D secondPoint,
904 LightStyle lightStyle,
905 DataPoint3D prevDataPointEx,
912 DrawingOperationTypes operationType,
913 LineSegmentType surfaceSegmentType,
915 float bottomDarkening,
916 PointF thirdPointPosition,
917 PointF fourthPointPosition,
922 // Create graphics path for selection
923 GraphicsPath resultPath = ((operationType & DrawingOperationTypes.CalcElementPath) == DrawingOperationTypes.CalcElementPath)
924 ? new GraphicsPath() : null;
926 // Fint point with line properties
927 DataPoint3D pointAttr = secondPoint;
928 if(prevDataPointEx.dataPoint.IsEmpty)
930 pointAttr = prevDataPointEx;
932 else if(firstPoint.index > secondPoint.index)
934 pointAttr = firstPoint;
937 //****************************************************************
938 //** Adjust point visual properties.
939 //****************************************************************
940 Color color = (useBorderColor) ? pointAttr.dataPoint.BorderColor : pointAttr.dataPoint.Color;
941 ChartDashStyle dashStyle = pointAttr.dataPoint.BorderDashStyle;
942 if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.Color == Color.Empty)
946 if( pointAttr.dataPoint.IsEmpty && pointAttr.dataPoint.BorderDashStyle == ChartDashStyle.NotSet )
948 dashStyle = ChartDashStyle.Solid;
951 //****************************************************************
952 //** Get axis position
953 //****************************************************************
954 float axisPosition = (float)VAxis.GetPosition(this.VAxis.Crossing);
957 //****************************************************************
958 //** Calculate position of top/bootom points.
959 //****************************************************************
960 PointF thirdPoint, fourthPoint;
961 GetBottomPointsPosition(
970 // Check if point's position provided as parameter
971 if(!float.IsNaN(thirdPointPosition.Y))
973 thirdPoint.Y = thirdPointPosition.Y;
975 if(!float.IsNaN(fourthPointPosition.Y))
977 fourthPoint.Y = fourthPointPosition.Y;
981 //****************************************************************
982 //** Check if top/bottom lines of range segment intersect.
983 //****************************************************************
984 double smallDouble = 0.0001;
985 if (Math.Abs(firstPoint.yPosition - (double)thirdPoint.Y) > smallDouble &&
986 Math.Abs(secondPoint.yPosition - (double)fourthPoint.Y) > smallDouble &&
987 ((firstPoint.yPosition > thirdPoint.Y && secondPoint.yPosition < fourthPoint.Y) ||
988 (firstPoint.yPosition < thirdPoint.Y && secondPoint.yPosition > fourthPoint.Y)))
990 // This feature is not supported in 3D SplineRange chart type
993 throw (new InvalidOperationException(SR.Exception3DSplineY1ValueIsLessThenY2));
996 // Find intersection point
997 PointF intersectionCoordinates = ChartGraphics.GetLinesIntersection(
998 (float)firstPoint.xPosition, (float)firstPoint.yPosition,
999 (float)secondPoint.xPosition, (float)secondPoint.yPosition,
1000 thirdPoint.X, thirdPoint.Y,
1001 fourthPoint.X, fourthPoint.Y);
1002 DataPoint3D intersectionPoint = new DataPoint3D();
1003 intersectionPoint.xPosition = intersectionCoordinates.X;
1004 intersectionPoint.yPosition = intersectionCoordinates.Y;
1006 // Check if intersection point is valid
1007 bool splitDraw = true;
1008 if( double.IsNaN(intersectionCoordinates.X) ||
1009 double.IsNaN(intersectionCoordinates.Y) )
1015 if( (decimal)intersectionCoordinates.X == (decimal)firstPoint.xPosition &&
1016 (decimal)intersectionCoordinates.Y == (decimal)firstPoint.yPosition )
1020 if( (decimal)intersectionCoordinates.X == (decimal)secondPoint.xPosition &&
1021 (decimal)intersectionCoordinates.Y == (decimal)secondPoint.yPosition )
1029 // Check if reversed drawing order required
1031 if((pointIndex + 1) < points.Count)
1033 DataPoint3D p = (DataPoint3D)points[pointIndex + 1];
1034 if(p.index == firstPoint.index)
1040 // Draw two segments
1041 for(int segmentIndex = 0; segmentIndex <= 1; segmentIndex++)
1043 GraphicsPath segmentPath = null;
1044 if(segmentIndex == 0 && !reversed ||
1045 segmentIndex == 1 && reversed)
1047 // Set coordinates of bottom points
1048 _fourthPointY2Value = (float)intersectionPoint.yPosition;
1050 // Draw first segment
1051 intersectionPoint.dataPoint = secondPoint.dataPoint;
1052 intersectionPoint.index = secondPoint.index;
1053 segmentPath = Draw3DSurface( firstPoint, intersectionPoint, reversed,
1054 area, graph, matrix, lightStyle, prevDataPointEx,
1055 positionZ, depth, points, pointIndex, pointLoopIndex,
1056 tension, operationType, surfaceSegmentType,
1057 topDarkening, bottomDarkening,
1058 new PointF(float.NaN, float.NaN),
1059 new PointF(float.NaN, float.NaN),
1064 if(segmentIndex == 1 && !reversed ||
1065 segmentIndex == 0 && reversed)
1067 // Set coordinates of bottom points
1068 _thirdPointY2Value = (float)intersectionPoint.yPosition;
1070 // Draw second segment
1071 intersectionPoint.dataPoint = firstPoint.dataPoint;
1072 intersectionPoint.index = firstPoint.index;
1073 segmentPath = Draw3DSurface( intersectionPoint, secondPoint, reversed,
1074 area, graph, matrix, lightStyle, prevDataPointEx,
1075 positionZ, depth, points, pointIndex, pointLoopIndex,
1076 tension, operationType, surfaceSegmentType,
1077 topDarkening, bottomDarkening,
1078 new PointF(float.NaN, float.NaN),
1079 new PointF(float.NaN, float.NaN),
1085 if(resultPath != null && segmentPath != null && segmentPath.PointCount > 0)
1087 resultPath.AddPath(segmentPath, true);
1090 // Reset bottom line "forced" Y coordinates
1091 _thirdPointY2Value = float.NaN;
1092 _fourthPointY2Value = float.NaN;
1101 //****************************************************************
1102 //** Detect visibility of the bounding rectangle.
1103 //****************************************************************
1104 float minX = (float)Math.Min(firstPoint.xPosition, secondPoint.xPosition);
1105 float minY = (float)Math.Min(firstPoint.yPosition, secondPoint.yPosition);
1106 minY = (float)Math.Min(minY, axisPosition);
1107 float maxX = (float)Math.Max(firstPoint.xPosition, secondPoint.xPosition);
1108 float maxY = (float)Math.Max(firstPoint.yPosition, secondPoint.yPosition);
1109 maxY = (float)Math.Max(maxY, axisPosition);
1110 RectangleF position = new RectangleF(minX, minY, maxX - minX, maxY - minY);
1111 SurfaceNames visibleSurfaces = graph.GetVisibleSurfaces(position,positionZ,depth,matrix);
1113 // Check if area point is drawn upside down.
1114 bool upSideDown = false;
1115 if(firstPoint.yPosition >= thirdPoint.Y && secondPoint.yPosition >= fourthPoint.Y)
1119 // Switch visibility between Top & Bottom surfaces
1120 bool topVisible = ( (visibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top );
1121 bool bottomVisible = ( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom );
1122 visibleSurfaces ^= SurfaceNames.Bottom;
1123 visibleSurfaces ^= SurfaceNames.Top;
1126 visibleSurfaces |= SurfaceNames.Bottom;
1130 visibleSurfaces |= SurfaceNames.Top;
1134 // Check Top/Bottom surface visibility
1135 GetTopSurfaceVisibility(area, firstPoint, secondPoint, upSideDown, positionZ, depth, matrix, ref visibleSurfaces);
1137 // Top and bottom surfaces are always visible for spline range
1138 bool bottomFirst = true;
1141 if( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom )
1143 bottomFirst = false;
1145 if( (visibleSurfaces & SurfaceNames.Bottom) == 0 &&
1146 (visibleSurfaces & SurfaceNames.Top) == 0 )
1148 bottomFirst = false;
1152 visibleSurfaces |= SurfaceNames.Bottom;
1153 visibleSurfaces |= SurfaceNames.Top;
1156 // Round double values to 5 decimals
1157 firstPoint.xPosition = Math.Round(firstPoint.xPosition, 5);
1158 firstPoint.yPosition = Math.Round(firstPoint.yPosition, 5);
1159 secondPoint.xPosition = Math.Round(secondPoint.xPosition, 5);
1160 secondPoint.yPosition = Math.Round(secondPoint.yPosition, 5);
1162 //****************************************************************
1163 //** Clip area first and second data points inside
1164 //** the plotting area.
1165 //****************************************************************
1191 //****************************************************************
1192 //** Clip area third and fourth data points inside
1193 //** the plotting area.
1194 //****************************************************************
1195 if(ClipBottomPoints(
1222 //****************************************************************
1223 //** Draw elements of area chart in 2 layers (back & front)
1224 //****************************************************************
1225 for(int elemLayer = 1; elemLayer <= 2; elemLayer++)
1227 // Loop through all surfaces
1228 SurfaceNames[] surfacesOrder = null;
1230 surfacesOrder = new SurfaceNames[] {SurfaceNames.Back, SurfaceNames.Bottom, SurfaceNames.Top, SurfaceNames.Left, SurfaceNames.Right, SurfaceNames.Front};
1232 surfacesOrder = new SurfaceNames[] {SurfaceNames.Back, SurfaceNames.Top, SurfaceNames.Bottom, SurfaceNames.Left, SurfaceNames.Right, SurfaceNames.Front};
1234 LineSegmentType lineSegmentType = LineSegmentType.Middle;
1235 foreach(SurfaceNames currentSurface in surfacesOrder)
1237 // Check id surface should be drawn
1238 if (ChartGraphics.ShouldDrawLineChartSurface(area, area.ReverseSeriesOrder, currentSurface, visibleSurfaces, color,
1239 points, firstPoint, secondPoint, this.multiSeries, ref lineSegmentType) != elemLayer)
1244 // Draw only borders of the invisible elements on the back layer
1245 Color surfaceColor = color;
1246 Color surfaceBorderColor = pointAttr.dataPoint.BorderColor;
1249 // Draw only if point color is semi-transparent
1250 if(surfaceColor.A == 255)
1255 // Define drawing colors
1256 surfaceColor = Color.Transparent;
1257 if(surfaceBorderColor == Color.Empty)
1259 // If border color is emty use color slightly darker than main back color
1260 surfaceBorderColor = ChartGraphics.GetGradientColor( color, Color.Black, 0.2 );
1265 GraphicsPath surfacePath = null;
1266 switch(currentSurface)
1268 case(SurfaceNames.Top):
1269 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth,
1270 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle,
1271 firstPoint, secondPoint, points, pointIndex,
1272 tension, operationType, LineSegmentType.Middle,
1273 (this.showPointLines) ? true : false, false, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1275 case(SurfaceNames.Bottom):
1277 // Calculate coordinates
1278 DataPoint3D dp1 = new DataPoint3D();
1279 dp1.dataPoint = firstPoint.dataPoint;
1280 dp1.index = firstPoint.index;
1281 dp1.xPosition = firstPoint.xPosition;
1282 dp1.yPosition = thirdPoint.Y;
1283 DataPoint3D dp2 = new DataPoint3D();
1284 dp2.dataPoint = secondPoint.dataPoint;
1285 dp2.index = secondPoint.index;
1286 dp2.xPosition = secondPoint.xPosition;
1287 dp2.yPosition = fourthPoint.Y;
1290 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth,
1291 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle,
1292 dp1, dp2, points, pointIndex,
1293 tension, operationType, LineSegmentType.Middle,
1294 (this.showPointLines) ? true : false, false, area.ReverseSeriesOrder, this.multiSeries, 1, true);
1298 case(SurfaceNames.Left):
1300 if(surfaceSegmentType == LineSegmentType.Single ||
1301 (!area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.First) ||
1302 (area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.Last))
1305 // Calculate coordinates
1306 DataPoint3D leftMostPoint = (firstPoint.xPosition <= secondPoint.xPosition) ? firstPoint : secondPoint;
1307 DataPoint3D dp1 = new DataPoint3D();
1308 dp1.xPosition = leftMostPoint.xPosition;
1309 dp1.yPosition = (firstPoint.xPosition <= secondPoint.xPosition) ? thirdPoint.Y : fourthPoint.Y;
1310 DataPoint3D dp2 = new DataPoint3D();
1311 dp2.xPosition = leftMostPoint.xPosition;;
1312 dp2.yPosition = leftMostPoint.yPosition;
1315 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth,
1316 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle,
1317 dp1, dp2, points, pointIndex,
1318 0f, operationType, LineSegmentType.Single, false, true, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1323 case(SurfaceNames.Right):
1325 if(surfaceSegmentType == LineSegmentType.Single ||
1326 (!area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.Last) ||
1327 (area.ReverseSeriesOrder && surfaceSegmentType == LineSegmentType.First))
1330 // Calculate coordinates
1331 DataPoint3D rightMostPoint = (secondPoint.xPosition >= firstPoint.xPosition) ? secondPoint : firstPoint;
1332 DataPoint3D dp1 = new DataPoint3D();
1333 dp1.xPosition = rightMostPoint.xPosition;
1334 dp1.yPosition = (secondPoint.xPosition >= firstPoint.xPosition) ? fourthPoint.Y : thirdPoint.Y;
1335 DataPoint3D dp2 = new DataPoint3D();
1336 dp2.xPosition = rightMostPoint.xPosition;
1337 dp2.yPosition = rightMostPoint.yPosition;
1340 surfacePath = graph.Draw3DSurface( area, matrix, lightStyle, currentSurface, positionZ, depth,
1341 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth, dashStyle,
1342 dp1, dp2, points, pointIndex,
1343 0f, operationType, LineSegmentType.Single, false, true, area.ReverseSeriesOrder, this.multiSeries, 0, true);
1348 case(SurfaceNames.Back):
1350 // Calculate coordinates
1351 DataPoint3D dp1 = new DataPoint3D();
1352 dp1.dataPoint = firstPoint.dataPoint;
1353 dp1.index = firstPoint.index;
1354 dp1.xPosition = firstPoint.xPosition;
1355 dp1.yPosition = thirdPoint.Y;
1356 DataPoint3D dp2 = new DataPoint3D();
1357 dp2.dataPoint = secondPoint.dataPoint;
1358 dp2.index = secondPoint.index;
1359 dp2.xPosition = secondPoint.xPosition;
1360 dp2.yPosition = fourthPoint.Y;
1363 surfacePath = Draw3DSplinePolygon( graph, area, positionZ,
1364 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth,
1365 firstPoint, secondPoint, dp2, dp1, points,
1366 tension, operationType, lineSegmentType,
1367 (this.showPointLines) ? true : false);
1371 case(SurfaceNames.Front):
1374 // Calculate coordinates
1375 DataPoint3D dp1 = new DataPoint3D();
1376 dp1.dataPoint = firstPoint.dataPoint;
1377 dp1.index = firstPoint.index;
1378 dp1.xPosition = firstPoint.xPosition;
1379 dp1.yPosition = thirdPoint.Y;
1380 DataPoint3D dp2 = new DataPoint3D();
1381 dp2.dataPoint = secondPoint.dataPoint;
1382 dp2.index = secondPoint.index;
1383 dp2.xPosition = secondPoint.xPosition;
1384 dp2.yPosition = fourthPoint.Y;
1386 // Change segment type for the reversed series order
1387 if (area.ReverseSeriesOrder)
1389 if(lineSegmentType == LineSegmentType.First)
1391 lineSegmentType = LineSegmentType.Last;
1393 else if(lineSegmentType == LineSegmentType.Last)
1395 lineSegmentType = LineSegmentType.First;
1399 if(surfaceSegmentType != LineSegmentType.Single)
1401 if( surfaceSegmentType == LineSegmentType.Middle ||
1402 ( surfaceSegmentType == LineSegmentType.First && lineSegmentType != LineSegmentType.First) ||
1403 ( surfaceSegmentType == LineSegmentType.Last && lineSegmentType != LineSegmentType.Last) )
1405 lineSegmentType = LineSegmentType.Middle;
1409 if(lineSegmentType == LineSegmentType.First)
1411 lineSegmentType = LineSegmentType.Last;
1413 else if(lineSegmentType == LineSegmentType.Last)
1415 lineSegmentType = LineSegmentType.First;
1421 surfacePath = Draw3DSplinePolygon( graph, area, positionZ + depth,
1422 surfaceColor, surfaceBorderColor, pointAttr.dataPoint.BorderWidth,
1423 firstPoint, secondPoint, dp2, dp1, points,
1424 tension, operationType, lineSegmentType,
1425 (this.showPointLines) ? true : false);
1431 // Add path of the fully visible surfaces to the result surface
1432 if(elemLayer == 2 && resultPath != null && surfacePath != null && surfacePath.PointCount > 0)
1434 resultPath.CloseFigure();
1435 resultPath.AddPath(surfacePath, true);
1445 /// Gets visibility of the top surface.
1447 /// <param name="area">Chart area object.</param>
1448 /// <param name="firstPoint">First data point of the line.</param>
1449 /// <param name="secondPoint">Second data point of the line.</param>
1450 /// <param name="upSideDown">Indicates that Y values of the data points are below axis line.</param>
1451 /// <param name="positionZ">Z coordinate of the back side of the cube.</param>
1452 /// <param name="depth">Cube depth.</param>
1453 /// <param name="matrix">Coordinate transformation matrix.</param>
1454 /// <param name="visibleSurfaces">Surface visibility reference. Initialized with bounary cube visibility.</param>
1455 protected virtual void GetTopSurfaceVisibility(
1457 DataPoint3D firstPoint,
1458 DataPoint3D secondPoint,
1463 ref SurfaceNames visibleSurfaces)
1465 //***********************************************************************
1466 //** Check Top surface visibility
1467 //***********************************************************************
1468 // If Top surface visibility in bounding rectangle - do not gurantee angled linde visibility
1469 if( (visibleSurfaces & SurfaceNames.Top) == SurfaceNames.Top)
1471 visibleSurfaces ^= SurfaceNames.Top;
1474 // Create top surface coordinates in 3D space
1475 Point3D[] cubePoints = new Point3D[3];
1476 if (!area.ReverseSeriesOrder)
1478 if(!upSideDown && firstPoint.xPosition < secondPoint.xPosition ||
1479 upSideDown && firstPoint.xPosition > secondPoint.xPosition)
1481 cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth );
1482 cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1483 cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1487 cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth );
1488 cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1489 cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1494 if(!upSideDown && secondPoint.xPosition < firstPoint.xPosition ||
1495 upSideDown && secondPoint.xPosition > firstPoint.xPosition)
1497 cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ + depth );
1498 cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1499 cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1503 cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ + depth );
1504 cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ );
1505 cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ );
1509 // Tranform coordinates
1510 matrix.TransformPoints( cubePoints );
1512 // Check the top side visibility
1513 if(ChartGraphics.IsSurfaceVisible(cubePoints[0],cubePoints[1],cubePoints[2]))
1515 visibleSurfaces |= SurfaceNames.Top;
1519 //***********************************************************************
1520 //** Check Bottom surface visibility
1521 //***********************************************************************
1523 // Get bottom surface points
1524 PointF thirdPoint, fourthPoint;
1525 GetBottomPointsPosition(area.Common, area, 0, ref firstPoint, ref secondPoint, out thirdPoint, out fourthPoint);
1528 // If Bottom surface visibility in bounding rectangle - do not gurantee angled linde visibility
1529 if( (visibleSurfaces & SurfaceNames.Bottom) == SurfaceNames.Bottom)
1531 visibleSurfaces ^= SurfaceNames.Bottom;
1534 // Create top surface coordinates in 3D space
1535 cubePoints = new Point3D[3];
1536 if (!area.ReverseSeriesOrder)
1538 if(!upSideDown && firstPoint.xPosition < secondPoint.xPosition ||
1539 upSideDown && firstPoint.xPosition > secondPoint.xPosition)
1541 cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ + depth );
1542 cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1543 cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1547 cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ + depth );
1548 cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1549 cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1554 if(!upSideDown && secondPoint.xPosition < firstPoint.xPosition ||
1555 upSideDown && secondPoint.xPosition > firstPoint.xPosition)
1557 cubePoints[0] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ + depth );
1558 cubePoints[1] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1559 cubePoints[2] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1563 cubePoints[0] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ + depth );
1564 cubePoints[1] = new Point3D( (float)firstPoint.xPosition, (float)thirdPoint.Y, positionZ );
1565 cubePoints[2] = new Point3D( (float)secondPoint.xPosition, (float)fourthPoint.Y, positionZ );
1569 // Tranform coordinates
1570 matrix.TransformPoints( cubePoints );
1572 // Check the top side visibility
1573 if(ChartGraphics.IsSurfaceVisible(cubePoints[2],cubePoints[1],cubePoints[0]))
1575 visibleSurfaces |= SurfaceNames.Bottom;
1581 /// Gets position ob the bottom points in area chart.
1583 /// <param name="common">Chart common elements.</param>
1584 /// <param name="area">Chart area the series belongs to.</param>
1585 /// <param name="axisPosition">Axis position.</param>
1586 /// <param name="firstPoint">First top point coordinates.</param>
1587 /// <param name="secondPoint">Second top point coordinates.</param>
1588 /// <param name="thirdPoint">Returns third bottom point coordinates.</param>
1589 /// <param name="fourthPoint">Returns fourth bottom point coordinates.</param>
1590 protected virtual void GetBottomPointsPosition(
1591 CommonElements common,
1594 ref DataPoint3D firstPoint,
1595 ref DataPoint3D secondPoint,
1596 out PointF thirdPoint,
1597 out PointF fourthPoint)
1599 // Set active vertical axis
1600 Axis vAxis = area.GetAxis(AxisName.Y, firstPoint.dataPoint.series.YAxisType, firstPoint.dataPoint.series.YSubAxisName);
1602 // Initialize points using second Y value
1603 float secondYValue = (float)vAxis.GetPosition(firstPoint.dataPoint.YValues[1]);
1604 thirdPoint = new PointF((float)firstPoint.xPosition, secondYValue);
1605 secondYValue = (float)vAxis.GetPosition(secondPoint.dataPoint.YValues[1]);
1606 fourthPoint = new PointF((float)secondPoint.xPosition, secondYValue);
1608 // Check if "forced" Y values where set
1609 if(!float.IsNaN(_thirdPointY2Value))
1611 thirdPoint.Y = _thirdPointY2Value;
1614 if(!float.IsNaN(_fourthPointY2Value))
1616 fourthPoint.Y = _fourthPointY2Value;
1621 /// Draws a 3D polygon defined by 4 points in 2D space.
1622 /// Top and Bottom lines are drawn as splines of specified tension.
1624 /// <param name="area">Chart area reference.</param>
1625 /// <param name="graph">Chart graphics.</param>
1626 /// <param name="positionZ">Z position of the back side of the 3D surface.</param>
1627 /// <param name="backColor">Color of rectangle</param>
1628 /// <param name="borderColor">Border Color</param>
1629 /// <param name="borderWidth">Border Width</param>
1630 /// <param name="firstPoint">First point.</param>
1631 /// <param name="secondPoint">Second point.</param>
1632 /// <param name="thirdPoint">Third point.</param>
1633 /// <param name="fourthPoint">Fourth point.</param>
1634 /// <param name="points">Array of points.</param>
1635 /// <param name="tension">Line tension.</param>
1636 /// <param name="operationType">AxisName of operation Drawing, Calculating Path or Both</param>
1637 /// <param name="lineSegmentType">AxisName of line segment. Used for step lines and splines.</param>
1638 /// <param name="forceThinBorder">Thin border will be drawn on all segments.</param>
1639 /// <returns>Returns elemnt shape path if operationType parameter is set to CalcElementPath, otherwise Null.</returns>
1640 internal GraphicsPath Draw3DSplinePolygon(
1641 ChartGraphics graph,
1647 DataPoint3D firstPoint,
1648 DataPoint3D secondPoint,
1649 DataPoint3D thirdPoint,
1650 DataPoint3D fourthPoint,
1653 DrawingOperationTypes operationType,
1654 LineSegmentType lineSegmentType,
1655 bool forceThinBorder)
1657 // Check tension parameter
1660 SurfaceNames thinBorderSides = 0;
1663 thinBorderSides = SurfaceNames.Left | SurfaceNames.Right;
1666 return graph.Draw3DPolygon( area, area.matrix3D, SurfaceNames.Front, positionZ,
1667 backColor, borderColor, borderWidth,
1668 firstPoint, secondPoint, thirdPoint, fourthPoint,
1669 operationType, lineSegmentType, thinBorderSides);
1672 // Create graphics path for selection
1673 bool drawElements = ((operationType & DrawingOperationTypes.DrawElement) == DrawingOperationTypes.DrawElement);
1674 GraphicsPath resultPath = new GraphicsPath();
1676 //**********************************************************************
1677 //** Prepare, transform polygon coordinates
1678 //**********************************************************************
1680 // Get top line path
1681 GraphicsPath topLine = graph.GetSplineFlattenPath(
1683 firstPoint, secondPoint, points, tension, false, true, 0);
1685 // Get bottom line path
1686 GraphicsPath bottomLine = graph.GetSplineFlattenPath(
1688 thirdPoint, fourthPoint, points, tension, false, true, 1);
1690 // Add paths to the result path
1691 resultPath.AddPath(topLine, true);
1692 resultPath.AddPath(bottomLine, true);
1693 resultPath.CloseAllFigures();
1696 //**********************************************************************
1697 //** Define drawing colors
1698 //**********************************************************************
1700 // Define 3 points polygon
1701 Point3D [] points3D = new Point3D[3];
1702 points3D[0] = new Point3D((float)firstPoint.xPosition, (float)firstPoint.yPosition, positionZ);
1703 points3D[1] = new Point3D((float)secondPoint.xPosition, (float)secondPoint.yPosition, positionZ);
1704 points3D[2] = new Point3D((float)thirdPoint.xPosition, (float)thirdPoint.yPosition, positionZ);
1706 // Transform coordinates
1707 area.matrix3D.TransformPoints( points3D );
1710 bool topIsVisible = ChartGraphics.IsSurfaceVisible( points3D[0], points3D[1], points3D[2]);
1711 Color polygonColor = area.matrix3D.GetPolygonLight(points3D, backColor, topIsVisible, area.Area3DStyle.Rotation, SurfaceNames.Front, area.ReverseSeriesOrder);
1712 Color surfaceBorderColor = borderColor;
1713 if(surfaceBorderColor == Color.Empty)
1715 // If border color is emty use color slightly darker than main back color
1716 surfaceBorderColor = ChartGraphics.GetGradientColor( backColor, Color.Black, 0.2 );
1719 //**********************************************************************
1720 //** Draw elements if required.
1721 //**********************************************************************
1722 Pen thickBorderPen = null;
1725 // Remember SmoothingMode and turn off anti aliasing
1726 SmoothingMode oldSmoothingMode = graph.SmoothingMode;
1727 graph.SmoothingMode = SmoothingMode.Default;
1730 using (Brush brush = new SolidBrush(polygonColor))
1732 graph.FillPath(brush, resultPath);
1735 // Return old smoothing mode
1736 graph.SmoothingMode = oldSmoothingMode;
1738 // Draw thin polygon border of darker color around the whole polygon
1741 graph.DrawPath(new Pen(surfaceBorderColor, 1), resultPath);
1743 else if(polygonColor.A == 255)
1745 graph.DrawPath(new Pen(polygonColor, 1), resultPath);
1748 // Create thick border line pen
1749 thickBorderPen = new Pen(surfaceBorderColor, borderWidth);
1750 thickBorderPen.StartCap = LineCap.Round;
1751 thickBorderPen.EndCap = LineCap.Round;
1753 // Draw thick Top & Bottom lines
1754 graph.DrawPath(thickBorderPen, topLine);
1755 graph.DrawPath(thickBorderPen, bottomLine);
1757 // Draw thick Right & Left lines on first & last segments of the line
1758 if(lineSegmentType == LineSegmentType.First)
1760 graph.DrawLine(thickBorderPen, topLine.PathPoints[0], bottomLine.GetLastPoint());
1763 else if(lineSegmentType == LineSegmentType.Last)
1765 graph.DrawLine(thickBorderPen, topLine.GetLastPoint(), bottomLine.PathPoints[0]);
1770 // Calculate path for selection
1771 if(resultPath != null && thickBorderPen != null)
1773 // Widen result path
1776 resultPath.Widen(thickBorderPen);
1778 catch (OutOfMemoryException)
1780 // GraphicsPath.Widen incorrectly throws OutOfMemoryException
1781 // catching here and reacting by not widening
1783 catch (ArgumentException)
1793 #region IDisposable overrides
1795 /// Releases unmanaged and - optionally - managed resources
1797 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
1798 protected override void Dispose(bool disposing)
1802 // Dispose managed resources
1803 if (this.areaBottomPath != null)
1805 this.areaBottomPath.Dispose();
1806 this.areaBottomPath = null;
1808 if (this.areaPath != null)
1810 this.areaPath.Dispose();
1811 this.areaPath = null;
1814 base.Dispose(disposing);