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
15 changes: 15 additions & 0 deletions Source/OxyPlot/Axes/Axis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,21 @@ public double Scale
/// </summary>
public OxyThickness DesiredMargin { get; protected set; }

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

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

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

Comment thread
VisualMelon marked this conversation as 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 someScreenPoint = ScreenPoint.Undefined;

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.

This could do with a better name

@ChrisCC6 ChrisCC6 Dec 6, 2022 •

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.

maybe

ScreenPoint? innerRadiusTangentScreenPoint = null;

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.

innermostPoint would make sense; may need to check if reversing the axis messes with this (e.g. setting StartPosition=1 and EndPosition=0)

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.

Is it not the case that all points of the angle axis have the same distance to the center? Am I getting it wrong right now?

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.

Ah, yes of course: sorry, my mistake.

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.

... but I admit: innerRadiusTangentScreenPoint is not nice to read...
optional: innerTangentPoint ?

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.

Given the points are all on the circumference, can we just derive one using magnitudeAxis.Transform(magnitudeAxis.ClipMaximum, <some sensible value>, axis))?

Actually, that inspires a name: pointOnInnerCircumference (long, but explicit, and hopefully should be clear if I've missed the point)

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));

someScreenPoint = screenPoints.FirstOrDefault();
Comment thread
VisualMelon marked this conversation as resolved.
Outdated
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 (someScreenPoint.Equals(ScreenPoint.Undefined) && screenPoints.Count() > 0)
someScreenPoint = 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 (!someScreenPoint.Equals(ScreenPoint.Undefined))
{
double r1 = magnitudeAxis.MidPoint.DistanceTo(someScreenPoint);
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 OxyCircle(0, 0, 0);
axis.AxisArea = new OxyCircle(0, 0, 0);
}
}
}
}
77 changes: 75 additions & 2 deletions Source/OxyPlot/Axes/Rendering/HorizontalAndVerticalAxisRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ namespace OxyPlot.Axes
/// </summary>
public class HorizontalAndVerticalAxisRenderer : AxisRendererBase
{
private ScreenPoint pt0;
Comment thread
VisualMelon marked this conversation as resolved.
Outdated
private ScreenPoint pt1;
private ScreenPoint pt2;
private ScreenPoint pt3;

/// <summary>
/// Initializes a new instance of the <see cref="HorizontalAndVerticalAxisRenderer" /> class.
/// </summary>
Expand Down Expand Up @@ -274,6 +279,37 @@ 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)


switch (axis.Position)
{
case AxisPosition.Left:
this.pt3 = new ScreenPoint(lpt.X, lpt.Y + titleSize.Width * 0.5);
this.pt2 = new ScreenPoint(lpt.X + titleSize.Height, lpt.Y - titleSize.Width * 0.5);
break;
case AxisPosition.Right:
this.pt3 = new ScreenPoint(lpt.X - titleSize.Height, lpt.Y + titleSize.Width * 0.5);
this.pt2 = new ScreenPoint(lpt.X, lpt.Y - titleSize.Width * 0.5);
break;
case AxisPosition.Top:
this.pt3 = new ScreenPoint(lpt.X - titleSize.Width * 0.5, lpt.Y + titleSize.Height);
this.pt2 = new ScreenPoint(lpt.X + titleSize.Width * 0.5, lpt.Y);
break;
case AxisPosition.Bottom:
this.pt3 = new ScreenPoint(lpt.x - titleSize.Width * 0.5, lpt.Y);
this.pt2 = 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(this.pt2, this.pt3);

if (axis.AxisLineArea is OxyRect axisLineArea)
axis.AxisArea = axisLineArea.Union(new OxyRect(this.pt2, this.pt3));

this.RenderContext.DrawMathText(
lpt,
axis.ActualTitle,
Expand Down Expand Up @@ -378,6 +414,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 +443,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 +455,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 +529,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)
{
this.pt0 = new ScreenPoint(axis.Transform(clipMinimum), axisPosition);
this.pt1 = new ScreenPoint(axis.Transform(clipMaximum), labelEndPosition);
}
else
{
this.pt0 = new ScreenPoint(axisPosition, axis.Transform(clipMinimum));
this.pt1 = new ScreenPoint(labelEndPosition, axis.Transform(clipMaximum));
}

axis.AxisLineArea = new OxyRect(this.pt0, this.pt1);
axis.AxisArea = new OxyRect(this.pt0, this.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 OxyRect(0, 0, 0, 0);
Comment thread
VisualMelon marked this conversation as resolved.
Outdated
angleAxis.AxisArea = new OxyRect(0, 0, 0, 0);

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

namespace OxyPlot
{
/// <summary>
/// Describes the Interface for a shape.
/// </summary>
public interface IShape
{
/// <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);

/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String" /> that represents this instance.</returns>
string ToString();

/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <param name="format">The format.</param>
/// <param name="formatProvider">The format provider.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public string ToString(string format, IFormatProvider formatProvider);
Comment thread
VisualMelon marked this conversation as resolved.
Outdated


}
}
Loading