Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / ChartTypes / ColumnChart.cs
blob12a04e29d32a30b88d92b03f5aed97ffb73c897c
1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: ColumnChart.cs
9 //
10 // Namespace: System.Web.UI.DataVisualization.Charting.ChartTypes
12 // Classes: ColumnChart, RangeColumnChart
14 // Purpose: Provides 2D/3D drawing and hit testing functionality
15 // for the Column and RangeColumn charts.
17 // Reviewed: AG - Aug 8, 2002
18 // AG - Microsoft 6, 2007
20 //===================================================================
22 #region Used namespaces
24 using System;
25 using System.Collections;
26 using System.Collections.Generic;
27 using System.Drawing;
28 using System.Drawing.Drawing2D;
29 using System.Globalization;
31 #if Microsoft_CONTROL
32 using System.Windows.Forms.DataVisualization.Charting.Utilities;
33 #else
34 using System.Web.UI.DataVisualization.Charting.Utilities;
35 #endif
37 #endregion
39 #if Microsoft_CONTROL
40 namespace System.Windows.Forms.DataVisualization.Charting.ChartTypes
41 #else
42 namespace System.Web.UI.DataVisualization.Charting.ChartTypes
43 #endif
45 /// <summary>
46 /// ColumnChart class provides 2D/3D drawing and hit testing
47 /// functionality for the Column and RangeColumn charts. The
48 /// only difference between the RangeColumn and Column chart
49 /// is that 2 Y values are used to position top and bottom
50 /// side of each RangeColumn column.
51 /// </summary>
52 internal class ColumnChart : PointChart
54 #region Fields
56 /// <summary>
57 /// Labels and markers have to be shifted if there
58 /// is more than one series for column chart.
59 /// </summary>
60 private double _shiftedX = 0;
62 /// <summary>
63 /// Labels and markers have to be shifted if there
64 /// is more than one series for column chart. This property
65 /// will give a name of the series, which is used, for
66 /// labels and markers. Point chart
67 /// </summary>
68 private string _shiftedSerName = "";
70 /// <summary>
71 /// Indicates that two Y values are used to calculate column position
72 /// </summary>
73 protected bool useTwoValues = false;
75 /// <summary>
76 /// Indicates that columns from different series are drawn side by side
77 /// </summary>
78 protected bool drawSeriesSideBySide = true;
80 /// <summary>
81 /// Coordinates of COP used when sorting 3D points order
82 /// </summary>
83 protected COPCoordinates coordinates = COPCoordinates.X;
85 #endregion
87 #region IChartType interface implementation
89 /// <summary>
90 /// Chart type name
91 /// </summary>
92 override public string Name { get{ return ChartTypeNames.Column;}}
94 /// <summary>
95 /// Gets chart type image.
96 /// </summary>
97 /// <param name="registry">Chart types registry object.</param>
98 /// <returns>Chart type image.</returns>
99 override public System.Drawing.Image GetImage(ChartTypeRegistry registry)
101 return (System.Drawing.Image)registry.ResourceManager.GetObject(this.Name + "ChartType");
104 /// <summary>
105 /// True if chart type is stacked
106 /// </summary>
107 override public bool Stacked { get{ return false;}}
109 /// <summary>
110 /// True if chart type supports axeses
111 /// </summary>
112 override public bool RequireAxes { get{ return true;} }
114 /// <summary>
115 /// True if chart type supports logarithmic axes
116 /// </summary>
117 override public bool SupportLogarithmicAxes { get{ return true;} }
119 /// <summary>
120 /// True if chart type requires to switch the value (Y) axes position
121 /// </summary>
122 override public bool SwitchValueAxes { get{ return false;} }
124 /// <summary>
125 /// True if chart series can be placed side-by-side.
126 /// </summary>
127 override public bool SideBySideSeries { get{ return true;} }
129 /// <summary>
130 /// True if each data point of a chart must be represented in the legend
131 /// </summary>
132 override public bool DataPointsInLegend { get{ return false;} }
134 /// <summary>
135 /// Indicates that extra Y values are connected to the scale of the Y axis
136 /// </summary>
137 override public bool ExtraYValuesConnectedToYAxis{ get { return false; } }
139 /// <summary>
140 /// True if palette colors should be applied for each data paoint.
141 /// Otherwise the color is applied to the series.
142 /// </summary>
143 override public bool ApplyPaletteColorsToPoints { get { return false; } }
145 /// <summary>
146 /// How to draw series/points in legend:
147 /// Filled rectangle, Line or Marker
148 /// </summary>
149 /// <param name="series">Legend item series.</param>
150 /// <returns>Legend item style.</returns>
151 override public LegendImageStyle GetLegendImageStyle(Series series)
153 return LegendImageStyle.Rectangle;
156 /// <summary>
157 /// Number of supported Y value(s) per point
158 /// </summary>
159 override public int YValuesPerPoint{ get { return 1; } }
161 /// <summary>
162 /// If the crossing value is auto Crossing value should be
163 /// automatically set to zero for some chart
164 /// types (Bar, column, area etc.)
165 /// </summary>
166 override public bool ZeroCrossing { get{ return true;} }
168 #endregion
170 #region Constructor
172 /// <summary>
173 /// Default constructor
174 /// </summary>
175 public ColumnChart() : base(false)
179 #endregion
181 #region Properties
183 /// <summary>
184 /// Labels and markers have to be shifted if there
185 /// is more than one series for column chart.
186 /// </summary>
187 override public double ShiftedX
191 return _shiftedX;
195 _shiftedX = value;
199 /// <summary>
200 /// Labels and markers have to be shifted if there
201 /// is more than one series for column chart. This property
202 /// will give a name of the series, which is used, for
203 /// labels and markers.
204 /// </summary>
205 override public string ShiftedSerName
209 return _shiftedSerName;
213 _shiftedSerName = value;
217 #endregion
219 #region Painting and selection methods
221 /// <summary>
222 /// Paint Column Chart.
223 /// </summary>
224 /// <param name="graph">The Chart Graphics object.</param>
225 /// <param name="common">The Common elements object.</param>
226 /// <param name="area">Chart area for this chart.</param>
227 /// <param name="seriesToDraw">Chart series to draw.</param>
228 override public void Paint(
229 ChartGraphics graph,
230 CommonElements common,
231 ChartArea area,
232 Series seriesToDraw
235 this.Common = common;
236 // Draw columns
237 ProcessChartType( false, false, graph, common, area, seriesToDraw );
239 // Draw labels and markers
240 ProcessChartType( true, false, graph, common, area, seriesToDraw );
243 /// <summary>
244 /// This method recalculates size of the columns and paint them or do the hit test.
245 /// This method is used from Paint or Select method.
246 /// </summary>
247 /// <param name="labels">Mode which draws only labels and markers.</param>
248 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
249 /// <param name="graph">The Chart Graphics object.</param>
250 /// <param name="common">The Common elements object.</param>
251 /// <param name="area">Chart area for this chart.</param>
252 /// <param name="seriesToDraw">Chart series to draw.</param>
253 private void ProcessChartType(
254 bool labels,
255 bool selection,
256 ChartGraphics graph,
257 CommonElements common,
258 ChartArea area,
259 Series seriesToDraw )
261 // Prosess 3D chart type
262 if(area.Area3DStyle.Enable3D)
264 ProcessChartType3D( labels, selection, graph, common, area, seriesToDraw );
265 return;
268 // Get pixel size
269 SizeF pixelRelSize = graph.GetRelativeSize(new SizeF(1.1f, 1.1f));
271 // All data series from chart area which have Column chart type
272 List<string> typeSeries = area.GetSeriesFromChartType(Name);
274 // Check if series should be drawn side by side
275 bool currentDrawSeriesSideBySide = this.drawSeriesSideBySide;
276 foreach(string seriesName in typeSeries)
278 if(common.DataManager.Series[seriesName].IsCustomPropertySet(CustomPropertyName.DrawSideBySide))
280 string attribValue = common.DataManager.Series[seriesName][CustomPropertyName.DrawSideBySide];
281 if(String.Compare( attribValue, "False", StringComparison.OrdinalIgnoreCase) == 0 )
283 currentDrawSeriesSideBySide = false;
285 else if(String.Compare( attribValue, "True", StringComparison.OrdinalIgnoreCase) == 0)
287 currentDrawSeriesSideBySide = true;
289 else if(String.Compare( attribValue, "Auto", StringComparison.OrdinalIgnoreCase) == 0)
291 // Do nothing
293 else
295 throw (new InvalidOperationException(SR.ExceptionAttributeDrawSideBySideInvalid));
300 // Find the number of "Column chart" data series
301 double numOfSeries = typeSeries.Count;
302 if(!currentDrawSeriesSideBySide)
304 numOfSeries = 1;
307 // Check if column chart series are indexed
308 bool indexedSeries = ChartHelper.IndexedSeries(this.Common, area.GetSeriesFromChartType(Name).ToArray());
310 //************************************************************
311 //** Loop through all series
312 //************************************************************
313 int seriesIndx = 0;
314 foreach( Series ser in common.DataManager.Series )
316 // Process non empty series of the area with Column chart type
317 if( String.Compare( ser.ChartTypeName, Name, true, System.Globalization.CultureInfo.CurrentCulture) != 0
318 || ser.ChartArea != area.Name || ser.Points.Count == 0 || !ser.IsVisible())
320 continue;
323 // Set shifted series name property
324 ShiftedSerName = ser.Name;
326 // Set active vertical/horizontal axis
327 Axis vAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
328 Axis hAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
329 double horizontalViewMax = hAxis.ViewMaximum;
330 double horizontalViewMin = hAxis.ViewMinimum;
331 double verticalViewMax = vAxis.ViewMaximum;
332 double verticalViewMin = vAxis.ViewMinimum;
333 double verticalAxisCrossing = vAxis.GetPosition(vAxis.Crossing);
335 // Get points interval:
336 // - set interval to 1 for indexed series
337 // - if points are not equaly spaced, the minimum interval between points is selected.
338 // - if points have same interval bars do not overlap each other.
339 bool sameInterval = false;
340 double interval = 1;
341 if(!indexedSeries)
343 if (ser.Points.Count == 1 &&
344 (ser.XValueType == ChartValueType.Date ||
345 ser.XValueType == ChartValueType.DateTime ||
346 ser.XValueType == ChartValueType.Time ||
347 ser.XValueType == ChartValueType.DateTimeOffset))
349 // Check if interval is the same
350 area.GetPointsInterval(typeSeries, hAxis.IsLogarithmic, hAxis.logarithmBase, true, out sameInterval);
352 // Special case when there is only one data point and date scale is used.
353 if (!double.IsNaN(hAxis.majorGrid.GetInterval()) && hAxis.majorGrid.GetIntervalType() != DateTimeIntervalType.NotSet)
355 interval = ChartHelper.GetIntervalSize(hAxis.minimum, hAxis.majorGrid.GetInterval(), hAxis.majorGrid.GetIntervalType());
357 else
359 interval = ChartHelper.GetIntervalSize(hAxis.minimum, hAxis.Interval, hAxis.IntervalType);
362 else
364 interval = area.GetPointsInterval( typeSeries, hAxis.IsLogarithmic, hAxis.logarithmBase, true, out sameInterval );
368 // Get column width
369 double width = ser.GetPointWidth(graph, hAxis, interval, 0.8) / numOfSeries;
371 // Call Back Paint event
372 if( !selection )
374 common.Chart.CallOnPrePaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
377 //************************************************************
378 //** Loop through all points in series
379 //************************************************************
380 int index = 0;
381 foreach( DataPoint point in ser.Points )
383 // Change Y value if Column is out of plot area
384 double yValue = vAxis.GetLogValue( GetYValue(common, area, ser, point, index, (useTwoValues) ? 1 : 0) );
386 if( yValue > verticalViewMax )
388 yValue = verticalViewMax;
390 if( yValue < verticalViewMin )
392 yValue = verticalViewMin;
395 // Recalculates Height position and zero position of Columns
396 double height = vAxis.GetLinearPosition( yValue );
398 // Set start position for a column
399 double columnStartPosition = 0;
400 if(useTwoValues)
402 // Point Y value (first) is used to determine the column starting position
403 double yValueStart = vAxis.GetLogValue( GetYValue(common, area, ser, point, index, 0 ) );
404 if( yValueStart > verticalViewMax )
406 yValueStart = verticalViewMax;
408 else if( yValueStart < verticalViewMin )
410 yValueStart = verticalViewMin;
413 columnStartPosition = vAxis.GetLinearPosition(yValueStart);
415 else
417 // Column starts on the horizontal axis crossing
418 columnStartPosition = verticalAxisCrossing;
421 // Increase point index
422 index++;
424 // Set x position
425 double xCenterVal;
426 double xPosition;
427 if( indexedSeries )
429 // The formula for position is based on a distance
430 //from the grid line or nPoints position.
431 xPosition = hAxis.GetPosition( (double)index ) - width * ((double) numOfSeries) / 2.0 + width/2 + seriesIndx * width;
432 xCenterVal = hAxis.GetPosition( (double)index );
434 else if( sameInterval )
436 xPosition = hAxis.GetPosition( point.XValue ) - width * ((double) numOfSeries) / 2.0 + width/2 + seriesIndx * width;
437 xCenterVal = hAxis.GetPosition( point.XValue );
439 else
441 xPosition = hAxis.GetPosition( point.XValue );
442 xCenterVal = hAxis.GetPosition( point.XValue );
445 // Labels and markers have to be shifted if there
446 // is more than one series for column chart.
447 ShiftedX = xPosition - xCenterVal;
450 // Make sure that points with small values are still visible
451 if( height < columnStartPosition &&
452 (columnStartPosition - height) < pixelRelSize.Height)
454 height = columnStartPosition - pixelRelSize.Height;
456 if( height > columnStartPosition &&
457 (height - columnStartPosition) < pixelRelSize.Height)
459 height = columnStartPosition + pixelRelSize.Height;
462 // Get column rectangle
463 RectangleF rectSize = RectangleF.Empty;
466 // Set the Column rectangle
467 rectSize.X = (float)(xPosition - width/2);
468 rectSize.Width = (float)(width);
471 // The top side of rectangle has always
472 // smaller value than a bottom value
473 if( columnStartPosition < height )
475 rectSize.Y = (float)columnStartPosition;
476 rectSize.Height = (float)height - rectSize.Y;
478 else
480 rectSize.Y = (float)height;
481 rectSize.Height = (float)columnStartPosition - rectSize.Y;
485 catch(OverflowException)
487 continue;
490 // if data point is not empty
491 if( point.IsEmpty )
493 continue;
496 //************************************************************
497 // Painting mode
498 //************************************************************
499 if( common.ProcessModePaint )
501 if( !labels )
503 // Check if column is completly out of the data scaleView
504 double xValue = (indexedSeries) ? index : point.XValue;
505 xValue = hAxis.GetLogValue(xValue);
506 if(xValue < horizontalViewMin || xValue > horizontalViewMax )
508 continue;
511 // Check if column is partialy in the data scaleView
512 bool clipRegionSet = false;
513 if(rectSize.X < area.PlotAreaPosition.X || rectSize.Right > area.PlotAreaPosition.Right)
515 // Set clipping region for line drawing
516 graph.SetClip( area.PlotAreaPosition.ToRectangleF() );
517 clipRegionSet = true;
520 // Start Svg Selection mode
521 graph.StartHotRegion( point );
523 // Draw the Column rectangle
524 DrawColumn2D(graph, vAxis, rectSize, point, ser);
526 // End Svg Selection mode
527 graph.EndHotRegion( );
529 // Reset Clip Region
530 if(clipRegionSet)
532 graph.ResetClip();
535 else if(this.useTwoValues)
537 // Draw labels and markers
538 DrawLabel(
539 area,
540 graph,
541 common,
542 rectSize,
543 point,
544 ser,
545 index);
549 //************************************************************
550 // Hot Regions mode used for image maps, tool tips and
551 // hit test function
552 //************************************************************
553 if( common.ProcessModeRegions && !labels)
555 common.HotRegionsList.AddHotRegion( rectSize, point, ser.Name, index - 1 );
559 // Call Paint event
560 if( !selection )
562 common.Chart.CallOnPostPaint(new ChartPaintEventArgs(ser, graph, common, area.PlotAreaPosition));
565 // Data series index
566 if(currentDrawSeriesSideBySide)
568 seriesIndx++;
571 // Draw labels and markers using the base class algorithm
572 if( labels && !this.useTwoValues)
574 base.ProcessChartType( false, graph, common, area, seriesToDraw );
579 /// <summary>
580 /// Draws 2D column.
581 /// </summary>
582 /// <param name="graph">Chart graphics.</param>
583 /// <param name="vAxis">Vertical axis.</param>
584 /// <param name="rectSize">Column position and size.</param>
585 /// <param name="point">Column data point.</param>
586 /// <param name="ser">Column series.</param>
587 protected virtual void DrawColumn2D(
588 ChartGraphics graph,
589 Axis vAxis,
590 RectangleF rectSize,
591 DataPoint point,
592 Series ser)
594 graph.FillRectangleRel(
595 rectSize,
596 point.Color,
597 point.BackHatchStyle,
598 point.BackImage,
599 point.BackImageWrapMode,
600 point.BackImageTransparentColor,
601 point.BackImageAlignment,
602 point.BackGradientStyle,
603 point.BackSecondaryColor,
604 point.BorderColor,
605 point.BorderWidth,
606 point.BorderDashStyle,
607 ser.ShadowColor,
608 ser.ShadowOffset,
609 PenAlignment.Inset,
610 ChartGraphics.GetBarDrawingStyle(point),
611 true);
614 /// <summary>
615 /// Gets label position for the column depending on the Y value.
616 /// </summary>
617 /// <returns>Return automaticly detected label position.</returns>
618 /// <param name="series">Data series.</param>
619 /// <param name="pointIndex">Point index.</param>
620 /// <returns>Label aligning.</returns>
621 override protected LabelAlignmentStyles GetAutoLabelPosition(Series series, int pointIndex)
623 if( series.Points[pointIndex].YValues[0] >= 0 )
624 return LabelAlignmentStyles.Top;
625 else
626 return LabelAlignmentStyles.Bottom;
629 /// <summary>
630 /// Indicates that markers are drawnd on the X edge of the data scaleView.
631 /// </summary>
632 /// <returns>False. Column chart never draws markers on the edge.</returns>
633 override protected bool ShouldDrawMarkerOnViewEdgeX()
635 return false;
638 #endregion
640 #region 3D painting and selection methods
642 /// <summary>
643 /// This method recalculates size of the columns and paint them or do the hit test in 3d space.
644 /// This method is used from Paint or Select method.
645 /// </summary>
646 /// <param name="labels">Mode which draws only labels and markers.</param>
647 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
648 /// <param name="graph">The Chart Graphics object.</param>
649 /// <param name="common">The Common elements object.</param>
650 /// <param name="area">Chart area for this chart.</param>
651 /// <param name="seriesToDraw">Chart series to draw.</param>
652 private void ProcessChartType3D( bool labels, bool selection, ChartGraphics graph, CommonElements common, ChartArea area, Series seriesToDraw )
654 // Labels & markers are drawn with the data points in the first iteration
655 if(labels && !selection)
657 return;
660 // Get pixel size
661 SizeF pixelRelSize = graph.GetRelativeSize(new SizeF(1.1f, 1.1f));
663 // Get list of series to draw
664 List<string> typeSeries = null;
665 bool currentDrawSeriesSideBySide = this.drawSeriesSideBySide;
666 if( (area.Area3DStyle.IsClustered && this.SideBySideSeries) ||
667 this.Stacked)
669 // Draw all series of the same chart type
670 typeSeries = area.GetSeriesFromChartType(Name);
672 // Check if series should be drawn side by side
673 foreach(string seriesName in typeSeries)
675 if(common.DataManager.Series[seriesName].IsCustomPropertySet(CustomPropertyName.DrawSideBySide))
677 string attribValue = common.DataManager.Series[seriesName][CustomPropertyName.DrawSideBySide];
678 if(String.Compare( attribValue, "False", StringComparison.OrdinalIgnoreCase)==0)
680 currentDrawSeriesSideBySide = false;
682 else if(String.Compare( attribValue, "True", StringComparison.OrdinalIgnoreCase)==0)
684 currentDrawSeriesSideBySide = true;
686 else if(String.Compare( attribValue, "Auto", StringComparison.OrdinalIgnoreCase)==0)
688 // Do nothing
690 else
692 throw (new InvalidOperationException(SR.ExceptionAttributeDrawSideBySideInvalid));
697 else
699 // Draw just one chart series
700 typeSeries = new List<string>();
701 typeSeries.Add(seriesToDraw.Name);
704 //************************************************************
705 //** Get order of data points drawing
706 //************************************************************
707 ArrayList dataPointDrawingOrder = area.GetDataPointDrawingOrder(typeSeries, this, selection, coordinates, null, this.YValueIndex, currentDrawSeriesSideBySide);
709 //************************************************************
710 //** Loop through all data poins
711 //************************************************************
712 foreach(object obj in dataPointDrawingOrder)
714 // Get point & series
715 DataPoint3D pointEx = (DataPoint3D) obj;
716 DataPoint point = pointEx.dataPoint;
717 Series ser = point.series;
719 // Get point bar drawing style
720 BarDrawingStyle barDrawingStyle = ChartGraphics.GetBarDrawingStyle(point);
722 // Set active vertical/horizontal axis
723 Axis vAxis = area.GetAxis(AxisName.Y, ser.YAxisType, ser.YSubAxisName);
724 Axis hAxis = area.GetAxis(AxisName.X, ser.XAxisType, ser.XSubAxisName);
726 // Change Y value if Column is out of plot area
727 float topDarkening = 0f;
728 float bottomDarkening = 0f;
729 double yValue = GetYValue(common, area, ser, pointEx.dataPoint, pointEx.index - 1, (useTwoValues) ? 1 : 0);
730 yValue = vAxis.GetLogValue(yValue);
731 if( yValue > vAxis.ViewMaximum )
733 topDarkening = 0.5f;
734 yValue = vAxis.ViewMaximum;
736 if( yValue < vAxis.ViewMinimum )
738 topDarkening = 0.5f;
739 yValue = vAxis.ViewMinimum;
742 // Recalculates Height position and zero position of Columns
743 double height = vAxis.GetLinearPosition( yValue );
745 // Set start position for a column
746 double columnStartPosition = 0;
747 if(useTwoValues)
749 // Point Y value (first) is used to determine the column starting position
750 double yValueStart = vAxis.GetLogValue( GetYValue(common, area, ser, point, pointEx.index - 1, 0 ) );
751 if( yValueStart > vAxis.ViewMaximum )
753 bottomDarkening = 0.5f;
754 yValueStart = vAxis.ViewMaximum;
756 else if( yValueStart < vAxis.ViewMinimum )
758 bottomDarkening = 0.5f;
759 yValueStart = vAxis.ViewMinimum;
762 columnStartPosition = vAxis.GetLinearPosition(yValueStart);
764 else
766 // Column starts on the horizontal axis crossing
767 columnStartPosition = vAxis.GetPosition(vAxis.Crossing);
770 // Labels and markers have to be shifted if there
771 // is more than one series for column chart.
772 if(!currentDrawSeriesSideBySide)
774 pointEx.xPosition = pointEx.xCenterVal;
776 ShiftedX = pointEx.xPosition - pointEx.xCenterVal;
778 // Make sure that points with small values are still visible
779 if( height < columnStartPosition &&
780 (columnStartPosition - height) < pixelRelSize.Height)
782 height = columnStartPosition - pixelRelSize.Height;
784 if( height > columnStartPosition &&
785 (height - columnStartPosition) < pixelRelSize.Height)
787 height = columnStartPosition + pixelRelSize.Height;
790 // Get column rectangle
791 RectangleF rectSize = RectangleF.Empty;
794 // Set the Column rectangle
795 rectSize.X = (float)(pointEx.xPosition - pointEx.width/2);
796 rectSize.Width = (float)(pointEx.width);
798 // The top side of rectangle has always
799 // smaller value than a bottom value
800 if( columnStartPosition < height )
802 float temp = bottomDarkening;
803 bottomDarkening = topDarkening;
804 topDarkening = temp;
806 rectSize.Y = (float)columnStartPosition;
807 rectSize.Height = (float)height - rectSize.Y;
809 else
811 rectSize.Y = (float)height;
812 rectSize.Height = (float)columnStartPosition - rectSize.Y;
815 catch(OverflowException)
817 continue;
820 //************************************************************
821 //** Painting mode
822 //************************************************************
823 // Path projection of 3D rect.
824 GraphicsPath rectPath = null;
826 // Check if column is completly out of the data scaleView
827 double xValue = (pointEx.indexedSeries) ? pointEx.index : point.XValue;
828 xValue = hAxis.GetLogValue(xValue);
829 if(xValue < hAxis.ViewMinimum || xValue > hAxis.ViewMaximum )
831 continue;
834 // Check if column is partialy in the data scaleView
835 bool clipRegionSet = false;
836 if(rectSize.Right <= area.PlotAreaPosition.X || rectSize.X >= area.PlotAreaPosition.Right)
838 continue;
841 if(rectSize.X < area.PlotAreaPosition.X)
843 rectSize.Width -= area.PlotAreaPosition.X - rectSize.X;
844 rectSize.X = area.PlotAreaPosition.X;
846 if(rectSize.Right > area.PlotAreaPosition.Right)
848 rectSize.Width -= rectSize.Right - area.PlotAreaPosition.Right;
850 if(rectSize.Width < 0)
852 rectSize.Width = 0;
855 // Detect if we need to get graphical path of drawn object
856 DrawingOperationTypes drawingOperationType = DrawingOperationTypes.DrawElement;
858 if( common.ProcessModeRegions )
860 drawingOperationType |= DrawingOperationTypes.CalcElementPath;
863 if(!point.IsEmpty &&
864 rectSize.Height > 0f &&
865 rectSize.Width > 0f)
867 // Start Svg Selection mode
868 graph.StartHotRegion( point );
870 rectPath = graph.Fill3DRectangle(
871 rectSize,
872 pointEx.zPosition,
873 pointEx.depth,
874 area.matrix3D,
875 area.Area3DStyle.LightStyle,
876 point.Color,
877 topDarkening,
878 bottomDarkening,
879 point.BorderColor,
880 point.BorderWidth,
881 point.BorderDashStyle,
882 barDrawingStyle,
883 true,
884 drawingOperationType);
886 // End Svg Selection mode
887 graph.EndHotRegion( );
889 //************************************************************
890 // Hot Regions mode used for image maps, tool tips and
891 // hit test function
892 //************************************************************
893 if( common.ProcessModeRegions && !labels)
895 common.HotRegionsList.AddHotRegion(
896 rectPath,
897 false,
898 graph,
899 point,
900 ser.Name,
901 pointEx.index - 1
904 if (rectPath != null)
906 rectPath.Dispose();
910 // Reset Clip Region
911 if(clipRegionSet)
913 graph.ResetClip();
916 // Draw Labels & markers for each data point
917 this.ProcessSinglePoint3D(
918 pointEx,
919 selection,
920 graph,
921 common,
922 area,
923 rectSize,
924 pointEx.index - 1
928 // Finish processing 3D labels
929 this.DrawAccumulated3DLabels(graph, common, area);
932 #endregion
934 #region 2D and 3D Labels Drawing
936 /// <summary>
937 /// This method draws label.
938 /// </summary>
939 /// <param name="graph">The Chart Graphics object</param>
940 /// <param name="common">The Common elements object</param>
941 /// <param name="area">Chart area for this chart</param>
942 /// <param name="columnPosition">Column position</param>
943 /// <param name="point">Data point</param>
944 /// <param name="ser">Data series</param>
945 /// <param name="pointIndex">Data point index.</param>
946 protected virtual void DrawLabel(
947 ChartArea area,
948 ChartGraphics graph,
949 CommonElements common,
950 RectangleF columnPosition,
951 DataPoint point,
952 Series ser,
953 int pointIndex)
955 // Labels drawing functionality is inhereted from the PointChart class.
958 /// <summary>
959 /// Draws\Hit tests single 3D point.
960 /// </summary>
961 /// <param name="pointEx">3D point information.</param>
962 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
963 /// <param name="graph">The Chart Graphics object.</param>
964 /// <param name="common">The Common elements object.</param>
965 /// <param name="area">Chart area for this chart.</param>
966 /// <param name="columnPosition">Column position</param>
967 /// <param name="pointIndex">Point index.</param>
968 protected virtual void ProcessSinglePoint3D(
969 DataPoint3D pointEx,
970 bool selection,
971 ChartGraphics graph,
972 CommonElements common,
973 ChartArea area,
974 RectangleF columnPosition,
975 int pointIndex
978 // Draw Labels & markers for each data point
979 base.ProcessSinglePoint3D(
980 pointEx,
981 graph,
982 common,
983 area
987 #endregion
990 /// <summary>
991 /// ColumnChart class contains all the code necessary to draw
992 /// both Column and RangeColumn charts. The RangeColumnChart class
993 /// is used to override few default settings, so that 2 Y values
994 /// will be used to define top and bottom position of each column.
995 /// </summary>
996 internal class RangeColumnChart : ColumnChart
998 #region Constructor
1000 /// <summary>
1001 /// Public constructor
1002 /// </summary>
1003 public RangeColumnChart()
1005 // Set the flag to use two Y values, while drawing the columns
1006 this.useTwoValues = true;
1008 // Coordinates of COP used when sorting 3D points order
1009 this.coordinates = COPCoordinates.X | COPCoordinates.Y;
1011 // Index of the main Y value
1012 this.YValueIndex = 1;
1015 #endregion
1017 #region IChartType interface implementation
1019 /// <summary>
1020 /// Chart type name
1021 /// </summary>
1022 override public string Name { get{ return ChartTypeNames.RangeColumn;}}
1024 /// <summary>
1025 /// If the crossing value is auto Crossing value should be
1026 /// automatically set to zero for some chart
1027 /// types (Bar, column, area etc.)
1028 /// </summary>
1029 override public bool ZeroCrossing { get{ return true;} }
1031 /// <summary>
1032 /// Number of supported Y value(s) per point
1033 /// </summary>
1034 override public int YValuesPerPoint{ get { return 2; } }
1036 /// <summary>
1037 /// Indicates that extra Y values are connected to the scale of the Y axis
1038 /// </summary>
1039 override public bool ExtraYValuesConnectedToYAxis{ get { return true; } }
1041 #endregion
1043 #region Y values related methods
1045 /// <summary>
1046 /// Helper function, which returns the Y value of the point.
1047 /// </summary>
1048 /// <param name="common">Chart common elements.</param>
1049 /// <param name="area">Chart area the series belongs to.</param>
1050 /// <param name="series">Sereis of the point.</param>
1051 /// <param name="point">Point object.</param>
1052 /// <param name="pointIndex">Index of the point.</param>
1053 /// <param name="yValueIndex">Index of the Y value to get.</param>
1054 /// <returns>Y value of the point.</returns>
1055 override public double GetYValue(
1056 CommonElements common,
1057 ChartArea area,
1058 Series series,
1059 DataPoint point,
1060 int pointIndex,
1061 int yValueIndex)
1063 // Calculate column height
1064 if(yValueIndex == -1)
1066 return -(base.GetYValue(common, area, series, point, pointIndex, 1) -
1067 base.GetYValue(common, area, series, point, pointIndex, 0));
1070 return base.GetYValue(common, area, series, point, pointIndex, yValueIndex);
1073 #endregion
1075 #region 2D and 3D Labels Drawing
1077 /// <summary>
1078 /// This method draws label.
1079 /// </summary>
1080 /// <param name="graph">The Chart Graphics object</param>
1081 /// <param name="common">The Common elements object</param>
1082 /// <param name="area">Chart area for this chart</param>
1083 /// <param name="columnPosition">Column position</param>
1084 /// <param name="point">Data point</param>
1085 /// <param name="series">Data series</param>
1086 /// <param name="pointIndex">Data point index.</param>
1087 protected override void DrawLabel(
1088 ChartArea area,
1089 ChartGraphics graph,
1090 CommonElements common,
1091 RectangleF columnPosition,
1092 DataPoint point,
1093 Series series,
1094 int pointIndex)
1096 //************************************************************
1097 //** Get marker position and size
1098 //************************************************************
1100 // Get intersection between column rectangle and plotting area rectangle
1101 RectangleF intersection = RectangleF.Intersect(
1102 columnPosition, area.PlotAreaPosition.ToRectangleF() );
1104 // If intersection is empty no drawing required
1105 if(intersection.Height <= 0f || intersection.Width <= 0f)
1107 return;
1110 // Get marker position
1111 PointF markerPosition = PointF.Empty;
1112 markerPosition.X = intersection.X + intersection.Width / 2f;
1113 markerPosition.Y = intersection.Y;
1115 // Remeber pre-calculated point position
1116 point.positionRel = new PointF(markerPosition.X, markerPosition.Y);
1118 // Get point some point properties and save them in variables
1119 int pointMarkerSize = point.MarkerSize;
1120 string pointMarkerImage = point.MarkerImage;
1121 MarkerStyle pointMarkerStyle = point.MarkerStyle;
1123 // Get marker size
1124 SizeF markerSize = base.GetMarkerSize(
1125 graph,
1126 common,
1127 area,
1128 point,
1129 pointMarkerSize,
1130 pointMarkerImage);
1132 //************************************************************
1133 //** Draw point chart
1134 //************************************************************
1135 if(pointMarkerStyle != MarkerStyle.None ||
1136 pointMarkerImage.Length > 0)
1138 // Start Svg Selection mode
1139 graph.StartHotRegion( point );
1141 // Draw the marker
1142 graph.DrawMarkerRel(markerPosition,
1143 (pointMarkerStyle == MarkerStyle.None) ? MarkerStyle.Circle : pointMarkerStyle,
1144 (int)markerSize.Height,
1145 (point.MarkerColor == Color.Empty) ? point.Color : point.MarkerColor,
1146 (point.MarkerBorderColor == Color.Empty) ? point.BorderColor : point.MarkerBorderColor,
1147 GetMarkerBorderSize(point),
1148 pointMarkerImage,
1149 point.MarkerImageTransparentColor,
1150 (point.series != null) ? point.series.ShadowOffset : 0,
1151 (point.series != null) ? point.series.ShadowColor : Color.Empty,
1152 new RectangleF(markerPosition.X, markerPosition.Y, markerSize.Width, markerSize.Height));
1154 // End Svg Selection mode
1155 graph.EndHotRegion( );
1157 //************************************************************
1158 // Hot Regions mode used for image maps, tool tips and
1159 // hit test function
1160 //************************************************************
1161 if( common.ProcessModeRegions )
1163 // Get relative marker size
1164 SizeF relativeMarkerSize = graph.GetRelativeSize(markerSize);
1166 // Insert area just after the last custom area
1167 int insertIndex = common.HotRegionsList.FindInsertIndex();
1169 // Insert circle area
1170 if(pointMarkerStyle == MarkerStyle.Circle)
1172 float[] circCoord = new float[3];
1173 circCoord[0] = markerPosition.X;
1174 circCoord[1] = markerPosition.Y;
1175 circCoord[2] = relativeMarkerSize.Width/2f;
1177 common.HotRegionsList.AddHotRegion(
1178 insertIndex,
1179 graph,
1180 circCoord[0],
1181 circCoord[1],
1182 circCoord[2],
1183 point,
1184 series.Name,
1185 pointIndex - 1 );
1187 // All other markers represented as rectangles
1188 else
1190 common.HotRegionsList.AddHotRegion(
1191 new RectangleF(markerPosition.X - relativeMarkerSize.Width/2f, markerPosition.Y - relativeMarkerSize.Height/2f, relativeMarkerSize.Width, relativeMarkerSize.Height),
1192 point,
1193 series.Name,
1194 pointIndex - 1 );
1200 //************************************************************
1201 //** Draw LabelStyle
1202 //************************************************************
1204 // Label text format
1205 using (StringFormat format = new StringFormat())
1207 format.Alignment = StringAlignment.Center;
1208 format.LineAlignment = StringAlignment.Center;
1210 // Disable the clip region
1211 Region oldClipRegion = graph.Clip;
1212 graph.Clip = new Region();
1214 if (point.IsValueShownAsLabel || point.Label.Length > 0)
1216 // Get label text
1217 string text;
1218 if (point.Label.Length == 0)
1220 // Round Y values for 100% stacked area
1221 double pointLabelValue = GetYValue(common, area, series, point, pointIndex, 0);
1223 text = ValueConverter.FormatValue(
1224 series.Chart,
1225 point,
1226 point.Tag,
1227 pointLabelValue,
1228 point.LabelFormat,
1229 series.YValueType,
1230 ChartElementType.DataPoint);
1232 else
1234 text = point.ReplaceKeywords(point.Label);
1237 // Calculate label position
1238 PointF labelPosition = PointF.Empty;
1239 labelPosition.X = intersection.X + intersection.Width / 2f;
1240 labelPosition.Y = intersection.Y + intersection.Height / 2f;
1242 // Start Svg Selection mode
1243 graph.StartHotRegion(point, true);
1245 // Get string size
1246 SizeF sizeFont = graph.GetRelativeSize(graph.MeasureString(text, point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
1248 // Get label background position
1249 RectangleF labelBackPosition = RectangleF.Empty;
1250 SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1251 sizeLabel.Width += sizeLabel.Width / text.Length;
1252 sizeLabel.Height += sizeFont.Height / 8;
1253 labelBackPosition = GetLabelPosition(
1254 graph,
1255 labelPosition,
1256 sizeLabel,
1257 format,
1258 true);
1260 // Draw label text
1261 using (Brush brush = new SolidBrush(point.LabelForeColor))
1263 graph.DrawPointLabelStringRel(
1264 common,
1265 text,
1266 point.Font,
1267 brush,
1268 labelPosition,
1269 format,
1270 point.LabelAngle,
1271 labelBackPosition,
1272 point.LabelBackColor,
1273 point.LabelBorderColor,
1274 point.LabelBorderWidth,
1275 point.LabelBorderDashStyle,
1276 series,
1277 point,
1278 pointIndex - 1);
1281 // End Svg Selection mode
1282 graph.EndHotRegion();
1285 // Restore old clip region
1286 graph.Clip = oldClipRegion;
1290 /// <summary>
1291 /// Draws\Hit tests single 3D point.
1292 /// </summary>
1293 /// <param name="pointEx">3D point information.</param>
1294 /// <param name="selection">If True selection mode is active, otherwise paint mode is active.</param>
1295 /// <param name="graph">The Chart Graphics object.</param>
1296 /// <param name="common">The Common elements object.</param>
1297 /// <param name="area">Chart area for this chart.</param>
1298 /// <param name="columnPosition">Column position</param>
1299 /// <param name="pointIndex">Point index.</param>
1300 protected override void ProcessSinglePoint3D(
1301 DataPoint3D pointEx,
1302 bool selection,
1303 ChartGraphics graph,
1304 CommonElements common,
1305 ChartArea area,
1306 RectangleF columnPosition,
1307 int pointIndex
1310 DataPoint point = pointEx.dataPoint;
1312 // Check required Y values number
1313 if(point.YValues.Length < this.YValuesPerPoint)
1315 throw(new InvalidOperationException(SR.ExceptionChartTypeRequiresYValues(this.Name,this.YValuesPerPoint.ToString(CultureInfo.InvariantCulture))));
1318 // Label text format
1319 using (StringFormat format = new StringFormat())
1321 format.Alignment = StringAlignment.Center;
1322 format.LineAlignment = StringAlignment.Center;
1324 // Disable the clip region
1325 Region oldClipRegion = graph.Clip;
1326 graph.Clip = new Region();
1328 if (point.IsValueShownAsLabel || point.Label.Length > 0)
1330 // Get label text
1331 string text;
1332 if (point.Label.Length == 0)
1334 // Get Y value
1335 double pointLabelValue = GetYValue(common, area, pointEx.dataPoint.series, point, pointEx.index - 1, 0);
1336 text = ValueConverter.FormatValue(
1337 pointEx.dataPoint.series.Chart,
1338 point,
1339 point.Tag,
1340 pointLabelValue,
1341 point.LabelFormat,
1342 pointEx.dataPoint.series.YValueType,
1343 ChartElementType.DataPoint);
1345 else
1347 text = point.ReplaceKeywords(point.Label);
1351 // Calculate label position
1352 PointF labelPosition = PointF.Empty;
1353 labelPosition.X = columnPosition.X + columnPosition.Width / 2f;
1354 labelPosition.Y = columnPosition.Y + columnPosition.Height / 2f;
1356 // Transform coordinates
1357 Point3D[] marker3DPosition = new Point3D[1];
1358 marker3DPosition[0] = new Point3D(labelPosition.X, labelPosition.Y, (float)(pointEx.zPosition + pointEx.depth));
1359 area.matrix3D.TransformPoints(marker3DPosition);
1361 labelPosition.X = marker3DPosition[0].X;
1362 labelPosition.Y = marker3DPosition[0].Y;
1364 // Start Svg Selection mode
1365 graph.StartHotRegion(point, true);
1367 // Get string size
1368 SizeF sizeFont = graph.GetRelativeSize(graph.MeasureString(text, point.Font, new SizeF(1000f, 1000f), StringFormat.GenericTypographic));
1370 // Get label background position
1371 RectangleF labelBackPosition = RectangleF.Empty;
1372 SizeF sizeLabel = new SizeF(sizeFont.Width, sizeFont.Height);
1373 sizeLabel.Width += sizeLabel.Width / text.Length;
1374 sizeLabel.Height += sizeFont.Height / 8;
1375 labelBackPosition = GetLabelPosition(
1376 graph,
1377 labelPosition,
1378 sizeLabel,
1379 format,
1380 true);
1382 // Draw label text
1383 using (Brush brush = new SolidBrush(point.LabelForeColor))
1385 graph.DrawPointLabelStringRel(
1386 common,
1387 text,
1388 point.Font,
1389 brush,
1390 labelPosition,
1391 format,
1392 point.LabelAngle,
1393 labelBackPosition,
1394 point.LabelBackColor,
1395 point.LabelBorderColor,
1396 point.LabelBorderWidth,
1397 point.LabelBorderDashStyle,
1398 point.series,
1399 point,
1400 pointIndex);
1403 // End Svg Selection mode
1404 graph.EndHotRegion();
1407 // Restore old clip region
1408 graph.Clip = oldClipRegion;
1412 #endregion