New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plotmetrics #90
Plotmetrics #90
Conversation
Thanks for starting the work on this feature. Unfortunately, I will only have time to look at this in detail next week, but it would be good to get the rest of the Racket teams opinion on this approach. My own take is that the overall approach is OK, and it does solve some of the challenges with returning extra information with the plot itself. |
A couple thoughts.
|
@samth Currently |
Hi @samth, is it OK to change the return type of Another question for @samth: this is a complex feature, and it is not clear to me that these changes will be sufficient or even useful for the types of things people have requested them. I don't think we can get this right in one go, since we have only one use case:
What do you think? |
[get-plot-bounds (-> (Vectorof (Vector Real Real)))] | ||
[plot->dc (-> (Vectorof Real) (Vectorof Real))])) | ||
|
||
(struct plotpict pict ([bounds : (Vectorof (Vector Real Real))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the structure should be named plot-pict
as this is more consistent with other names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, plot-pict
is a function name...
(define-type Plot-Metrics<%> | ||
(Class | ||
[get-plot-bounds (-> (Vectorof (Vector Real Real)))] | ||
[plot->dc (-> (Vectorof Real) (Vectorof Real))])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need a dc->plot
method, since that would be used to convert mouse coordinates to the plot space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the return type be for plot3D, a line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or alternatively: apart from adding dc->plot
, also provide a plane-vector
method. For 2D this would always return #(0 0 1), for 3D this would return the unit vector e
that is perpendicular to the screen. So for dc->plot
3D can just return the coordinate c0
for the plane trough the origin. Then the user can reconstruct the right coordinate by adding these two together: (+ c0 (* a e))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not think about the 3D case and the 2D plot area already has a dc->plot
method.
I am thinking about this feature in terms of "what functionality is needed to construct interactive plots?" since this was the original use case for creating #83. I have some experience with this, from using the plot-snip%
interface for 2D plots, and I know that there needs some way from converting from DC to plot coordinates, so be able to interact with the mouse.
For 3D plots, I have no experience with them, and not sure what is needed to achieve the final goal. Making dc->plot
return a line is the mathematically correct thing to do, but I am not sure if this would be sufficient and/or useful for anything practical.
Perhaps we could not do the 3D plot changes at all, and wait until someone comes with a use case that we can use to validate the design?
(plot-area area renderer-list))])) | ||
(plot-area area renderer-list) | ||
|
||
(define bounds (vector (vector (assert (ivl-min (vector-ref bounds-rect 0)) real?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the bounds should be calculated and memory allocated for them in the get-plot-bounds
method below, perhaps lazily initialize them as a member of the returned plot-metrics object.
The plot/dc
method is used in every 2D plot and most of these uses will just discard the plot-metrics object so I want to avoid the memory allocation penalty (and runtime execution penalty) in all those use cases.
(assert (ivl-max (vector-ref bounds-rect 0)) real?)) | ||
(vector (assert (ivl-min (vector-ref bounds-rect 1)) real?) | ||
(assert (ivl-max (vector-ref bounds-rect 1)) real?)))) | ||
(new (class object% |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While creating a new class type and immediately instantiating an object is supported by Racket, I am not sure if this is the most efficient way to do it in terms of memory allocation and runtime performance -- this object will be discarded in most current uses of the plot package).
@samth do you know whether it would be more efficient to create a separate plot-metrics%
class and just instantiate this here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mflatt will know better here, but it will at most save a bit of allocation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We only have one use case for these changes (#83 (comment)) and this would involve calling plot/dc
several times a second (30FPS perhaps). I think this code should avoid any unnecessary memory allocations and defer any deferrable computations until they are needed (e.g. calculating the bounds).
(plot-area area renderer-list))])) | ||
(plot-area area renderer-list) | ||
|
||
(define bounds (vector (vector (assert (ivl-min (vector-ref bounds-rect 0)) real?) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same notes about allocating bounds
as for the 2D plot.
(assert (ivl-max (vector-ref bounds-rect 1)) real?)) | ||
(vector (assert (ivl-min (vector-ref bounds-rect 2)) real?) | ||
(assert (ivl-max (vector-ref bounds-rect 2)) real?)))) | ||
(new (class object% |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and for creating a new class and instantiating it...
@bdeket I don't really understand your comment. You can define the interface in untyped code, and then ensure that it's implemented by all the classes. There would be some duplication, which is unavoidable here for the reasons I described on Slack, but that doesn't seem so bad here. |
@alex-hhh On the backwards-compatibility issue, I think it's unlikely to be a problem. I agree with @rfindler (where did his comment go?) that the most likely problem is someone with a contract that starts failing, but I still think that's unlikely. On the experimental issue, I'm less sure. We should think about what changes we might want to make that wouldn't be possible in a compatible way (say, by adding another method). |
@samth I didn't understand how to do what you were proposing. But I think I do now: My initial thought was only having the interface in untyped code, and I didn't see how to integrate that with the other pieces. |
Hi @samth
My concern is that this is a large and complex change, and we only have only one use case for it (#83 (comment)). There is a distinct possibility that, apart from perhaps a few tests and toy examples, no one will use this, and, when they will, we'll discover that, despite our best efforts, the interface is not sufficient or not useful. As such, I would like to get these changes out in a Racket release (since most people won't build the plot package themselves), announce them, allow people to try them out and provide feedback then incorporate that feedback in a future plot package release. |
Hi @bdeket , I think the changes look good. Can you please add some unit tests for these, I think we should leave the documentation update for a separate pull request, as this one is getting too large. |
Hi @alex-hhh, I will have time tomorrow/later this week. |
I also thought about creating a Another option is to not merge this branch yet, and, when someone asks for this feature in the future, point them to this branch and complete the work at that time based on their feedback. The plot package does not change that often and it would be simple to keep this branch up to date with the main branch. I am not sure how many people would use this feature in the short to medium term (say 6 - 12 months), the person who originally created the issue has not responded yet, and it may be that they are no longer interested. Finally, I am also happy to just merge this. When someone starts using these features, we'll get some feedback and decide at that time what to do if the API needs to be changed. |
Don't create an |
(check-equal? (send ps plot->dc plotcoords) coords)) | ||
(test-suite "PR90: 3d/snip after resize" | ||
(send ps resize 800 800) | ||
(sleep/yield .5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this sleep call will create a flaky test which might fail depending on what machine it is run on and the overall CPU load on that machine. Unfortunately, I don't have a suggestion on how to carry out this test, so I think this should be disabled for now.
I think the testing is sufficient for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will it be ok if I leave the test in as a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it should be left in as a comment -- it would be a good idea to be able to test these things, but I don't know how.
Also, you will need to merge the master branch into this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There ought to be something that can wait for the resize to complete -- maybe @rfindler knows.
Returning to the "marking things unstable" issue, after thinking about this more and talking with the rest of the core team, I think that just noting in the documentation that this API is unstable in the 8.1 release is very reasonable. Of course, just committing to it now is also fine, and I'll leave that judgment up to you. |
I will consider any backwards incompatible changes very carefully, and avoid them if at all possible, but, given that this change will not be validated with real use-cases, there is a small possibility that we got this interface wrong. |
Sorry for the delay, alas it looks like I missed all the action. Regardless @alex-hhh this handles everything that I was thinking of.
I'm no expert on API design, but this implementation seems to me to have enough wiggle room that it can be modified or extended with a new approach that feels closer to the platonic ideal of this sort of a thing. |
As part of racket#90, calls to `(make-bitmap ...)` were replaced with `(make-object bitmap% ...)` but the two calls have different defaults for whether to create an alpha channel for the bitmap or not. The `(make-object bitmap% ...)` variant defaults to not having an alpha channel, so it needs to be explicitly specified.
Pull request #90 implements the plot-metrics interface, a mechanism that allows the user to determine the location of elements on the plot image itself, so plots can be further decorated, it is based on a request in issue #83. While this functionality has been available for a while now, it was not documented. This PR adds documentation to this interface, so users can discover the functionality and use it.
Possible implementation for getting plot-metrics from plots. see #83
Currently implemented methods:
get-plot-boundaries
:(-> (Vectorof (Vector Real Real))
- give min/max x y (z) valuesplot->dc
:(-> (Vecorof Real) (Vectorof Real))
- translate plot x y (z) coordinates to dc x y coordinatesPossible problems:
plot/dc
,plot-bitmap
,plot-snip
,plot-pict
plot-pict
the return types are new objects for which no interface or class is defined. There will be no straighforward way to check if the returned objects implementPlot-Metrics<%>
todo: