Skip to content

Markers: refactor#1688

Merged
swharden merged 17 commits intomasterfrom
marker
Feb 19, 2022
Merged

Markers: refactor#1688
swharden merged 17 commits intomasterfrom
marker

Conversation

@swharden
Copy link
Member

Currently MarkerShape is an enum and DrawMarker() uses a nasty switch to draw each marker.

Eventually (either here or ScottPlot 5) I want Marker objects to be classes that inherit IMarker and can Draw() themselves. This will make the code easier to maintain, make it easier to add new markers, and allow users to add their own custom markers too.

This PR will start by breaking apart the RenderMarker() method into smaller static methods, and explore options for moving toward a class-based marker system that minimizes breaking changes. Drawing routines will be optimized where appropriate. Resolves #1668

@swharden
Copy link
Member Author

@BambOoxX you might get a kick out of seeing where this is now.

I'm going to try some deeper refactoring tomorrow, but now your core suggestion from #1660 of discretely drawing lines instead of placing text has been implemented 🚀

before after
image image

I reduced the size where necessary to make sure all the markers are the same size

@BambOoxX
Copy link
Contributor

@swharden Very nice! The only one I would modify is the asterisk. I believe reducing the length of the diagonal lines by a sqrt(2) factor would make the marker rounder.

@BambOoxX
Copy link
Contributor

BambOoxX commented Feb 19, 2022

I think it would be good that hollow / thin markers inherit the line width by default, in order to maintain consistency.
See what happens if you (ridiculously) increase the line width.
image

I will try to add that and also the two marker types proposed in #1660 initially

@BambOoxX
Copy link
Contributor

Here a the new markers from #1689

image

@BambOoxX
Copy link
Contributor

Regarding the DrawMarkers method, my idea was focused at removing all the brush / pen creation from the loops as advised in
https://stackoverflow.com/a/1961966/9576551

@swharden
Copy link
Member Author

my idea was focused at removing all the brush / pen creation from the loops as advised...

This is a good point indeed! I'll keep refactoring the Marker system today and favor brush/pen re-use where possible.

...but t be honest I'm not terribly motivated to micro-optimize GDI rendering at this time since my biggest active goal right now is cutting GDI out of this library entirely (#1036). My coding goal for now is aimed at making rendering methods as easy to port to another rendering system as possible.

@BambOoxX
Copy link
Contributor

@swharden I see you are moving a lot of stuff around. I'll let you finish this then open a new PR with the following modifications (that I implemented at the moment) unless you don't think they are relevant.

  • Add a MarkerLineWidth property to IHasMarker (or the equivalent stuff handling markers), and correct the plottables implementing IHasMarker.
  • Improve DrawMarker and subsequent methods so that they take the MarkerLineWidth into account to plot the makers
  • improve LegendItem so that there is no overflow of markers or linewidths (currently this is handled by isRectangle but that leads to not plotting a maker ever if there is one for large LineWidth values)
  • Make MarkerSize and MarkerLineWidth depend on the LineWidth value in order to keep consistency (by default) between those values

@swharden
Copy link
Member Author

swharden commented Feb 19, 2022

This landed in a weird spot. All marker logic is in struct objects now which is a step in the right direction, but it wasn't easy to swap-out MarkerShape from an enum to a struct or class because lots of ScottPlot code uses MarkerShape arguments with default values (something you can only do with enums) so if I made the change it would break a lot of existing code... so I had to stop just short of my original goal.

The current solution is a little wonky but it works, doesn't break existing code, and these classes are ready to pick-up in ScottPlot 5 where breaking changes will be permitted.

public static IMarker Create(MarkerShape shape)
{
return shape switch
{
MarkerShape.none => new MarkerShapes.None(),
MarkerShape.filledCircle => new MarkerShapes.FilledCircle(),
MarkerShape.filledSquare => new MarkerShapes.FilledSquare(),
MarkerShape.openCircle => new MarkerShapes.OpenCircle(),
MarkerShape.openSquare => new MarkerShapes.OpenSquare(),
MarkerShape.filledDiamond => new MarkerShapes.FilledDiamond(),
MarkerShape.openDiamond => new MarkerShapes.OpenDiamond(),
MarkerShape.asterisk => new MarkerShapes.Asterisk(),
MarkerShape.hashTag => new MarkerShapes.Hashtag(),
MarkerShape.cross => new MarkerShapes.Cross(),
MarkerShape.eks => new MarkerShapes.Eks(),
MarkerShape.verticalBar => new MarkerShapes.VerticalBar(),
MarkerShape.triUp => new MarkerShapes.TriStarUp(),
MarkerShape.triDown => new MarkerShapes.TriStarDown(),
MarkerShape.filledTriangleUp => new MarkerShapes.FilledTriangleUp(),
MarkerShape.filledTriangleDown => new MarkerShapes.FilledTriangleDown(),
MarkerShape.openTriangleUp => new MarkerShapes.OpenTriangleUp(),
MarkerShape.openTriangleDown => new MarkerShapes.OpenTriangleDown(),
_ => throw new NotImplementedException($"unsupported {shape.GetType()}: {shape}"),
};
}

public static void DrawMarker(Graphics gfx, PointF pixelLocation, MarkerShape shape, float size, Brush brush, Pen pen)
{
if (size == 0 || shape == MarkerShape.none)
return;
float diameter = size;
float radius = diameter / 2;
/* Improve marker vs. line alignment on Linux and MacOS
* https://github.com/ScottPlot/ScottPlot/issues/340
* https://github.com/ScottPlot/ScottPlot/pull/1660
*/
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
pixelLocation = new PointF(pixelLocation.X + .5f, pixelLocation.Y);
IMarker marker = Marker.Create(shape);
marker.Draw(gfx, pixelLocation, radius, brush, pen);
}

I see you are moving a lot of stuff around. I'll let you finish this then open a new PR with the following modifications (that I implemented at the moment) unless you don't think they are relevant.

@BambOoxX I'm going to merge this now, and if you'd still like to make these changes given the new class structure you're welcome to in a new PR 👍 🚀

@swharden swharden merged commit 9d0b936 into master Feb 19, 2022
@swharden swharden deleted the marker branch February 19, 2022 19:44
@BambOoxX
Copy link
Contributor

I'm going to merge this now, and if you'd still like to make these changes given the new class structure you're welcome to in a new PR 👍 🚀

All right it's on !

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.

Markers: Render without using DrawString()

2 participants