diff --git a/plot-doc/plot/scribblings/plotting.scrbl b/plot-doc/plot/scribblings/plotting.scrbl index 204eaacb..eb370934 100644 --- a/plot-doc/plot/scribblings/plotting.scrbl +++ b/plot-doc/plot/scribblings/plotting.scrbl @@ -32,7 +32,7 @@ Each 3D plotting procedure behaves the same way as its corresponding 2D procedur [#:legend-anchor legend-anchor legend-anchor/c (plot-legend-anchor)] [#:out-file out-file (or/c path-string? output-port? #f) #f] [#:out-kind out-kind plot-file-format/c 'auto] - ) (or/c (is-a?/c snip%) void?)]{ + ) (or/c (and/c (is-a?/c snip%) (is-a?/c plot-metrics<%>)) void?)]{ Plots a 2D renderer or list of renderers (or more generally, a tree of renderers), as returned by @(racket points), @(racket function), @(racket contours), @(racket discrete-histogram), and others. By default, @(racket plot) produces a Racket value that is displayed as an image and can be manipulated like any other value. @@ -85,7 +85,7 @@ The @(racket #:lncolor) keyword argument is also accepted for backward compatibi [#:legend-anchor legend-anchor legend-anchor/c (plot-legend-anchor)] [#:out-file out-file (or/c path-string? output-port? #f) #f] [#:out-kind out-kind plot-file-format/c 'auto] - ) (or/c (is-a?/c snip%) void?)]{ + ) (or/c (and/c (is-a?/c snip%) (is-a/c plot-metrics<%>)) void?)]{ Plots a 3D renderer or list of renderers (or more generally, a tree of renderers), as returned by @(racket points3d), @(racket parametric3d), @(racket surface3d), @(racket isosurface3d), and others. When the parameter @(racket plot-new-window?) is @(racket #t), @(racket plot3d) opens a new window to display the plot and returns @(racket (void)). @@ -108,9 +108,9 @@ The @(racket #:az) and @(racket #:alt) keyword arguments are backward-compatible } @defproc[(plot-snip [ ] ...) - (is-a?/c 2d-plot-snip%)] + (and/c (is-a?/c 2d-plot-snip%) (is-a?/c plot-metrics<%>))] @defproc[(plot3d-snip [ ] ...) - (is-a?/c snip%)] + (and/c (is-a?/c snip%) (is-a?/c plot-metrics<%>))] @defproc[(plot-frame [ ] ...) (is-a?/c frame%)] @defproc[(plot3d-frame [ ] ...) @@ -142,13 +142,13 @@ for more details. [#: ] ...) void?] @defproc[(plot-pict [ ] ...) - pict?] + plot-pict?] @defproc[(plot3d-pict [ ] ...) - pict?] + plot-pict?] @defproc[(plot-bitmap [ ] ...) - (is-a?/c bitmap%)] + (and/c (is-a?/c bitmap%) (is-a?/c plot-metrics<%>))] @defproc[(plot3d-bitmap [ ] ...) - (is-a?/c bitmap%)]{ + (and/c (is-a?/c bitmap%) (is-a?/c plot-metrics<%>))]{ Plot to different non-GUI backends. These procedures accept the same arguments as @(racket plot) and @(racket plot3d), except deprecated keywords, and @racket[#:out-file] and @racket[#:out-kind]. @@ -182,7 +182,7 @@ Use @(racket plot-bitmap) or @(racket plot3d-bitmap) to create a @(racket bitmap [width (>=/c 0)] [height (>=/c 0)] [#: ] ...) - void?] + (is-a?/c plot-metrics<%>)] @defproc[(plot3d/dc [renderer-tree (treeof (or/c renderer3d? nonrenderer?))] [dc (is-a?/c dc<%>)] [x real?] @@ -190,7 +190,7 @@ Use @(racket plot-bitmap) or @(racket plot3d-bitmap) to create a @(racket bitmap [width (>=/c 0)] [height (>=/c 0)] [#: ] ...) - void?]{ + (is-a?/c plot-metrics<%>)]{ Plot to an arbitrary device context, in the rectangle with width @(racket width), height @(racket height), and upper-left corner @(racket x),@(racket y). These procedures accept the same arguments as @(racket plot) and @(racket plot3d), except deprecated keywords, and @racket[#:out-file] and @racket[#:out-kind]. diff --git a/plot-doc/plot/scribblings/utils.scrbl b/plot-doc/plot/scribblings/utils.scrbl index 554fca5f..137ffe3d 100644 --- a/plot-doc/plot/scribblings/utils.scrbl +++ b/plot-doc/plot/scribblings/utils.scrbl @@ -582,3 +582,136 @@ Convert @racket[plot-time]s to real seconds, and vice-versa. (plot-time+ (plot-time 32 0 12 1) (plot-time 32 0 14 1))] } + +@section{Plot Metrics} + +@definterface[plot-metrics<%> ()]{ + + The @racket[plot-metrics<%>] interface allows obtaining plot area positions + on the plots returned by @racket[plot], @racket[plot-snip], + @racket[plot-bitmap], @racket[plot/dc], as well as their 3D variants, + @racket[plot3d], @racket[plot3d-snip], @racket[plot3d-bitmap] and + @racket[plot3d/dc]. All plot objects returned by these functions implement + this interface. + + For plots created by @racket[plot-pict] and @racket[plot3d-pict], there is a + separate set of functions that provide the same functionality, for example, + see @racket[plot-pict-bounds]. + + @defmethod[(get-plot-bounds) (vectorof (vector/c real? real?))]{ + + Return the bounds of the plot as a vector of minimum and maximum values, + one for each axis in the plot. For 2D plots, this method returns a vector + of two elements, for the X and Y axes, while 3D plots return a vector of + three elements for the X, Y and Z axes. + + The values returned are in plot coordinates, to obtain the coordinates on + the drawing surface (i.e. image coordinates), use @method[plot-metrics<%> + plot->dc] on these bounds. + + Plot bounds for interactive plots, like those produced by @racket[plot] + and @racket[plot-snip], can change as the user zoom in and out the plot, + @method[plot-metrics<%> get-plot-bounds] always returns the current bounds + of the plot, but they might be invalidated by a user operation. + + } + + @defmethod[(plot->dc [coordinates (vectorof real?)]) (vectorof real?)]{ + + Convert @racket[coordinates] from plot coordinate system to the drawing + coordinate system (that is, image coordinates). For 2D plots, + @racket[coordinates] is a vector of two values, the X and Y coordinates on + the plot, while for 3D plots it is a vector of three values, the X, Y and + Z coordinates. + + This method can be used, for example, to determine the actual location on + the image where the coordinates @racket[0], @racket[0] are. It can also + be used to determine the location of the plot area inside the image, by + calling it on the plot bounds returned by @method[plot-metrics<%> get-plot-bounds]. + + For interactive plots, the coordinates might change as the user zooms in + and out the plot. + + } + + @defmethod[(dc->plot [coordinates (vectorof real?)]) (vectorof real?)]{ + + For 2D plots, this method returns the 2D plot coordinates that correspond + to the input @racket[coordinates], which are in the draw context + coordinate system. + + For 3D plots, this method returns a 3D position on the plane perpendicular + to the user view for the plot. Together with the normal vector for this + plane, returned by @method[plot-metrics<%> plane-vector], the projection + line can be reconstructed. + + This is the reverse operation from @method[plot-metrics<%> plot->dc] and + same remark about the user zooming in and out the plot applies. + + } + + @defmethod[(plane-vector) (vectorof real?)]{ + + Return the unit vector representing the normal of the screen through the + plot origin. For 2D plots this always returns @racket[#(0 0 1)], for 3D + plots this unit vector can be used to reconstruct plot coordinates from + draw context coordinates. + + For interactive 3D plots, the returned value will change if the user + rotates the plot. + + } + + @history[#:added "8.1"] +} + +@defproc[(plot-pict? [any any/c]) boolean?]{ + + Return @racket[#t] if @racket[any] is a plot returned by @racket[plot-pict]. + This can be used to determine if the functions @racket[plot-pict-bounds], + @racket[plot-pict-plot->dc], @racket[plot-pict-dc->plot] and + @racket[plot-pict-plane-vector] can be called on it. + + @history[#:added "8.1"] + +} + +@defproc[(plot-pict-bounds [plot plot-pict?]) (vectorof (vector/c real? real?))]{ + + Return the bounds of the plot returned by @racket[plot-pict]. See + @method[plot-metrics<%> get-plot-bounds] for more details. + + @history[#:added "8.1"] + +} + +@defproc[(plot-pict-plot->dc [plot plot-pict?] [coordinates (vectorof real?)]) + (vectorof real?)]{ + + Convert the plot @racket[coordinates] to draw context coordinates for the + @racket[plot]. See @method[plot-metrics<%> plot->dc] for more details. + + @history[#:added "8.1"] + +} + +@defproc[(plot-pict-dc->plot [plot plot-pict?] [coordinates (vectorof real?)]) + (vectorof real?)]{ + + Convert the draw contect @racket[coordinates] to plot coordinates for the + @racket[plot]. See @method[plot-metrics<%> dc->plot] for more details. + + @history[#:added "8.1"] + +} + +@defproc[(plot-pict-plane-vector [plot plot-pict?]) (vectorof real?)]{ + + Return the unit vector representing the normal of the screen through the + plot origin. For 2D plots this always returns @racket[#(0 0 1)], for 3D + plots this can be used to reconstruct plot coordinates from draw context + coordinates. See @method[plot-metrics<%> plane-vector] for more details. + + @history[#:added "8.1"] + +} diff --git a/plot-lib/plot/private/common/plotmetrics.rkt b/plot-lib/plot/private/common/plotmetrics.rkt index b890955a..b6ace8f7 100644 --- a/plot-lib/plot/private/common/plotmetrics.rkt +++ b/plot-lib/plot/private/common/plotmetrics.rkt @@ -3,26 +3,26 @@ ;; Untyped interface / contract (module untyped racket/base (require racket/class - racket/contract - racket/match - racket/draw) + racket/contract) (provide plot-metrics<%> - plot-metrics-object/c) + plot-metrics-object/c + interface?) - (define plot-metrics<%> (interface () - get-plot-bounds - dc->plot - plot->dc - plane-vector - get-plot-metrics-functions)) + (define plot-metrics<%> + (interface () + get-plot-bounds + dc->plot + plot->dc + plane-vector + get-plot-metrics-functions)) (define plot-metrics-object/c (object/c [get-plot-bounds (->m (vectorof (vector/c real? real?)))] [plot->dc (->m (vectorof real?) (vectorof real?))] [dc->plot (->m (vectorof real?) (vectorof real?))] [plane-vector (->m (vectorof real?))] - [get-plot-metrics-functions (->m (values (-> (vectorof (vector/c real? real?))) + [get-plot-metrics-functions (->m (list/c (-> (vectorof (vector/c real? real?))) (-> (vectorof real?) (vectorof real?)) (-> (vectorof real?) (vectorof real?)) (-> (vectorof real?))))])) @@ -81,7 +81,6 @@ (define plot-metrics% (plot-metrics-mixin object%)) - (struct plot-pict pict ([bounds : (Vectorof (Vectorof Real))] [plot->dc : (-> (Vectorof Real) (Vectorof Real))] [dc->plot : (-> (Vectorof Real) (Vectorof Real))] @@ -94,4 +93,4 @@ (plot-pict (pict-draw P) (pict-width P) (pict-height P) (pict-ascent P) (pict-descent P) (pict-children P) (pict-panbox P) (pict-last P) - (bounds) ->dc ->plot (plane))) \ No newline at end of file + (bounds) ->dc ->plot (plane))) diff --git a/plot-lib/plot/private/utils-and-no-gui.rkt b/plot-lib/plot/private/utils-and-no-gui.rkt index a81b42d7..d476879f 100644 --- a/plot-lib/plot/private/utils-and-no-gui.rkt +++ b/plot-lib/plot/private/utils-and-no-gui.rkt @@ -21,9 +21,11 @@ Legend-Anchor) (require "common/plotmetrics.rkt") +(require (only-in (submod "common/plotmetrics.rkt" untyped) plot-metrics<%>)) (provide Plot-Metrics<%> + plot-metrics<%> plot-pict? Plot-Pict plot-pict-bounds diff --git a/plot-lib/plot/utils.rkt b/plot-lib/plot/utils.rkt index aa0a1aa3..9c8d1628 100644 --- a/plot-lib/plot/utils.rkt +++ b/plot-lib/plot/utils.rkt @@ -2,6 +2,7 @@ (require "private/utils-and-no-gui.rkt") (provide (all-from-out "private/utils-and-no-gui.rkt")) +(provide plot-metrics<%>) (require "private/common/contract.rkt" "private/common/leftover-contracts.rkt" diff --git a/plot-test/plot/tests/PRs/90.rkt b/plot-test/plot/tests/PRs/90.rkt index 2e421db7..9304f6f8 100644 --- a/plot-test/plot/tests/PRs/90.rkt +++ b/plot-test/plot/tests/PRs/90.rkt @@ -1,7 +1,11 @@ #lang racket -(require rackunit - racket/draw pict (only-in racket/gui/base sleep/yield) - plot (submod plot/private/common/plotmetrics untyped)) +(require pict + plot + racket/draw + (only-in racket/gui/base + sleep/yield) + rackunit) + ; tests for PR#90, https://github.com/racket/plot/pull/90 ; "Plotmetrics: access/calculate data about the plot area"