Skip to content

X axis and Y axis MeasureTicks does not take tick rotation into account cutting off X axis tick labels and leaving additional space for Y axis tick labels. #3458

@David-A-Blankenship

Description

@David-A-Blankenship

Issue:
When calculating the size of the panel for the axis, the MeasureTicks function does not account for rotation for custom tick labels. This causes X axis tick labels to be cut off because the panel is not large enough in height. The Y axis width is wider than it needs to be for rotated custom tick labels.

With axis.TickLabelStyle.Rotation = 0 for all axes.
Panel sizing is correct for both X and Y axis, though X axis labels overlap.
image

With axis.TickLabelStyle.Rotation = 90 for all axes.
Panel sizing is incorrect for both X and Y axis. Labels are cut off on the X axis and additional white space is visible on the Y axis.
image

ScottPlot Version:
ScottPlot.Avalonia version 5.0.21
ScottPlot version 5.0.21

Code Sample: See http://scottplot.net/faq/repro/ for tips about creating reproducible code samples

This is how I am setting the ticks on the axis.

            // create a manual tick generator and add ticks 
            ScottPlot.TickGenerators.NumericManual ticks = new();

            // add major ticks with their labels
            foreach (var kvp in results.Labels)
            {
                ticks.AddMajor(kvp.Value, kvp.Key);
            }

            // tell the axis to use the custom tick genrator
            axis.TickGenerator = ticks;

            // rotate the tick marks 
            axis.TickLabelStyle.Rotation = 90;
            axis.TickLabelStyle.OffsetY = 0; 
            axis.TickLabelStyle.Alignment = Alignment.MiddleLeft;

In XAxisBase.MeasureTicks only the height of the label is considered without regard for rotation.

    private float MeasureTicks()
    {
        using SKPaint paint = new();
        TickLabelStyle.ApplyToPaint(paint);

        float largestTickHeight = 0;

        foreach (Tick tick in TickGenerator.Ticks)
        {
            PixelSize tickLabelSize = Drawing.MeasureString(tick.Label, paint);
            largestTickHeight = Math.Max(largestTickHeight, tickLabelSize.Height + 10);
        }

        return largestTickHeight;
    }

I worked around this for the X axis by setting the minimal panel height based on the XAxisBase.Measure function.

            using SKPaint paint = new();
            axis.TickLabelStyle.ApplyToPaint(paint);

            float largestTickHeight = 0;

            double degrees = axis.TickLabelStyle.Rotation;
            double angle = Math.PI * degrees / 180.0;
            float sinRotation = (float)Math.Abs(Math.Sin(angle));
            float cosRotation = (float)Math.Abs(Math.Cos(angle));

            foreach (Tick tick in axis.TickGenerator.Ticks)
            {
                ScottPlot.PixelSize tickLabelSize = Drawing.MeasureString(tick.Label, paint);
                var rotatedTickLabelHeight = tickLabelSize.Width * sinRotation;
                var rotatedTickLabelWidth = tickLabelSize.Height * cosRotation;
                var rotatedTickHeight = rotatedTickLabelHeight + rotatedTickLabelWidth;
                largestTickHeight = Math.Max(largestTickHeight, rotatedTickHeight + 10);
            }

The YAxisBase.MeasureTicks function only using tickLabelSize.Width and not using the rotation.

There may be a better way to calculate the tick label width and height, but this is what I did.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions