12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- //
- // Purpose: Smart Labels are used to avoid data point's labels
- // overlapping. SmartLabelStyle class is exposed from
- // the Series and Annotation classes and allows enabling
- // and adjusting of SmartLabelStyle algorithm. SmartLabelStyle class
- // exposes a set of helper utility methods and store
- // information about labels in a chart area.
- //
- using System;
- using System.Collections;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Design;
- using System.Drawing.Drawing2D;
- using FastReport.DataVisualization.Charting.ChartTypes;
- namespace FastReport.DataVisualization.Charting
- {
- #region Enumerations
- /// <summary>
- /// Line anchor cap style.
- /// </summary>
- [
- SRDescription("DescriptionAttributeLineAnchorCapStyle_LineAnchorCapStyle")
- ]
- public enum LineAnchorCapStyle
- {
- /// <summary>
- /// No line anchor cap.
- /// </summary>
- None,
- /// <summary>
- /// Arrow line anchor cap.
- /// </summary>
- Arrow,
- /// <summary>
- /// Diamond line anchor cap.
- /// </summary>
- Diamond,
- /// <summary>
- /// Square line anchor cap.
- /// </summary>
- Square,
- /// <summary>
- /// Round line anchor cap.
- /// </summary>
- Round
- }
- /// <summary>
- /// Data point label callout style.
- /// </summary>
- [
- SRDescription("DescriptionAttributeLabelCalloutStyle_LabelCalloutStyle")
- ]
- public enum LabelCalloutStyle
- {
- /// <summary>
- /// Label connected with the marker using just a line.
- /// </summary>
- None,
- /// <summary>
- /// Label is undelined and connected with the marker using a line.
- /// </summary>
- Underlined,
- /// <summary>
- /// Box is drawn around the label and it's connected with the marker using a line.
- /// </summary>
- Box
- }
- /// <summary>
- /// Data point label outside of the plotting area style.
- /// </summary>
- [
- SRDescription("DescriptionAttributeLabelOutsidePlotAreaStyle_LabelOutsidePlotAreaStyle")
- ]
- public enum LabelOutsidePlotAreaStyle
- {
- /// <summary>
- /// Labels can be positioned outside of the plotting area.
- /// </summary>
- Yes,
- /// <summary>
- /// Labels can not be positioned outside of the plotting area.
- /// </summary>
- No,
- /// <summary>
- /// Labels can be partially outside of the plotting area.
- /// </summary>
- Partial
- }
- #endregion
- /// <summary>
- /// SmartLabelStyle class is used to enable and configure the
- /// SmartLabelStyle algorithm for data point labels and annotations.
- /// In most of the cases it is enough just to enable the algorithm,
- /// but this class also contains properties which allow controlling
- /// how the labels are moved around to avoid collisions. Visual
- /// appearance of callouts can also be set through this class.
- /// </summary>
- [
- DefaultProperty("Enabled"),
- SRDescription("DescriptionAttributeSmartLabelsStyle_SmartLabelsStyle"),
- TypeConverter(typeof(NoNameExpandableObjectConverter))
- ]
- public class SmartLabelStyle
- {
- #region Fields
- // Reference to the series this style belongs to
- internal object chartElement = null;
- // Indicates if SmartLabelStyle algorithm is enabled.
- private bool _enabled = true;
- // Indicates that marker overlapping by label is allowed.
- private bool _isMarkerOverlappingAllowed = false;
- // Indicates that overlapped labels that can't be repositioned will be hidden.
- private bool _isOverlappedHidden = true;
- // Possible moving directions for the overlapped SmartLabelStyle.
- private LabelAlignmentStyles _movingDirection = LabelAlignmentStyles.Top | LabelAlignmentStyles.Bottom | LabelAlignmentStyles.Right | LabelAlignmentStyles.Left | LabelAlignmentStyles.TopLeft | LabelAlignmentStyles.TopRight | LabelAlignmentStyles.BottomLeft | LabelAlignmentStyles.BottomRight;
- // Minimum distance the overlapped SmartLabelStyle can be moved from the marker. Distance is measured in pixels.
- private double _minMovingDistance = 0.0;
- // Maximum distance the overlapped SmartLabelStyle can be moved from the marker. Distance is measured in pixels.
- private double _maxMovingDistance = 30.0;
- // Defines if SmartLabelStyle are allowed to be drawn outside of the plotting area.
- private LabelOutsidePlotAreaStyle _allowOutsidePlotArea = LabelOutsidePlotAreaStyle.Partial;
- // Callout style of the repositioned SmartLabelStyle.
- private LabelCalloutStyle _calloutStyle = LabelCalloutStyle.Underlined;
- // Label callout line color.
- private Color _calloutLineColor = Color.Black;
- // Label callout line style.
- private ChartDashStyle _calloutLineDashStyle = ChartDashStyle.Solid;
- // Label callout back color. Applies to the Box style only!
- private Color _calloutBackColor = Color.Transparent;
- // Label callout line width.
- private int _calloutLineWidth = 1;
- // Label callout line anchor cap.
- private LineAnchorCapStyle _calloutLineAnchorCapStyle = LineAnchorCapStyle.Arrow;
- #endregion
- #region Constructors and initialization
- /// <summary>
- /// Default public constructor.
- /// </summary>
- public SmartLabelStyle()
- {
- this.chartElement = null;
- }
- /// <summary>
- /// Constructor.
- /// </summary>
- /// <param name="chartElement">Chart element this style belongs to.</param>
- internal SmartLabelStyle(Object chartElement)
- {
- this.chartElement = chartElement;
- }
- #endregion
- #region Properties
- /// <summary>
- /// SmartLabelStyle algorithm enabled flag.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(true),
- SRDescription("DescriptionAttributeEnabled13"),
- ParenthesizePropertyNameAttribute(true),
- ]
- virtual public bool Enabled
- {
- get
- {
- return _enabled;
- }
- set
- {
- _enabled = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Indicates that marker overlapping by label is allowed.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(false),
- SRDescription("DescriptionAttributeMarkerOverlapping"),
- ]
- virtual public bool IsMarkerOverlappingAllowed
- {
- get
- {
- return _isMarkerOverlappingAllowed;
- }
- set
- {
- _isMarkerOverlappingAllowed = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Indicates that overlapped labels that can't be repositioned will be hidden.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(true),
- SRDescription("DescriptionAttributeHideOverlapped"),
- ]
- virtual public bool IsOverlappedHidden
- {
- get
- {
- return _isOverlappedHidden;
- }
- set
- {
- _isOverlappedHidden = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Possible moving directions for the overlapped SmartLabelStyle.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(typeof(LabelAlignmentStyles), "Top, Bottom, Right, Left, TopLeft, TopRight, BottomLeft, BottomRight"),
- SRDescription("DescriptionAttributeMovingDirection"),
- #if DESIGNER
- Editor(typeof(FlagsEnumUITypeEditor), typeof(UITypeEditor))
- #endif
- ]
- virtual public LabelAlignmentStyles MovingDirection
- {
- get
- {
- return _movingDirection;
- }
- set
- {
- if (value == 0)
- {
- throw (new InvalidOperationException(SR.ExceptionSmartLabelsDirectionUndefined));
- }
- _movingDirection = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Minimum distance the overlapped SmartLabelStyle can be moved from the marker. Distance is measured in pixels.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(0.0),
- SRDescription("DescriptionAttributeMinMovingDistance"),
- ]
- virtual public double MinMovingDistance
- {
- get
- {
- return _minMovingDistance;
- }
- set
- {
- if (value < 0)
- {
- throw (new InvalidOperationException(SR.ExceptionSmartLabelsMinMovingDistanceIsNegative));
- }
- _minMovingDistance = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Maximum distance the overlapped SmartLabelStyle can be moved from the marker. Distance is measured in pixels.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(30.0),
- SRDescription("DescriptionAttributeMaxMovingDistance"),
- ]
- virtual public double MaxMovingDistance
- {
- get
- {
- return _maxMovingDistance;
- }
- set
- {
- if (value < 0)
- {
- throw (new InvalidOperationException(SR.ExceptionSmartLabelsMaxMovingDistanceIsNegative));
- }
- _maxMovingDistance = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Defines if SmartLabelStyle are allowed to be drawn outside of the plotting area.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(LabelOutsidePlotAreaStyle.Partial),
- SRDescription("DescriptionAttributeAllowOutsidePlotArea"),
- ]
- virtual public LabelOutsidePlotAreaStyle AllowOutsidePlotArea
- {
- get
- {
- return _allowOutsidePlotArea;
- }
- set
- {
- _allowOutsidePlotArea = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Callout style of the repositioned SmartLabelStyle.
- /// </summary>
- [
- SRCategory("CategoryAttributeMisc"),
- Bindable(true),
- DefaultValue(LabelCalloutStyle.Underlined),
- SRDescription("DescriptionAttributeCalloutStyle3"),
- ]
- virtual public LabelCalloutStyle CalloutStyle
- {
- get
- {
- return _calloutStyle;
- }
- set
- {
- _calloutStyle = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Label callout line color.
- /// </summary>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(typeof(Color), "Black"),
- SRDescription("DescriptionAttributeCalloutLineColor"),
- TypeConverter(typeof(ColorConverter)),
- #if DESIGNER
- Editor(typeof(ChartColorEditor), typeof(UITypeEditor))
- #endif
- ]
- virtual public Color CalloutLineColor
- {
- get
- {
- return _calloutLineColor;
- }
- set
- {
- _calloutLineColor = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Label callout line style.
- /// </summary>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(ChartDashStyle.Solid),
- SRDescription("DescriptionAttributeLineDashStyle"),
- ]
- virtual public ChartDashStyle CalloutLineDashStyle
- {
- get
- {
- return _calloutLineDashStyle;
- }
- set
- {
- _calloutLineDashStyle = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Label callout back color. Applies to the Box style only!
- /// </summary>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(typeof(Color), "Transparent"),
- SRDescription("DescriptionAttributeCalloutBackColor"),
- TypeConverter(typeof(ColorConverter)),
- #if DESIGNER
- Editor(typeof(ChartColorEditor), typeof(UITypeEditor))
- #endif
- ]
- virtual public Color CalloutBackColor
- {
- get
- {
- return _calloutBackColor;
- }
- set
- {
- _calloutBackColor = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Label callout line width.
- /// </summary>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(1),
- SRDescription("DescriptionAttributeLineWidth"),
- ]
- virtual public int CalloutLineWidth
- {
- get
- {
- return _calloutLineWidth;
- }
- set
- {
- _calloutLineWidth = value;
- Invalidate();
- }
- }
- /// <summary>
- /// Label callout line anchor cap.
- /// </summary>
- [
- SRCategory("CategoryAttributeAppearance"),
- Bindable(true),
- DefaultValue(LineAnchorCapStyle.Arrow),
- SRDescription(SR.Keys.DescriptionAttributeCalloutLineAnchorCap),
- ]
- virtual public LineAnchorCapStyle CalloutLineAnchorCapStyle
- {
- get
- {
- return _calloutLineAnchorCapStyle;
- }
- set
- {
- _calloutLineAnchorCapStyle = value;
- Invalidate();
- }
- }
- #endregion
- #region Methods
- /// <summary>
- /// Invalidates assosiated chart element.
- /// </summary>
- private void Invalidate()
- {
- if (chartElement != null)
- {
- if (chartElement is Series)
- {
- ((Series)chartElement).Invalidate(false, false);
- }
- else if (chartElement is Annotation)
- {
- ((Annotation)chartElement).Invalidate();
- }
- }
- }
- #endregion
- }
- /// <summary>
- /// SmartLabelStyle class implements the SmartLabelStyle algorithm for the
- /// data series points. It keeps track of all labels drawn and
- /// detects their collisions. When labels collision is detected
- /// the algorithm tries to resolve it by repositioning the labels.
- /// If label can not be repositioned it maybe hidden depending on
- /// the current settings.
- /// </summary>
- [
- SRDescription("DescriptionAttributeSmartLabels_SmartLabels"),
- ]
- internal class SmartLabel
- {
- #region Fields
- // List of all SmartLabelStyle positions in the area
- internal ArrayList smartLabelsPositions = null;
- // Indicates that not a single collision is allowed
- internal bool checkAllCollisions = false;
- // Number of positions in array for the markers
- internal int markersCount = 0;
- #endregion
- #region Constructors and initialization
- /// <summary>
- /// Default public constructor.
- /// </summary>
- public SmartLabel()
- {
- }
- #endregion
- #region Methods
- /// <summary>
- /// Reset SmartLabelStyle object.
- /// </summary>
- internal void Reset()
- {
- // Re-initialize list of labels position
- smartLabelsPositions = new ArrayList();
- }
- /// <summary>
- /// Process single SmartLabelStyle by adjusting it's position in case of collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="labelPosition">Original label position.</param>
- /// <param name="labelSize">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="markerSize">Marker size.</param>
- /// <param name="labelAlignment">Original label alignment.</param>
- /// <returns>Adjusted position of the label.</returns>
- internal PointF AdjustSmartLabelPosition(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF labelPosition,
- SizeF labelSize,
- StringFormat format,
- PointF markerPosition,
- SizeF markerSize,
- LabelAlignmentStyles labelAlignment)
- {
- return AdjustSmartLabelPosition(
- common,
- graph,
- area,
- smartLabelStyle,
- labelPosition,
- labelSize,
- format,
- markerPosition,
- markerSize,
- labelAlignment,
- false);
- }
- /// <summary>
- /// Process single SmartLabelStyle by adjusting it's position in case of collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="labelPosition">Original label position.</param>
- /// <param name="labelSize">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="markerSize">Marker size.</param>
- /// <param name="labelAlignment">Original label alignment.</param>
- /// <param name="checkCalloutLineOverlapping">Indicates that labels overlapping by callout line must be checked.</param>
- /// <returns>Adjusted position of the label.</returns>
- internal PointF AdjustSmartLabelPosition(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF labelPosition,
- SizeF labelSize,
- StringFormat format,
- PointF markerPosition,
- SizeF markerSize,
- LabelAlignmentStyles labelAlignment,
- bool checkCalloutLineOverlapping)
- {
- // Check if SmartLabelStyle are enabled
- if (smartLabelStyle.Enabled)
- {
- bool labelMovedAway = false;
- // Add series markers positions to avoid their overlapping
- bool rememberMarkersCount = (this.smartLabelsPositions.Count == 0);
- AddMarkersPosition(common, area);
- if (rememberMarkersCount)
- {
- this.markersCount = this.smartLabelsPositions.Count;
- }
- // Check label collision
- if (IsSmartLabelCollide(
- common,
- graph,
- area,
- smartLabelStyle,
- labelPosition,
- labelSize,
- markerPosition,
- format,
- labelAlignment,
- checkCalloutLineOverlapping))
- {
- // Try to find a new position for the SmartLabelStyle
- labelMovedAway = FindNewPosition(
- common,
- graph,
- area,
- smartLabelStyle,
- ref labelPosition,
- labelSize,
- format,
- markerPosition,
- ref markerSize,
- ref labelAlignment,
- checkCalloutLineOverlapping);
- // Draw label callout if label was moved away or
- // it's displayed in the corners of the marker
- if (labelMovedAway ||
- (labelAlignment == LabelAlignmentStyles.BottomLeft ||
- labelAlignment == LabelAlignmentStyles.BottomRight ||
- labelAlignment == LabelAlignmentStyles.TopLeft ||
- labelAlignment == LabelAlignmentStyles.TopRight))
- {
- if (!labelPosition.IsEmpty)
- {
- DrawCallout(
- common,
- graph,
- area,
- smartLabelStyle,
- labelPosition,
- labelSize,
- format,
- markerPosition,
- markerSize,
- labelAlignment);
- }
- }
- }
- // Add label position into the list
- AddSmartLabelPosition(graph, labelPosition, labelSize, format);
- }
- // Return label position
- return labelPosition;
- }
- /// <summary>
- /// Process single SmartLabelStyle by adjusting it's position in case of collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="labelPosition">Original label position.</param>
- /// <param name="labelSize">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="markerSize">Marker size.</param>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <param name="checkCalloutLineOverlapping">Indicates that labels overlapping by callout line must be checked.</param>
- /// <returns>True if label was moved away from the marker.</returns>
- private bool FindNewPosition(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- ref PointF labelPosition,
- SizeF labelSize,
- StringFormat format,
- PointF markerPosition,
- ref SizeF markerSize,
- ref LabelAlignmentStyles labelAlignment,
- bool checkCalloutLineOverlapping)
- {
- SizeF newMarkerSize = SizeF.Empty;
- PointF newLabelPosition = PointF.Empty;
- int positionIndex = 0;
- float labelMovement = 0f;
- bool labelMovedAway = false;
- LabelAlignmentStyles[] positions = new LabelAlignmentStyles[] {
- LabelAlignmentStyles.Top,
- LabelAlignmentStyles.Bottom,
- LabelAlignmentStyles.Left,
- LabelAlignmentStyles.Right,
- LabelAlignmentStyles.TopLeft,
- LabelAlignmentStyles.TopRight,
- LabelAlignmentStyles.BottomLeft,
- LabelAlignmentStyles.BottomRight,
- LabelAlignmentStyles.Center
- };
- // Get relative size of single pixel
- SizeF pixelSize = graph.GetRelativeSize(new SizeF(1f, 1f));
- // Try to find a new position for the label
- bool positionFound = false;
- float movingStep = 2f;
- float minMove = (float)Math.Min(smartLabelStyle.MinMovingDistance, smartLabelStyle.MaxMovingDistance);
- float maxMove = (float)Math.Max(smartLabelStyle.MinMovingDistance, smartLabelStyle.MaxMovingDistance);
- for (labelMovement = minMove; !positionFound && labelMovement <= maxMove; labelMovement += movingStep)
- {
- // Move label by increasing marker size by 4 pixels
- newMarkerSize = new SizeF(
- markerSize.Width + labelMovement * (pixelSize.Width * 2f),
- markerSize.Height + labelMovement * (pixelSize.Height * 2f));
- // Loop through different alignment types
- for (positionIndex = 0; positionIndex < positions.Length; positionIndex++)
- {
- // Center label alignment should only be tried once!
- if (positions[positionIndex] == LabelAlignmentStyles.Center && labelMovement != minMove)
- {
- continue;
- }
- // Check if this alignment is valid
- if ((smartLabelStyle.MovingDirection & positions[positionIndex]) == positions[positionIndex])
- {
- // Calculate new position of the label
- newLabelPosition = CalculatePosition(
- positions[positionIndex],
- markerPosition,
- newMarkerSize,
- labelSize,
- ref format);
- // Check new position collision
- if (!IsSmartLabelCollide(
- common,
- null,
- area,
- smartLabelStyle,
- newLabelPosition,
- labelSize,
- markerPosition,
- format,
- positions[positionIndex],
- checkCalloutLineOverlapping))
- {
- positionFound = true;
- labelMovedAway = (labelMovement == 0f) ? false : true;
- break;
- }
- }
- }
- }
- // Set new data if new label position was found
- if (positionFound)
- {
- markerSize = newMarkerSize;
- labelPosition = newLabelPosition;
- labelAlignment = positions[positionIndex];
- }
- // DEBUG code
- #if DEBUG
- if (common.Chart.ShowDebugMarkings)
- {
- RectangleF lp = GetLabelPosition(graph, labelPosition, labelSize, format, false);
- if (positionFound)
- {
- graph.Graphics.DrawRectangle(Pens.Green, Rectangle.Round(graph.GetAbsoluteRectangle(lp)));
- }
- else
- {
- graph.Graphics.DrawRectangle(new Pen(Color.Magenta, 3), Rectangle.Round(graph.GetAbsoluteRectangle(lp)));
- }
- }
- #endif
- // Do not draw overlapped labels that can't be repositioned
- if (!positionFound && smartLabelStyle.IsOverlappedHidden)
- {
- labelPosition = PointF.Empty;
- }
- return (labelMovedAway && positionFound) ? true : false;
- }
- /// <summary>
- /// Process single SmartLabelStyle by adjusting it's position in case of collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="labelPosition">Original label position.</param>
- /// <param name="labelSize">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="markerSize">Marker size.</param>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <returns>Adjusted position of the label.</returns>
- virtual internal void DrawCallout(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF labelPosition,
- SizeF labelSize,
- StringFormat format,
- PointF markerPosition,
- SizeF markerSize,
- LabelAlignmentStyles labelAlignment)
- {
- // Calculate label position rectangle
- RectangleF labelRectAbs = graph.GetAbsoluteRectangle(
- GetLabelPosition(graph, labelPosition, labelSize, format, true));
- // Create callout pen
- Pen calloutPen = new Pen(smartLabelStyle.CalloutLineColor, smartLabelStyle.CalloutLineWidth);
- calloutPen.DashStyle = graph.GetPenStyle(smartLabelStyle.CalloutLineDashStyle);
- // Draw callout frame
- if (smartLabelStyle.CalloutStyle == LabelCalloutStyle.Box)
- {
- // Fill callout box around the label
- if (smartLabelStyle.CalloutBackColor != Color.Transparent)
- {
- using (Brush calloutBrush = new SolidBrush(smartLabelStyle.CalloutBackColor))
- {
- graph.FillRectangle(calloutBrush, labelRectAbs);
- }
- }
- // Draw box border
- graph.DrawRectangle(calloutPen, labelRectAbs.X, labelRectAbs.Y, labelRectAbs.Width, labelRectAbs.Height);
- }
- else if (smartLabelStyle.CalloutStyle == LabelCalloutStyle.Underlined)
- {
- if (labelAlignment == LabelAlignmentStyles.Right)
- {
- // Draw line to the left of label's text
- graph.DrawLine(calloutPen, labelRectAbs.X, labelRectAbs.Top, labelRectAbs.X, labelRectAbs.Bottom);
- }
- else if (labelAlignment == LabelAlignmentStyles.Left)
- {
- // Draw line to the right of label's text
- graph.DrawLine(calloutPen, labelRectAbs.Right, labelRectAbs.Top, labelRectAbs.Right, labelRectAbs.Bottom);
- }
- else if (labelAlignment == LabelAlignmentStyles.Bottom)
- {
- // Draw line on top of the label's text
- graph.DrawLine(calloutPen, labelRectAbs.X, labelRectAbs.Top, labelRectAbs.Right, labelRectAbs.Top);
- }
- else
- {
- // Draw line under the label's text
- graph.DrawLine(calloutPen, labelRectAbs.X, labelRectAbs.Bottom, labelRectAbs.Right, labelRectAbs.Bottom);
- }
- }
- // Calculate connector line point on the label
- PointF connectorPosition = graph.GetAbsolutePoint(labelPosition);
- if (labelAlignment == LabelAlignmentStyles.Top)
- {
- connectorPosition.Y = labelRectAbs.Bottom;
- }
- else if (labelAlignment == LabelAlignmentStyles.Bottom)
- {
- connectorPosition.Y = labelRectAbs.Top;
- }
- if (smartLabelStyle.CalloutStyle == LabelCalloutStyle.Underlined)
- {
- if (labelAlignment == LabelAlignmentStyles.TopLeft ||
- labelAlignment == LabelAlignmentStyles.TopRight ||
- labelAlignment == LabelAlignmentStyles.BottomLeft ||
- labelAlignment == LabelAlignmentStyles.BottomRight)
- {
- connectorPosition.Y = labelRectAbs.Bottom;
- }
- }
- // Apply anchor cap settings
- if (smartLabelStyle.CalloutLineAnchorCapStyle == LineAnchorCapStyle.Arrow)
- {
- //calloutPen.StartCap = LineCap.ArrowAnchor;
- calloutPen.StartCap = LineCap.Custom;
- calloutPen.CustomStartCap = new AdjustableArrowCap(calloutPen.Width + 2, calloutPen.Width + 3, true);
- }
- else if (smartLabelStyle.CalloutLineAnchorCapStyle == LineAnchorCapStyle.Diamond)
- {
- calloutPen.StartCap = LineCap.DiamondAnchor;
- }
- else if (smartLabelStyle.CalloutLineAnchorCapStyle == LineAnchorCapStyle.Round)
- {
- calloutPen.StartCap = LineCap.RoundAnchor;
- }
- else if (smartLabelStyle.CalloutLineAnchorCapStyle == LineAnchorCapStyle.Square)
- {
- calloutPen.StartCap = LineCap.SquareAnchor;
- }
- // Draw connection line between marker position and label
- PointF markerPositionAbs = graph.GetAbsolutePoint(markerPosition);
- graph.DrawLine(
- calloutPen,
- markerPositionAbs.X,
- markerPositionAbs.Y,
- connectorPosition.X,
- connectorPosition.Y);
- }
- /// <summary>
- /// Checks SmartLabelStyle collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="position">Original label position.</param>
- /// <param name="size">Label text size.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <param name="checkCalloutLineOverlapping">Indicates that labels overlapping by callout line must be checked.</param>
- /// <returns>True if label collides.</returns>
- virtual internal bool IsSmartLabelCollide(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF position,
- SizeF size,
- PointF markerPosition,
- StringFormat format,
- LabelAlignmentStyles labelAlignment,
- bool checkCalloutLineOverlapping)
- {
- bool collisionDetected = false;
- // Calculate label position rectangle
- RectangleF labelPosition = GetLabelPosition(graph, position, size, format, false);
- // Check if label goes outside of the chart picture
- if (labelPosition.X < 0f || labelPosition.Y < 0f ||
- labelPosition.Bottom > 100f || labelPosition.Right > 100f)
- {
- #if DEBUG
- // DEBUG: Mark collided labels
- if (graph != null && common != null && common.Chart != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Cyan, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- }
- // Check if label is drawn outside of plotting area (collides with axis?).
- if (!collisionDetected && area != null)
- {
- if (area.chartAreaIsCurcular)
- {
- using (GraphicsPath areaPath = new GraphicsPath())
- {
- // Add circular shape of the area into the graphics path
- areaPath.AddEllipse(area.PlotAreaPosition.ToRectangleF());
- if (smartLabelStyle.AllowOutsidePlotArea == LabelOutsidePlotAreaStyle.Partial)
- {
- PointF centerPos = new PointF(
- labelPosition.X + labelPosition.Width / 2f,
- labelPosition.Y + labelPosition.Height / 2f);
- if (!areaPath.IsVisible(centerPos))
- {
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Cyan, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- }
- }
- else if (smartLabelStyle.AllowOutsidePlotArea == LabelOutsidePlotAreaStyle.No)
- {
- if (!areaPath.IsVisible(labelPosition.Location) ||
- !areaPath.IsVisible(new PointF(labelPosition.Right, labelPosition.Y)) ||
- !areaPath.IsVisible(new PointF(labelPosition.Right, labelPosition.Bottom)) ||
- !areaPath.IsVisible(new PointF(labelPosition.X, labelPosition.Bottom)))
- {
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Cyan, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- }
- }
- }
- }
- else
- {
- if (smartLabelStyle.AllowOutsidePlotArea == LabelOutsidePlotAreaStyle.Partial)
- {
- PointF centerPos = new PointF(
- labelPosition.X + labelPosition.Width / 2f,
- labelPosition.Y + labelPosition.Height / 2f);
- if (!area.PlotAreaPosition.ToRectangleF().Contains(centerPos))
- {
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Cyan, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- }
- }
- else if (smartLabelStyle.AllowOutsidePlotArea == LabelOutsidePlotAreaStyle.No)
- {
- if (!area.PlotAreaPosition.ToRectangleF().Contains(labelPosition))
- {
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Cyan, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- }
- }
- }
- }
- // Check if 1 collisuion is aceptable in case of cennter alignment
- bool allowOneCollision =
- (labelAlignment == LabelAlignmentStyles.Center && !smartLabelStyle.IsMarkerOverlappingAllowed) ? true : false;
- if (this.checkAllCollisions)
- {
- allowOneCollision = false;
- }
- // Loop through all smart label positions
- if (!collisionDetected && this.smartLabelsPositions != null)
- {
- int index = -1;
- foreach (RectangleF pos in this.smartLabelsPositions)
- {
- // Increase index
- ++index;
- // Check if label collide with other labels or markers.
- bool collision = pos.IntersectsWith(labelPosition);
- // Check if label callout line collide with other labels or markers.
- // Line may overlap markers!
- if (!collision &&
- checkCalloutLineOverlapping &&
- index >= markersCount)
- {
- PointF labelCenter = new PointF(
- labelPosition.X + labelPosition.Width / 2f,
- labelPosition.Y + labelPosition.Height / 2f);
- if (LineIntersectRectangle(pos, markerPosition, labelCenter))
- {
- collision = true;
- }
- }
- // Collision detected
- if (collision)
- {
- // Check if 1 collision allowed
- if (allowOneCollision)
- {
- allowOneCollision = false;
- continue;
- }
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null &&
- common.ChartPicture != null &&
- common.ChartPicture.ChartGraph != null &&
- common.Chart.ShowDebugMarkings)
- {
- common.ChartPicture.ChartGraph.Graphics.DrawRectangle(Pens.Blue, Rectangle.Round(common.ChartPicture.ChartGraph.GetAbsoluteRectangle(pos)));
- common.ChartPicture.ChartGraph.Graphics.DrawRectangle(Pens.Red, Rectangle.Round(common.ChartPicture.ChartGraph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- break;
- }
- }
- }
- return collisionDetected;
- }
- /// <summary>
- /// Checks if rectangle intersected by the line.
- /// </summary>
- /// <param name="rect">Rectangle to be tested.</param>
- /// <param name="point1">First line point.</param>
- /// <param name="point2">Second line point.</param>
- /// <returns>True if line intersects rectangle.</returns>
- private bool LineIntersectRectangle(RectangleF rect, PointF point1, PointF point2)
- {
- // Check for horizontal line
- if (point1.X == point2.X)
- {
- if (point1.X >= rect.X && point1.X <= rect.Right)
- {
- if (point1.Y < rect.Y && point2.Y < rect.Y)
- {
- return false;
- }
- if (point1.Y > rect.Bottom && point2.Y > rect.Bottom)
- {
- return false;
- }
- return true;
- }
- return false;
- }
- // Check for vertical line
- if (point1.Y == point2.Y)
- {
- if (point1.Y >= rect.Y && point1.Y <= rect.Bottom)
- {
- if (point1.X < rect.X && point2.X < rect.X)
- {
- return false;
- }
- if (point1.X > rect.Right && point2.X > rect.Right)
- {
- return false;
- }
- return true;
- }
- return false;
- }
- // Check if line completly outside rectangle
- if (point1.X < rect.X && point2.X < rect.X)
- {
- return false;
- }
- else if (point1.X > rect.Right && point2.X > rect.Right)
- {
- return false;
- }
- else if (point1.Y < rect.Y && point2.Y < rect.Y)
- {
- return false;
- }
- else if (point1.Y > rect.Bottom && point2.Y > rect.Bottom)
- {
- return false;
- }
- // Check if one of the points inside rectangle
- if (rect.Contains(point1) ||
- rect.Contains(point2))
- {
- return true;
- }
- // Calculate intersection point of the line with each side of the rectangle
- PointF intersection = CalloutAnnotation.GetIntersectionY(point1, point2, rect.Y);
- if (rect.Contains(intersection))
- {
- return true;
- }
- intersection = CalloutAnnotation.GetIntersectionY(point1, point2, rect.Bottom);
- if (rect.Contains(intersection))
- {
- return true;
- }
- intersection = CalloutAnnotation.GetIntersectionX(point1, point2, rect.X);
- if (rect.Contains(intersection))
- {
- return true;
- }
- intersection = CalloutAnnotation.GetIntersectionX(point1, point2, rect.Right);
- if (rect.Contains(intersection))
- {
- return true;
- }
- return false;
- }
- /// <summary>
- /// Adds positions of the series markers into the list.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="area">Chart area.</param>
- virtual internal void AddMarkersPosition(
- CommonElements common,
- ChartArea area)
- {
- // Proceed only if there is no items in the list yet
- if (this.smartLabelsPositions.Count == 0 && area != null)
- {
- // Get chart types registry
- ChartTypeRegistry registry = common.ChartTypeRegistry;
- // Loop through all the series from this chart area
- foreach (Series series in common.DataManager.Series)
- {
- // Check if marker overapping is enabled for the series
- if (series.ChartArea == area.Name &&
- series.SmartLabelStyle.Enabled &&
- !series.SmartLabelStyle.IsMarkerOverlappingAllowed)
- {
- // Get series chart type
- IChartType chartType = registry.GetChartType(series.ChartTypeName);
- // Add series markers positions into the list
- chartType.AddSmartLabelMarkerPositions(common, area, series, this.smartLabelsPositions);
- }
- }
- // Make sure labels do not intersect with scale breaks
- foreach (Axis currentAxis in area.Axes)
- {
- // Check if scale breaks are defined and there are non zero spacing
- if (currentAxis.ScaleBreakStyle.Spacing > 0.0
- && currentAxis.ScaleSegments.Count > 0)
- {
- for (int index = 0; index < (currentAxis.ScaleSegments.Count - 1); index++)
- {
- // Get break position in pixel coordinates
- RectangleF breakPosition = currentAxis.ScaleSegments[index].GetBreakLinePosition(common.graph, currentAxis.ScaleSegments[index + 1]);
- breakPosition = common.graph.GetRelativeRectangle(breakPosition);
- // Create array list if needed
- if (this.smartLabelsPositions == null)
- {
- this.smartLabelsPositions = new ArrayList();
- }
- // Add label position into the list
- this.smartLabelsPositions.Add(breakPosition);
- }
- }
- }
- }
- }
- /// <summary>
- /// Adds single Smart Label position into the list.
- /// </summary>
- /// <param name="graph">Chart graphics object.</param>
- /// <param name="position">Original label position.</param>
- /// <param name="size">Label text size.</param>
- /// <param name="format">Label string format.</param>
- internal void AddSmartLabelPosition(
- ChartGraphics graph,
- PointF position,
- SizeF size,
- StringFormat format)
- {
- // Calculate label position rectangle
- RectangleF labelPosition = GetLabelPosition(graph, position, size, format, false);
- if (this.smartLabelsPositions == null)
- {
- this.smartLabelsPositions = new ArrayList();
- }
- // Add label position into the list
- this.smartLabelsPositions.Add(labelPosition);
- }
- /// <summary>
- /// Gets rectangle position of the label.
- /// </summary>
- /// <param name="graph">Chart graphics object.</param>
- /// <param name="position">Original label position.</param>
- /// <param name="size">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="adjustForDrawing">Result position is adjusted for drawing.</param>
- /// <returns>Label rectangle position.</returns>
- internal RectangleF GetLabelPosition(
- ChartGraphics graph,
- PointF position,
- SizeF size,
- StringFormat format,
- bool adjustForDrawing)
- {
- // Calculate label position rectangle
- RectangleF labelPosition = RectangleF.Empty;
- labelPosition.Width = size.Width;
- labelPosition.Height = size.Height;
- // Calculate pixel size in relative coordiantes
- SizeF pixelSize = SizeF.Empty;
- if (graph != null)
- {
- pixelSize = graph.GetRelativeSize(new SizeF(1f, 1f));
- }
- if (format.Alignment == StringAlignment.Far)
- {
- labelPosition.X = position.X - size.Width;
- if (adjustForDrawing && !pixelSize.IsEmpty)
- {
- labelPosition.X -= 4f * pixelSize.Width;
- labelPosition.Width += 4f * pixelSize.Width;
- }
- }
- else if (format.Alignment == StringAlignment.Near)
- {
- labelPosition.X = position.X;
- if (adjustForDrawing && !pixelSize.IsEmpty)
- {
- labelPosition.Width += 4f * pixelSize.Width;
- }
- }
- else if (format.Alignment == StringAlignment.Center)
- {
- labelPosition.X = position.X - size.Width / 2F;
- if (adjustForDrawing && !pixelSize.IsEmpty)
- {
- labelPosition.X -= 2f * pixelSize.Width;
- labelPosition.Width += 4f * pixelSize.Width;
- }
- }
- if (format.LineAlignment == StringAlignment.Far)
- {
- labelPosition.Y = position.Y - size.Height;
- }
- else if (format.LineAlignment == StringAlignment.Near)
- {
- labelPosition.Y = position.Y;
- }
- else if (format.LineAlignment == StringAlignment.Center)
- {
- labelPosition.Y = position.Y - size.Height / 2F;
- }
- return labelPosition;
- }
- /// <summary>
- /// Gets point position of the label.
- /// </summary>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="sizeMarker">Marker size.</param>
- /// <param name="sizeFont">Label size.</param>
- /// <param name="format">String format.</param>
- /// <returns>Label point position.</returns>
- private PointF CalculatePosition(
- LabelAlignmentStyles labelAlignment,
- PointF markerPosition,
- SizeF sizeMarker,
- SizeF sizeFont,
- ref StringFormat format)
- {
- format.Alignment = StringAlignment.Near;
- format.LineAlignment = StringAlignment.Center;
- // Calculate label position
- PointF position = new PointF(markerPosition.X, markerPosition.Y);
- switch (labelAlignment)
- {
- case LabelAlignmentStyles.Center:
- format.Alignment = StringAlignment.Center;
- break;
- case LabelAlignmentStyles.Bottom:
- format.Alignment = StringAlignment.Center;
- position.Y += sizeMarker.Height / 1.75F;
- position.Y += sizeFont.Height / 2F;
- break;
- case LabelAlignmentStyles.Top:
- format.Alignment = StringAlignment.Center;
- position.Y -= sizeMarker.Height / 1.75F;
- position.Y -= sizeFont.Height / 2F;
- break;
- case LabelAlignmentStyles.Left:
- format.Alignment = StringAlignment.Far;
- position.X -= sizeMarker.Height / 1.75F;
- break;
- case LabelAlignmentStyles.TopLeft:
- format.Alignment = StringAlignment.Far;
- position.X -= sizeMarker.Height / 1.75F;
- position.Y -= sizeMarker.Height / 1.75F;
- position.Y -= sizeFont.Height / 2F;
- break;
- case LabelAlignmentStyles.BottomLeft:
- format.Alignment = StringAlignment.Far;
- position.X -= sizeMarker.Height / 1.75F;
- position.Y += sizeMarker.Height / 1.75F;
- position.Y += sizeFont.Height / 2F;
- break;
- case LabelAlignmentStyles.Right:
- position.X += sizeMarker.Height / 1.75F;
- break;
- case LabelAlignmentStyles.TopRight:
- position.X += sizeMarker.Height / 1.75F;
- position.Y -= sizeMarker.Height / 1.75F;
- position.Y -= sizeFont.Height / 2F;
- break;
- case LabelAlignmentStyles.BottomRight:
- position.X += sizeMarker.Height / 1.75F;
- position.Y += sizeMarker.Height / 1.75F;
- position.Y += sizeFont.Height / 2F;
- break;
- }
- return position;
- }
- #endregion
- }
- /// <summary>
- /// AnnotationSmartLabel class provides SmartLabelStyle functionality
- /// specific to the annotation objects.
- /// </summary>
- [
- SRDescription("DescriptionAttributeAnnotationSmartLabels_AnnotationSmartLabels"),
- ]
- internal class AnnotationSmartLabel : SmartLabel
- {
- #region Constructors and initialization
- /// <summary>
- /// Default public constructor.
- /// </summary>
- public AnnotationSmartLabel()
- {
- }
- #endregion
- #region Methods
- /// <summary>
- /// Checks SmartLabelStyle collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="position">Original label position.</param>
- /// <param name="size">Label text size.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <param name="checkCalloutLineOverlapping">Indicates that labels overlapping by callout line must be checked.</param>
- /// <returns>True if label collides.</returns>
- override internal bool IsSmartLabelCollide(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF position,
- SizeF size,
- PointF markerPosition,
- StringFormat format,
- LabelAlignmentStyles labelAlignment,
- bool checkCalloutLineOverlapping)
- {
- bool collisionDetected = false;
- //*******************************************************************
- //** Check collision with smatl labels of series in chart area
- //*******************************************************************
- if (area != null && area.Visible)
- {
- area.smartLabels.checkAllCollisions = true;
- if (area.smartLabels.IsSmartLabelCollide(
- common,
- graph,
- area,
- smartLabelStyle,
- position,
- size,
- markerPosition,
- format,
- labelAlignment,
- checkCalloutLineOverlapping))
- {
- area.smartLabels.checkAllCollisions = false;
- return true;
- }
- area.smartLabels.checkAllCollisions = false;
- }
- //*******************************************************************
- //** Check collision with other annotations.
- //*******************************************************************
- // Calculate label position rectangle
- RectangleF labelPosition = GetLabelPosition(graph, position, size, format, false);
- // Check if 1 collisuion is aceptable in case of cennter alignment
- bool allowOneCollision =
- (labelAlignment == LabelAlignmentStyles.Center && !smartLabelStyle.IsMarkerOverlappingAllowed) ? true : false;
- if (this.checkAllCollisions)
- {
- allowOneCollision = false;
- }
- // Check if label collide with other labels or markers.
- foreach (RectangleF pos in this.smartLabelsPositions)
- {
- if (pos.IntersectsWith(labelPosition))
- {
- // Check if 1 collision allowed
- if (allowOneCollision)
- {
- allowOneCollision = false;
- continue;
- }
- // DEBUG: Mark collided labels
- #if DEBUG
- if (graph != null && common.Chart.ShowDebugMarkings)
- {
- graph.Graphics.DrawRectangle(Pens.Blue, Rectangle.Round(graph.GetAbsoluteRectangle(pos)));
- graph.Graphics.DrawRectangle(Pens.Red, Rectangle.Round(graph.GetAbsoluteRectangle(labelPosition)));
- }
- #endif
- collisionDetected = true;
- break;
- }
- }
- return collisionDetected;
- }
- /// <summary>
- /// Adds positions of the series markers into the list.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="area">Chart area.</param>
- override internal void AddMarkersPosition(
- CommonElements common,
- ChartArea area)
- {
- // Proceed only if there is no items in the list yet
- if (this.smartLabelsPositions.Count == 0 &&
- common != null &&
- common.Chart != null)
- {
- // Add annotations anchor points
- foreach (Annotation annotation in common.Chart.Annotations)
- {
- annotation.AddSmartLabelMarkerPositions(this.smartLabelsPositions);
- }
- }
- }
- /// <summary>
- /// Process single SmartLabelStyle by adjusting it's position in case of collision.
- /// </summary>
- /// <param name="common">Reference to common elements.</param>
- /// <param name="graph">Reference to chart graphics object.</param>
- /// <param name="area">Chart area.</param>
- /// <param name="smartLabelStyle">Smart labels style.</param>
- /// <param name="labelPosition">Original label position.</param>
- /// <param name="labelSize">Label text size.</param>
- /// <param name="format">Label string format.</param>
- /// <param name="markerPosition">Marker position.</param>
- /// <param name="markerSize">Marker size.</param>
- /// <param name="labelAlignment">Label alignment.</param>
- /// <returns>Adjusted position of the label.</returns>
- override internal void DrawCallout(
- CommonElements common,
- ChartGraphics graph,
- ChartArea area,
- SmartLabelStyle smartLabelStyle,
- PointF labelPosition,
- SizeF labelSize,
- StringFormat format,
- PointF markerPosition,
- SizeF markerSize,
- LabelAlignmentStyles labelAlignment)
- {
- // No callout is drawn for the annotations
- }
- #endregion
- }
- }
|