Skip to content
Open
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
## Unreleased

### Added

- Add AxisArea, AxisLineArea and AxisTitleArea property in Axis class (#1949)
### Changed

### Removed
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Carlos Anderson <carlosjanderson@gmail.com>
Carlos Teixeira <karlmtc@gmail.com>
Chase Long <chaselfromal@gmail.com>
Choden Konigsmark <choden.konigsmark@gmail.com>
ChrisCC6 <christian.woempner@gmail.com>
classicboss302
csabar <rumancsabi@gmail.com>
Curt Mullin <curt.mullin@gmail.com>
Expand Down
19 changes: 19 additions & 0 deletions Source/OxyPlot/Axes/Axis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ protected Axis()

this.DataMaximum = double.NaN;
this.DataMinimum = double.NaN;

this.TitleArea = new EmptyShape();
this.AxisArea = new EmptyShape();
this.AxisLineArea = new EmptyShape();
}

/// <summary>
Expand Down Expand Up @@ -634,6 +638,21 @@ public double Scale
/// </summary>
public OxyThickness DesiredMargin { get; protected set; }

/// <summary>
/// The area of the axis in screen coordinates.
/// </summary>
public IOxyRegion AxisArea { get; internal protected set; }

/// <summary>
/// The area of the axis line in screen coordinates.
/// </summary>
public IOxyRegion AxisLineArea { get; internal protected set; }

/// <summary>
/// The area of the axis title in screen coordinates.
/// </summary>
public IOxyRegion TitleArea { get; internal protected set; }

Comment thread
VisualMelon marked this conversation as resolved.
Resolved
/// <summary>
/// Gets or sets the position tier max shift.
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions Source/OxyPlot/Axes/Rendering/AngleAxisRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,18 @@ public override void Render(Axis axis, int pass)

var axisLength = Math.Abs(scaledEndAngle - scaledStartAngle);
var eps = axis.MinorStep * 1e-3;

ScreenPoint? innerTangentPoint = null;
double maxTextLength = double.MinValue;

if (this.MinorPen != null)
{
var tickCount = Math.Abs((int)(axisLength / axis.ActualMinorStep));
var screenPoints = this.MinorTickValues
.Take(tickCount + 1)
.Select(x => magnitudeAxis.Transform(magnitudeAxis.ClipMaximum, x, axis));

innerTangentPoint = screenPoints.FirstOrDefault();
foreach (var screenPoint in screenPoints)
{
this.RenderContext.DrawLine(magnitudeAxis.MidPoint.x, magnitudeAxis.MidPoint.y, screenPoint.x, screenPoint.y, this.MinorPen, axis.EdgeRenderingMode);
Expand All @@ -77,6 +82,8 @@ public override void Render(Axis axis, int pass)
.Take(majorTickCount)
.Select(x => magnitudeAxis.Transform(magnitudeAxis.ClipMaximum, x, axis));

if (innerTangentPoint == null && screenPoints.Count() > 0)
innerTangentPoint = screenPoints.FirstOrDefault();
foreach (var point in screenPoints)
{
this.RenderContext.DrawLine(magnitudeAxis.MidPoint.x, magnitudeAxis.MidPoint.y, point.x, point.y, this.MajorPen, axis.EdgeRenderingMode);
Expand Down Expand Up @@ -112,9 +119,26 @@ public override void Render(Axis axis, int pass)
ha = HorizontalAlignment.Right;
}

maxTextLength = Math.Max(maxTextLength, this.RenderContext.MeasureText(text).Width);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be maxTextLength = Math.Max(maxTextLength, this.RenderContext.MeasureText(text, axis.ActualFont, axis.ActualFontSize, axis.ActualFontWeight).Width);


this.RenderContext.DrawMathText(
pt, text, axis.ActualTextColor, axis.ActualFont, axis.ActualFontSize, axis.ActualFontWeight, angle, ha, va);
}

if (innerTangentPoint != null)
{
double r1 = magnitudeAxis.MidPoint.DistanceTo(innerTangentPoint.Value);
double r2 = r1 + maxTextLength + axis.AxisTickToLabelDistance;
if (r2 < 5)
r2 = r1 + 5;
axis.AxisLineArea = new OxyAnnulus(magnitudeAxis.MidPoint, r1, r2);
axis.AxisArea = new OxyAnnulus(magnitudeAxis.MidPoint, r1, r2);
}
else
{
axis.AxisLineArea = new EmptyShape();
axis.AxisArea = new EmptyShape();
}
}
}
}
78 changes: 76 additions & 2 deletions Source/OxyPlot/Axes/Rendering/HorizontalAndVerticalAxisRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,40 @@ protected virtual void RenderAxisTitle(Axis axis, double titlePosition)

var lpt = this.GetAxisTitlePositionAndAlignment(axis, titlePosition, ref angle, ref halign, ref valign);

var titleSize = this.RenderContext.MeasureText(axis.ActualTitle, axis.ActualTitleFont, axis.TitleFontSize, axis.TitleFontWeight);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be var titleSize = this.RenderContext.MeasureText(axis.ActualTitle, axis.ActualTitleFont, axis.ActualTitleFontSize, axis.TitleFontWeight); (ActualTitleFontSize)


