Created
August 29, 2018 17:10
-
-
Save anuviswan/1b0f4633a7e01f4e1ac46d83058f8b6b to your computer and use it in GitHub Desktop.
RectangleAnnotation With support for RoundedCorner in OxyPlot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// -------------------------------------------------------------------------------------------------------------------- | |
// <summary> | |
// Represents an annotation that shows a rounded rectangle. Forked from OxyPlot Source. | |
// </summary> | |
// -------------------------------------------------------------------------------------------------------------------- | |
using System; | |
namespace OxyPlot.Annotations | |
{ | |
/// <summary> | |
/// Represents an annotation that shows a rounded rectangle. | |
/// </summary> | |
public class RoundedRectangleAnnotation : ShapeAnnotation | |
{ | |
/// <summary> | |
/// The rectangle transformed to screen coordinates. | |
/// </summary> | |
private OxyRect screenRectangle; | |
private OxyRect screenRectangleWithClippedXAxis; | |
private OxyRect screenRectangleWithClippedYAxis; | |
private OxyRect screenEllipseLeftBottom; | |
private OxyRect screenEllipseLeftTop; | |
private OxyRect screenEllipseRightBottom; | |
private OxyRect screenEllipseRightTop; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="RoundedRectangleAnnotation" /> class. | |
/// </summary> | |
public RoundedRectangleAnnotation() | |
{ | |
this.MinimumX = double.MinValue; | |
this.MaximumX = double.MaxValue; | |
this.MinimumY = double.MinValue; | |
this.MaximumY = double.MaxValue; | |
this.TextRotation = 0; | |
this.ClipByXAxis = true; | |
this.ClipByYAxis = true; | |
this.CornerRadius = 0; | |
} | |
/// <summary> | |
/// Gets or sets the minimum X. | |
/// </summary> | |
/// <value>The minimum X.</value> | |
public double MinimumX { get; set; } | |
/// <summary> | |
/// Gets or sets the maximum X. | |
/// </summary> | |
/// <value>The maximum X.</value> | |
public double MaximumX { get; set; } | |
/// <summary> | |
/// Gets or sets the minimum Y. | |
/// </summary> | |
/// <value>The minimum Y.</value> | |
public double MinimumY { get; set; } | |
/// <summary> | |
/// Gets or sets the maximum Y. | |
/// </summary> | |
/// <value>The maximum Y.</value> | |
public double MaximumY { get; set; } | |
/// <summary> | |
/// Gets or sets the corner radius | |
/// </summary> | |
/// <value>The value of Corner Radius</value> | |
public double CornerRadius { get; set; } | |
/// <summary> | |
/// Gets or sets a value indicating whether to clip the annotation by the X axis range. | |
/// </summary> | |
/// <value><c>true</c> if clipping by the X axis is enabled; otherwise, <c>false</c>.</value> | |
public bool ClipByXAxis { get; set; } | |
/// <summary> | |
/// Gets or sets a value indicating whether to clip the annotation by the Y axis range. | |
/// </summary> | |
/// <value><c>true</c> if clipping by the Y axis is enabled; otherwise, <c>false</c>.</value> | |
public bool ClipByYAxis { get; set; } | |
/// <summary> | |
/// Renders the rectangle annotation. | |
/// </summary> | |
/// <param name="rc">The render context.</param> | |
public override void Render(IRenderContext rc) | |
{ | |
base.Render(rc); | |
if (CornerRadius == 0) | |
DrawRegularRectangle(rc); | |
else | |
DrawRoundedRectangle(rc); | |
} | |
private void DrawRoundedRectangle(IRenderContext rc) | |
{ | |
var xMin = double.IsNaN(this.MinimumX) || this.MinimumX.Equals(double.MinValue) | |
? this.ClipByXAxis | |
? this.XAxis.ActualMinimum | |
: this.XAxis.InverseTransform(this.PlotModel.PlotArea.Left) | |
: this.MinimumX; | |
var xMax = double.IsNaN(this.MaximumX) || this.MaximumX.Equals(double.MaxValue) | |
? this.ClipByXAxis | |
? this.XAxis.ActualMaximum | |
: this.XAxis.InverseTransform(this.PlotModel.PlotArea.Right) | |
: this.MaximumX; | |
var yMin = double.IsNaN(this.MinimumY) || this.MinimumY.Equals(double.MinValue) | |
? this.ClipByYAxis | |
? this.YAxis.ActualMinimum | |
: this.YAxis.InverseTransform(this.PlotModel.PlotArea.Bottom) | |
: this.MinimumY; | |
var yMax = double.IsNaN(this.MaximumY) || this.MaximumY.Equals(double.MaxValue) | |
? this.ClipByYAxis | |
? this.YAxis.ActualMaximum | |
: this.YAxis.InverseTransform(this.PlotModel.PlotArea.Top) | |
: this.MaximumY; | |
var xCornerRadius = (CornerRadius / (XAxis.Maximum - XAxis.Minimum)) * 100; | |
var yCornerRadius = (CornerRadius / (YAxis.Maximum - YAxis.Minimum)) * 100; | |
this.screenRectangleWithClippedXAxis = new OxyRect(this.Transform(xMin + xCornerRadius, yMin), this.Transform(xMax - xCornerRadius, yMax)); | |
this.screenRectangleWithClippedYAxis = new OxyRect(this.Transform(xMin, yMin + yCornerRadius), this.Transform(xMax, yMax - yCornerRadius)); | |
this.screenEllipseLeftBottom = new OxyRect(this.Transform(xMin, yMin), this.Transform(xMin + 2* xCornerRadius, yMin + 2* yCornerRadius)); | |
this.screenEllipseLeftTop = new OxyRect(this.Transform(xMin, yMax), this.Transform(xMin + 2 * xCornerRadius, yMax - 2 * yCornerRadius)); | |
this.screenEllipseRightBottom = new OxyRect(this.Transform(xMax, yMin), this.Transform(xMax - 2 * xCornerRadius, yMin + 2 * yCornerRadius)); | |
this.screenEllipseRightTop = new OxyRect(this.Transform(xMax, yMax), this.Transform(xMax - 2 * xCornerRadius, yMax - 2 * yCornerRadius)); | |
// clip to the area defined by the axes | |
var clippingRectangle = OxyRect.Create( | |
this.ClipByXAxis ? this.XAxis.ScreenMin.X : this.PlotModel.PlotArea.Left, | |
this.ClipByYAxis ? this.YAxis.ScreenMin.Y : this.PlotModel.PlotArea.Top, | |
this.ClipByXAxis ? this.XAxis.ScreenMax.X : this.PlotModel.PlotArea.Right, | |
this.ClipByYAxis ? this.YAxis.ScreenMax.Y : this.PlotModel.PlotArea.Bottom); | |
rc.DrawClippedRectangle(clippingRectangle,this.screenRectangleWithClippedXAxis, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
rc.DrawClippedRectangle(clippingRectangle, this.screenRectangleWithClippedYAxis, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
rc.DrawClippedEllipse(clippingRectangle, screenEllipseLeftBottom, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
rc.DrawClippedEllipse(clippingRectangle, screenEllipseLeftTop, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
rc.DrawClippedEllipse(clippingRectangle, screenEllipseRightBottom, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
rc.DrawClippedEllipse(clippingRectangle, screenEllipseRightTop, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
if (!string.IsNullOrEmpty(this.Text)) | |
{ | |
var textPosition = this.GetActualTextPosition(() => this.screenRectangle.Center); | |
rc.DrawClippedText( | |
clippingRectangle, | |
textPosition, | |
this.Text, | |
this.ActualTextColor, | |
this.ActualFont, | |
this.ActualFontSize, | |
this.ActualFontWeight, | |
this.TextRotation, | |
HorizontalAlignment.Center, | |
VerticalAlignment.Middle); | |
} | |
} | |
private void DrawRegularRectangle(IRenderContext rc) | |
{ | |
double x0 = double.IsNaN(this.MinimumX) || this.MinimumX.Equals(double.MinValue) | |
? this.ClipByXAxis | |
? this.XAxis.ActualMinimum | |
: this.XAxis.InverseTransform(this.PlotModel.PlotArea.Left) | |
: this.MinimumX; | |
double x1 = double.IsNaN(this.MaximumX) || this.MaximumX.Equals(double.MaxValue) | |
? this.ClipByXAxis | |
? this.XAxis.ActualMaximum | |
: this.XAxis.InverseTransform(this.PlotModel.PlotArea.Right) | |
: this.MaximumX; | |
double y0 = double.IsNaN(this.MinimumY) || this.MinimumY.Equals(double.MinValue) | |
? this.ClipByYAxis | |
? this.YAxis.ActualMinimum | |
: this.YAxis.InverseTransform(this.PlotModel.PlotArea.Bottom) | |
: this.MinimumY; | |
double y1 = double.IsNaN(this.MaximumY) || this.MaximumY.Equals(double.MaxValue) | |
? this.ClipByYAxis | |
? this.YAxis.ActualMaximum | |
: this.YAxis.InverseTransform(this.PlotModel.PlotArea.Top) | |
: this.MaximumY; | |
this.screenRectangle = new OxyRect(this.Transform(x0, y0), this.Transform(x1, y1)); | |
// clip to the area defined by the axes | |
var clippingRectangle = OxyRect.Create( | |
this.ClipByXAxis ? this.XAxis.ScreenMin.X : this.PlotModel.PlotArea.Left, | |
this.ClipByYAxis ? this.YAxis.ScreenMin.Y : this.PlotModel.PlotArea.Top, | |
this.ClipByXAxis ? this.XAxis.ScreenMax.X : this.PlotModel.PlotArea.Right, | |
this.ClipByYAxis ? this.YAxis.ScreenMax.Y : this.PlotModel.PlotArea.Bottom); | |
rc.DrawClippedRectangle( | |
clippingRectangle, | |
this.screenRectangle, | |
this.GetSelectableFillColor(this.Fill), | |
this.GetSelectableColor(this.Stroke), | |
this.StrokeThickness); | |
if (!string.IsNullOrEmpty(this.Text)) | |
{ | |
var textPosition = this.GetActualTextPosition(() => this.screenRectangle.Center); | |
rc.DrawClippedText( | |
clippingRectangle, | |
textPosition, | |
this.Text, | |
this.ActualTextColor, | |
this.ActualFont, | |
this.ActualFontSize, | |
this.ActualFontWeight, | |
this.TextRotation, | |
HorizontalAlignment.Center, | |
VerticalAlignment.Middle); | |
} | |
} | |
/// <summary> | |
/// When overridden in a derived class, tests if the plot element is hit by the specified point. | |
/// </summary> | |
/// <param name="args">The hit test arguments.</param> | |
/// <returns> | |
/// The result of the hit test. | |
/// </returns> | |
protected override HitTestResult HitTestOverride(HitTestArguments args) | |
{ | |
if (this.screenRectangle.Contains(args.Point)) | |
{ | |
return new HitTestResult(this, args.Point); | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment