-
Notifications
You must be signed in to change notification settings - Fork 981
Description
In engineering, equal axes are a big deal to the point of often being a show stopper... and while describing the functionality provided here as 'wrong' might seem like hyperbole, the user experience falls short of what is provided by other packages (and implicitly, what is expected).
First a demonstration of the issue: Set any plot to EqualScaleMode.ZoomOut and the axes will be equal alright (so it's sort of the correct behavior), but testing user experience demands we go further. Grab the lower right corner of your window and drag it right, then down, then left, then up, then right... Keep doing this resizing the window one axis at a time and watch your plot shrink smaller and smaller until it becomes but a blip in the center of the screen surrounded by ever growing axes... Shrinking data as I shrink the plot? Yes. Same data size as I expand it? No! It should expand too! (that's the 'wrong' behavior, which is truly annoying).
After finally figuring out what properties represent what in there, and that DataSizePx is NOT the pixel size the data needs (which would make sense considering how it's used in EnforceEqualAxisScales() ), but in fact the max pixel size that data HAS available, the source of the problem became obvious.
In EnforceEqualAxisScales, the line
double num = Math.Max(XAxis.Dims.UnitsPerPx, YAxis.Dims.UnitsPerPx);can't possibly use UnitsPerPx since it's determined using DataSizePx. Equal scaling must be determined considering what the data would actually need to get plotted at a given scale, not how much it has available.
The following is a hacked EnforceEqualAxisScales that uses C&P code from AxisAutoX/Y to determine data extents. It's ugly, it doesn't respect any kind of margins, but it works as expected:
case EqualScaleMode.ZoomOut:
double minX = double.NaN;
double maxX = double.NaN;
double minY = double.NaN;
double maxY = double.NaN;
int xAxisIndex = 0;
int yAxisIndex = 0;
var plottableXLimits = Plottables.Where(x => x is IPlottable)
.Select(x => (IPlottable)x)
.Where(x => x.IsVisible)
.Where(x => x.XAxisIndex == xAxisIndex)
.Select(x => x.GetAxisLimits())
.ToArray();
foreach (var limits in plottableXLimits)
{
if (!double.IsNaN(limits.XMin))
minX = double.IsNaN(minX) ? limits.XMin : Math.Min(minX, limits.XMin);
if (!double.IsNaN(limits.XMax))
maxX = double.IsNaN(maxX) ? limits.XMax : Math.Max(maxX, limits.XMax);
}
var plottableYLimits = Plottables.Where(x => x is IPlottable)
.Select(x => (IPlottable)x)
.Where(x => x.IsVisible)
.Where(x => x.YAxisIndex == yAxisIndex)
.Select(x => x.GetAxisLimits())
.ToArray();
foreach (var limits in plottableYLimits)
{
if (!double.IsNaN(limits.YMin))
minY = double.IsNaN(minY) ? limits.YMin : Math.Min(minY, limits.YMin);
if (!double.IsNaN(limits.YMax))
maxY = double.IsNaN(maxY) ? limits.YMax : Math.Max(maxY, limits.YMax);
}
if (double.IsNaN(minX) && double.IsNaN(maxX))
return;
if (double.IsNaN(minY) && double.IsNaN(maxY))
return;
double maxUnitsPerPx = Math.Max((maxX - minX) / XAxis.Dims.DataSizePx, (maxY - minY) / YAxis.Dims.DataSizePx);
double halfX = (XAxis.Dims.DataSizePx / 2) * maxUnitsPerPx;
double halfY = (YAxis.Dims.DataSizePx / 2) * maxUnitsPerPx;
AxisSet(XAxis.Dims.Center - halfX, XAxis.Dims.Center + halfX, YAxis.Dims.Center - halfY, YAxis.Dims.Center + halfY);
return;The point is, it finds the actual data extents, and FROM THAT figures out the axes scaling (and the data shrinks and grows as needed). I didn't check since these modes are useless to me, but looking at the code I suspect PreserveX/Y suffer from the same issues...
...I'm also thinking a dedicated method to find data extents might be in order (I was a bit surprised there wasn't one). Given how often this code has to run with equal axes on (again, it's a big deal), performance might even benefit from computing extents on Add/Update and having it readily available when needed instead of having to go through the list of plottables on every render... though that might be an architectural change.
- ScottPlot Version: 4.1.16
- Operating System: Windows 10
- Application Type: WPF
- .NET Version: .NET 5.0