Skip to content

Bracket Plottable#1863

Merged
swharden merged 24 commits intoScottPlot:mainfrom
bclehmann:feature/bracket-annotation
Jun 3, 2022
Merged

Bracket Plottable#1863
swharden merged 24 commits intoScottPlot:mainfrom
bclehmann:feature/bracket-annotation

Conversation

@bclehmann
Copy link
Member

Purpose:
#1302 #1028

double x1 = 0;
double y1 = 0;
double x2 = 1;
double y2 = 1;

var defaultBracket = plt.AddBracket(x1, y1, x2, y2);
defaultBracket.Label = "Default";

var invertedBracket = plt.AddBracket(x1, y1, x2, y2);
invertedBracket.Label = "Inverted";
invertedBracket.Invert = true;

image

@bclehmann bclehmann changed the title Feature/bracket annotation Bracket Plottable May 29, 2022
@bclehmann
Copy link
Member Author

I'm still a little unsure of the Invert property, I feel like there's a better way to give the orientation than having the user try one way and then flip it.

@bclehmann
Copy link
Member Author

I also added a dependency: System.Numerics.Vectors. My understanding is that it's included in .NET Standard 2.1 and up, but for earlier versions you have to add the NuGet package.

I know you dislike adding dependencies, so I'm happy to write a simple Vector2 struct. Or indeed write the math without a vector struct, although it is more clear and concise like this.

@swharden
Copy link
Member

swharden commented Jun 2, 2022

Hey @bclehmann, this looks great! Here are my thoughts, and depending on your responses I can merge now or wait for an update then merge when you say it's ready.

Dependencies

Regarding the System.Numerics.Vectors dependency, it only affects .NET Framework, and I'm super fine with it. I don't mind adding dependencies that are officially supported by Microsoft

https://www.nuget.org/packages/System.Numerics.Vectors/

Naming is Hard

I'm still a little unsure of the Invert property, I feel like there's a better way to give the orientation than having the user try one way and then flip it.

When rendering you could assume the line is always diagonal from lower-left to upper-right (using Math.Min() and Math.Max() to swap them inside the render loop as needed). Your orientation flag could then be something like IsAbove or OpensDownard, and the only corner case would be a perfectly vertical bracket in which case IsLeft perhaps would be the correct language (or maybe could be a pass-through to control the same variable?)

Just an idea! I'm okay with it how it is if nothing better comes to mind.

@bclehmann
Copy link
Member Author

bclehmann commented Jun 2, 2022

Your orientation flag could then be something like IsAbove or OpensDownard, and the only corner case would be a perfectly vertical bracket in which case IsLeft perhaps would be the correct language

Yeah, I had a few ideas, but I don't know which (if any) are actually intuitive from an API perspective:

  • Have the user specify either clockwise or counterclockwise from the vector $\overrightarrow{\textbf{v}}$
  • Have the user specify a direction vector (or angle?), and we are to choose the normal vector of v that is closest to it
  • Have the user specify a normal vector. If it isn't a normal vector, find a way to fallback gracefully (probably the method above)
  • Have the user specify a mixture of IsAbove, IsBelow, etc

I think the best option is to specify an angle (relative to the plotting frame, not an angle relative to $\overrightarrow{\textbf{v}}$. I don't think we need a tie-breaker if the two normal vectors are bisected by the given angle, I think we can just pick the first one.

Of course, the most mathematically beautiful option is for them to specify Into or OutOf, which we interpret as the normal vector of the plane, which we then use to deduce the handedness of the coordinate system. With that, we can choose one of the normal vectors as the "real" normal, and the other its negation. Unfortunately I hardly think that's the simple or intuitive option.

@swharden
Copy link
Member

swharden commented Jun 2, 2022

Hey sorry for being brief but I'm on mobile and can respond in more detail tomorrow if needed, but I'm definitely leaning toward the first one.

Does this problem get easier if the user gives two points (maybe even two coordinate objects instead of double?) then sets a flag named something like NormalPosition? The XML comment could indicate that normal means CCW, then our logic is simpler and user intent can even align with variable names

@bclehmann
Copy link
Member Author

I implemented it, the only question is if the name LabelCounterClockwise could be improved.

@swharden
Copy link
Member

swharden commented Jun 3, 2022

I implemented it, the only question is if the name LabelCounterClockwise could be improved.

Thanks! I like the new name. Again, I really appreciate this PR - it's looking great! 🚀

Looking at this today I think I'll make 2 more quick improvements then merge in a few minutes.

  1. I catch myself forgetting the order of AddBracket() arguments, so I'll replace the method add an overload that accepts two coordinates instead of 4 doubles. All add an optional string label argument while I'm in there.

  2. I'll update the cookbook recipes to favor simplicity (using Func and math to pick coordinates was clever and produced a great figure, but is a little intimidating for new programmers) and also favor reproducibility (the Random() results in a different image hash every time). Random(0) could do the work, but I'll just stick an integer in there for simplicity.

@swharden
Copy link
Member

swharden commented Jun 3, 2022

Oof, this doesn't look as clean as I was hoping....

plt.AddBracket(new Coordinate(33, -1), new Coordinate(43, -1), "A");

vs.

plt.AddBracket(33, -1, 43, -1, "A");

I'll leave the overload but probably revert to passing integers for the cookbook.

I wish there were a more elegant way to pass pairs of numbers as arguments

@bclehmann
Copy link
Member Author

the Random() results in a different image hash every time

Ah, that was a mistake, it used to be (0, 0) and (1, 1), I changed it to be random because it was useful for testing.

As for the new overload, I like it, if you use the abbreviated syntax it can be quite clean:

plt.AddBracket(new(1, 1), new(2, 2));

Or alternatively:

plt.AddBracket(new() { X = 1, Y = 1 }, new() { X = 2, Y = 2 });

@swharden
Copy link
Member

swharden commented Jun 3, 2022

@bclehmann I'm merging this now, but I noticed some surprising behavior at the corners as the axes stretch away from a square aspect ratio and opened an issue #1869 - I didn't try to fix it yet (I'll put this down for tonight), but you can have a go at it if you're up for the challenge! Seems like more vector math "fun" redoing all the calculations to respect aspect ratio 😅

@swharden swharden enabled auto-merge June 3, 2022 03:50
@swharden swharden merged commit 43332a4 into ScottPlot:main Jun 3, 2022
@swharden
Copy link
Member

swharden commented Jun 3, 2022

As for the new overload, I like it, if you use the abbreviated syntax it can be quite clean:

plt.AddBracket(new(1, 1), new(2, 2));

I forgot about this syntax - I love it!

I'd love to move toward using Pixel and Coordinate and more, and this syntax looks great

@swharden swharden mentioned this pull request Nov 24, 2024
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.

2 participants