Skip to content

Legend Restore MarkerStyle.Size behavior and fix marker clipping inside rectangle#5031

Merged
swharden merged 6 commits intoScottPlot:mainfrom
manaruto:legend-autosize-markers
Aug 22, 2025
Merged

Legend Restore MarkerStyle.Size behavior and fix marker clipping inside rectangle#5031
swharden merged 6 commits intoScottPlot:mainfrom
manaruto:legend-autosize-markers

Conversation

@manaruto
Copy link
Contributor

@manaruto manaruto commented Aug 9, 2025

This PR addresses a regression introduced after PR #5006, where the MarkerStyle.Size property stopped taking effect when a MarkerShapeDefault was set.

Fixes #4999 (marker shape clipping by legend rectangle).

In the original PR, the logic was:

if (MarkerShapeDefault != MarkerShape.None)
{
    item.MarkerStyle.Shape = item.MarkerShape != MarkerShape.None ? item.MarkerShape : MarkerShapeDefault;

    // NOTE: tried this but size seems to be defaulting something to something small, 
    // so you can barely see the shape, defaulting to font size for now
    //item.MarkerStyle.Size = item.MarkerStyle.Size != 0 ? item.MarkerStyle.Size : item.LabelFontSize;
    item.MarkerStyle.Size = item.LabelFontSize;
}
else
{
    item.MarkerStyle.Shape = item.MarkerShape;

    // If the marker size is not set, use the label font size as a fallback
    // NOTE: Same as above, tried it, but somehow the default is really small, 
    // setting to font size for now.
    //item.MarkerStyle.Size = item.MarkerStyle.Shape != MarkerShape.None && item.MarkerStyle.Size != 0 
    //    ? item.MarkerStyle.Size : item.LabelFontSize;

    item.MarkerStyle.Size = item.LabelFontSize;

    if (item.MarkerShape == MarkerShape.None && item.MarkerStyle.Shape == MarkerShape.None)
    {
        item.LineStyle.Render(canvas, symbolLine, paint);
    }

    item.FillStyle.Render(canvas, symbolFillRect, paint);
    item.OutlineStyle.Render(canvas, symbolFillOutlineRect, paint);
}

Because item.MarkerStyle.Size was being overwritten unconditionally with LabelFontSize, any explicit size set by the user was ignored.

Changes in this PR

Legend.cs

  • Removed unconditional overwrite of LegendItem.MarkerStyle.Size with LabelFontSize.
  • Apply MarkerShapeDefault only when no explicit marker shape is provided.
  • Use label font size as a fallback size iff MarkerStyle.Size <= 0.

Wrapping.cs

  • Updated Wrapping.GetLayout() to calculate symbol square as the max of Legend.SymbolWidth and the largest MarkerStyle.Size.
  • Adjusted row height and per-item symbol rectangles to prevent clipping when markers are larger than the configured symbol width.

Code Example:

// 1) Add your plottables
var xs = Generate.Consecutive(51);
var ys1 = Generate.Sin(51);
var ys2 = Generate.Cos(51);

var sp1 = formsPlot1.Plot.Add.Scatter(xs, ys1);
sp1.LegendText = "Sine";
sp1.MarkerSize = 15;
sp1.LineWidth = 0;
sp1.Color = Colors.Magenta;

var sp2 = formsPlot1.Plot.Add.Scatter(xs, ys2);
sp2.LegendText = "Cosine";
sp2.MarkerSize = 45;                       // big marker
sp2.MarkerShape = MarkerShape.OpenSquare;
sp2.LineWidth = 0;
sp2.Color = Colors.Green;

var legend = formsPlot1.Plot.ShowLegend(
Alignment.LowerRight,
 ScottPlot.Orientation.Horizontal);

// make the square each symbol sits in ≥ the largest marker
 float big = formsPlot1.Plot.PlottableList
.OfType<IHasMarker>()
.Max(p => p.MarkerStyle.Size);

legend.SymbolWidth = big + 6;

formsPlot1.Refresh();
Screenshot 2025-08-09 134430

- Removed unconditional overwrite of LegendItem.MarkerStyle.Size with LabelFontSize.
- Apply MarkerShapeDefault only when no explicit marker shape is provided.
- Use label font size as a fallback size iff MarkerStyle.Size <= 0.
- Updated Wrapping.GetLayout() to calculate symbol square as the max of Legend.SymbolWidth and the largest MarkerStyle.Size.
- Adjusted row height and per-item symbol rectangles to prevent clipping when markers are larger than the configured symbol width.
@aespitia
Copy link
Contributor

thanks for looking into this, wasn't sure what was going on originally

@swharden swharden enabled auto-merge (squash) August 22, 2025 23:28
@swharden
Copy link
Member

Thanks @manaruto, this looks great! 🚀

@swharden swharden disabled auto-merge August 22, 2025 23:32
@swharden swharden merged commit 774ba75 into ScottPlot:main Aug 22, 2025
3 checks passed
@swharden
Copy link
Member

Something I noticed after merging is that this makes the calculated height larger than it should be for some things like line plots

before after
image image

It doesn't seem like a big deal, but I'd like to keep this pixel perfect consistent with previous releases if possible. I bet some people out there carefully size their plots and the number of rows in their legend to fit, and I don't want to disrupt that if we can avoid it. I'll take care of this in a separate PR, but wanted to note it here

@swharden
Copy link
Member

I think the core issue is that the present logic assumes that the symbol in the legend is always a square. However, for many legends this is not the case (e.g., the one pictured is a line which is wider than high). I think this is fixable though. I'll keep working... hopefully publish a package to NuGet tonight once this last thing is fixed

@swharden
Copy link
Member

I think it's solved in #5056

[Test]
public void Test_LargeMarkers_ExpandLegendToFit()
{
    Plot plot = new();

    var xs = Generate.Consecutive(51);
    var ys1 = Generate.Sin(51);
    var ys2 = Generate.Cos(51);

    var sp1 = plot.Add.Markers(xs, ys1);
    sp1.LegendText = "Sine";
    sp1.MarkerSize = 15;

    var sp2 = plot.Add.Markers(xs, ys2);
    sp2.LegendText = "Cosine";
    sp2.MarkerSize = 45;
    sp2.MarkerShape = MarkerShape.OpenSquare;

    plot.ShowLegend(Alignment.LowerRight, Orientation.Horizontal);
    plot.SaveTestImage();
}
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Marker size not fully displayed on legend. (shows cropped)

3 participants