ScreenPoint areaBottomLeft = new ScreenPoint(0, 0);
ScreenPoint areaTopRight = new ScreenPoint(0, 0);

switch (axis.Position)
{
case AxisPosition.Left:
areaTopRight = new ScreenPoint(lpt.X, lpt.Y + titleSize.Width * 0.5);
areaBottomLeft = new ScreenPoint(lpt.X + titleSize.Height, lpt.Y - titleSize.Width * 0.5);
break;
case AxisPosition.Right:
areaTopRight = new ScreenPoint(lpt.X - titleSize.Height, lpt.Y + titleSize.Width * 0.5);
areaBottomLeft = new ScreenPoint(lpt.X, lpt.Y - titleSize.Width * 0.5);
break;
case AxisPosition.Top:
areaTopRight = new ScreenPoint(lpt.X - titleSize.Width * 0.5, lpt.Y + titleSize.Height);
areaBottomLeft = new ScreenPoint(lpt.X + titleSize.Width * 0.5, lpt.Y);
break;
case AxisPosition.Bottom:
areaTopRight = new ScreenPoint(lpt.x - titleSize.Width * 0.5, lpt.Y);
areaBottomLeft = new ScreenPoint(lpt.X + titleSize.Width * 0.5, lpt.Y - titleSize.Height);
break;

}
// TODO: ensure min area of 5px x X to capture clicks, o this above acc. to axis position

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this TODO get resolved?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ChrisCC6 this and comment code below could do with being addressed. If you don't have time to work on this let us know and I'll try to take a look into this and @Jonarw 's questions because I'm keen for this PR to go through.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VisualMelon I added two examples based on @Jonarw's code above and added the AxisArea to be set for the MagnitudeAxisRenderer.
The TODO is resolved as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks indeed! Shall try to take a proper look myself later today.

//if (Math.Abs(this.pt2.X - this.pt3.X) < 5)
// this.pt3.X = this.pt2.X + 5;

axis.TitleArea = new OxyRect(areaBottomLeft, areaTopRight);

if (axis.AxisLineArea is OxyRect axisLineArea)
axis.AxisArea = axisLineArea.Union(new OxyRect(areaBottomLeft, areaTopRight));

this.RenderContext.DrawMathText(
lpt,
axis.ActualTitle,
Expand All @@ -296,6 +330,9 @@ protected virtual void RenderAxisTitle(Axis axis, double titlePosition)
/// <param name="drawAxisLine">Draw the axis line if set to <c>true</c>.</param>
protected virtual void RenderMajorItems(Axis axis, double axisPosition, double titlePosition, bool drawAxisLine)
{
ScreenPoint pt0;
ScreenPoint pt1;
Comment on lines +344 to +345

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move these guys to just above their usage


double eps = axis.ActualMinorStep * 1e-3;

double clipMinimum = axis.ClipMinimum;
Expand Down Expand Up @@ -378,6 +415,10 @@ protected virtual void RenderMajorItems(Axis axis, double axisPosition, double t
}
}

// Label max width
double labelEndPosition = double.MinValue;
if (axis.Position == AxisPosition.Left || axis.Position == AxisPosition.Top) labelEndPosition = double.MaxValue;

// Render the axis labels (numbers or category names)
foreach (double value in this.MajorLabelValues)
{
Expand All @@ -403,6 +444,9 @@ protected virtual void RenderMajorItems(Axis axis, double axisPosition, double t
SnapTo(plotAreaBottom, ref transformedValue);
}

string text = axis.FormatValue(value);
var labelSize = this.RenderContext.MeasureText(text, axis.ActualFont, axis.ActualFontSize, axis.ActualFontWeight);

@VisualMelon VisualMelon Aug 11, 2023 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the overload that takes an angle and passing it axis.Angle (last param) means we can change the block below to the following, which is simpler and makes the code consistent with the automatic margin codes. (There is also a bug in the Bottom block at present: should be like Top but is like Left and Right). The behaviour of this still seems a bit funny, but because it matches the margin code it should be left alone in this PR. May be something for us to investigate further another time @Jonarw

                    case AxisPosition.Left:
                        pt = new ScreenPoint(axisPosition + a1 - axis.AxisTickToLabelDistance, transformedValue);
                        this.GetRotatedAlignments(axis.Angle, -90, out ha, out va);
                        labelEndPosition = Math.Min(labelEndPosition, pt.x - labelSize.Width);
                        break;
                    case AxisPosition.Right:
                        pt = new ScreenPoint(axisPosition + a1 + axis.AxisTickToLabelDistance, transformedValue);
                        this.GetRotatedAlignments(axis.Angle, 90, out ha, out va);
                        labelEndPosition = Math.Max(labelEndPosition, pt.x + labelSize.Width);
                        break;

                    case AxisPosition.Top:
                        pt = new ScreenPoint(transformedValue, axisPosition + a1 - axis.AxisTickToLabelDistance);
                        this.GetRotatedAlignments(axis.Angle, 0, out ha, out va);
                        labelEndPosition = Math.Min(labelEndPosition, pt.y - labelSize.Height);
                        break;
                    case AxisPosition.Bottom:
                        pt = new ScreenPoint(transformedValue, axisPosition + a1 + axis.AxisTickToLabelDistance);
                        this.GetRotatedAlignments(axis.Angle, -180, out ha, out va);
                        labelEndPosition = Math.Max(labelEndPosition, pt.y + labelSize.Height);
                        break;


var pt = new ScreenPoint();
var ha = HorizontalAlignment.Right;
var va = VerticalAlignment.Middle;
Expand All @@ -412,25 +456,40 @@ protected virtual void RenderMajorItems(Axis axis, double axisPosition, double t
pt = new ScreenPoint(axisPosition + a1 - axis.AxisTickToLabelDistance, transformedValue);
this.GetRotatedAlignments(axis.Angle, -90, out ha, out va);

if (axis.Angle % 90 == 0)
labelEndPosition = Math.Min(labelEndPosition, pt.x - labelSize.Width);
else
labelEndPosition = Math.Min(labelEndPosition, pt.x - labelSize.Height);
break;
case AxisPosition.Right:
pt = new ScreenPoint(axisPosition + a1 + axis.AxisTickToLabelDistance, transformedValue);
this.GetRotatedAlignments(axis.Angle, 90, out ha, out va);

if (axis.Angle % 90 == 0)
labelEndPosition = Math.Max(labelEndPosition, pt.x + labelSize.Width);
else
labelEndPosition = Math.Max(labelEndPosition, pt.x + labelSize.Height);
break;

case AxisPosition.Top:
pt = new ScreenPoint(transformedValue, axisPosition + a1 - axis.AxisTickToLabelDistance);
this.GetRotatedAlignments(axis.Angle, 0, out ha, out va);

if (axis.Angle % 90 == 0)
labelEndPosition = Math.Min(labelEndPosition, pt.y - labelSize.Height);
else
labelEndPosition = Math.Min(labelEndPosition, pt.y - labelSize.Width);
break;
case AxisPosition.Bottom:
pt = new ScreenPoint(transformedValue, axisPosition + a1 + axis.AxisTickToLabelDistance);
this.GetRotatedAlignments(axis.Angle, -180, out ha, out va);

if (axis.Angle % 90 == 0)
labelEndPosition = Math.Max(labelEndPosition, pt.y + labelSize.Width);
else
labelEndPosition = Math.Max(labelEndPosition, pt.y + labelSize.Height);
break;
}

string text = axis.FormatValue(value);
this.RenderContext.DrawMathText(
pt,
text,
Expand Down Expand Up @@ -471,6 +530,21 @@ protected virtual void RenderMajorItems(Axis axis, double axisPosition, double t
this.RenderContext.DrawLineSegments(extraSegments, this.ExtraPen, axis.EdgeRenderingMode.GetActual(EdgeRenderingMode.PreferSharpness));
}

// Capture the points for the axis area
if (isHorizontal)
{
pt0 = new ScreenPoint(axis.Transform(clipMinimum), axisPosition);
pt1 = new ScreenPoint(axis.Transform(clipMaximum), labelEndPosition);
}
else
{
pt0 = new ScreenPoint(axisPosition, axis.Transform(clipMinimum));
pt1 = new ScreenPoint(labelEndPosition, axis.Transform(clipMaximum));
}

axis.AxisLineArea = new OxyRect(pt0, pt1);
axis.AxisArea = new OxyRect(pt0, pt1);

if (drawAxisLine)
{
// Draw the axis line (across the tick marks)
Expand Down
3 changes: 3 additions & 0 deletions Source/OxyPlot/Axes/Rendering/MagnitudeAxisRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public override void Render(Axis axis, int pass)

var angleAxis = this.Plot.DefaultAngleAxis;

angleAxis.AxisLineArea = new EmptyShape();
angleAxis.AxisArea = new EmptyShape();

if (angleAxis == null)
{
throw new NullReferenceException("Angle axis should not be null.");
Expand Down
15 changes: 15 additions & 0 deletions Source/OxyPlot/Rendering/EmptyShape.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace OxyPlot
{
/// <summary>
/// Describes an empty shape
/// </summary>
public struct EmptyShape : IOxyRegion
{
/// <inheritdoc/>
public bool Contains(double x, double y) { return false; }
}
}
19 changes: 19 additions & 0 deletions Source/OxyPlot/Rendering/IOxyRegion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace OxyPlot
{
/// <summary>
/// Describes the Interface for a shape.
/// </summary>
public interface IOxyRegion
{
/// <summary>
/// Determines whether the specified point is inside the shape.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <returns><c>true</c> if the shape contains the specified point; otherwise, <c>false</c>.</returns>
bool Contains(double x, double y);

Comment thread
VisualMelon marked this conversation as resolved.
Resolved
}
}
Loading