diff --git a/plot-compat/plot/private/compat.rkt b/plot-compat/plot/private/compat.rkt index 188c7d2c..03f02833 100644 --- a/plot-compat/plot/private/compat.rkt +++ b/plot-compat/plot/private/compat.rkt @@ -165,7 +165,7 @@ (define bm (make-bitmap (ceiling width) (ceiling height))) (define dc (make-object bitmap-dc% bm)) (define area (make-object 2d-plot-area% - bounds-rect x-ticks x-ticks y-ticks y-ticks dc 0 0 width height)) + bounds-rect x-ticks x-ticks y-ticks y-ticks '() dc 0 0 width height)) (define data+axes (mix x-axis-data y-axis-data data)) @@ -214,7 +214,7 @@ (define bm (make-bitmap (ceiling width) (ceiling height))) (define dc (make-object bitmap-dc% bm)) (define area (make-object 3d-plot-area% - bounds-rect x-ticks x-ticks y-ticks y-ticks z-ticks z-ticks dc 0 0 width height)) + bounds-rect x-ticks x-ticks y-ticks y-ticks z-ticks z-ticks '() dc 0 0 width height)) (send area start-plot) (send area start-renderer bounds-rect) diff --git a/plot-gui-lib/plot/private/gui/plot2d.rkt b/plot-gui-lib/plot/private/gui/plot2d.rkt index 88344f6c..eb2b1125 100644 --- a/plot-gui-lib/plot/private/gui/plot2d.rkt +++ b/plot-gui-lib/plot/private/gui/plot2d.rkt @@ -50,7 +50,7 @@ plot) (require/typed plot/utils - (anchor/c (-> Any Boolean)) + (legend-anchor/c (-> Any Boolean)) (plot-color/c (-> Any Boolean)) (plot-file-format/c (-> Any Boolean))) @@ -66,7 +66,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Snip%))) (define (plot-snip renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -89,7 +89,7 @@ [(and title (not (or (string? title) (pict? title)))) (fail/kw "#f, string or pict" '#:title title)] [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)]) + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)]) (parameterize ([plot-title title] [plot-x-label x-label] @@ -112,9 +112,10 @@ (define dc (make-object bitmap-dc% bm)) (define-values (x-ticks x-far-ticks y-ticks y-far-ticks) (get-ticks renderer-list bounds-rect)) + (define legend (get-legend-entry-list renderer-list bounds-rect)) (define new-area (make-object 2d-plot-area% - bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks + bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks legend dc 0 0 width height)) (set! area new-area) (plot-area new-area renderer-list)) @@ -136,7 +137,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Frame%))) (define (plot-frame renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -159,7 +160,7 @@ [(and title (not (or (string? title) (pict? title)))) (fail/kw "#f, string or pict" '#:title title)] [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)]) + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)]) ;; make-snip will be called in a separate thread, make sure the ;; parameters have the correct values in that thread as well. @@ -185,7 +186,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor + #:legend-anchor Legend-Anchor #:out-file (U Path-String Output-Port #f) #:out-kind (U 'auto Image-File-Format) #:fgcolor Plot-Color @@ -225,7 +226,7 @@ [(and title (not (or (string? title) (pict? title)))) (fail/kw "#f, string or pict" '#:title title)] [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)] + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)] [(and out-kind (not (plot-file-format/c out-kind))) (fail/kw "plot-file-format/c" '#:out-kind out-kind)] [(not (plot-file-format/c out-kind)) (fail/kw "plot-file-format/c" '#:out-kind out-kind)] [(and fgcolor (not (plot-color/c fgcolor))) (fail/kw "plot-color/c" '#:fgcolor fgcolor)] diff --git a/plot-gui-lib/plot/private/gui/plot3d.rkt b/plot-gui-lib/plot/private/gui/plot3d.rkt index 1f8c7c14..1c2494d6 100644 --- a/plot-gui-lib/plot/private/gui/plot3d.rkt +++ b/plot-gui-lib/plot/private/gui/plot3d.rkt @@ -25,7 +25,7 @@ plot3d) (require/typed plot/utils - (anchor/c (-> Any Boolean)) + (legend-anchor/c (-> Any Boolean)) (plot-color/c (-> Any Boolean)) (plot-file-format/c (-> Any Boolean))) @@ -44,7 +44,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Snip%))) (define (plot3d-snip renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -76,7 +76,7 @@ [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] [(and z-label (not (or (string? z-label) (pict? z-label)))) (fail/kw "#f, string or pict" '#:y-label z-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)]) + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)]) (parameterize ([plot-title title] [plot-x-label x-label] @@ -90,7 +90,8 @@ (get-ticks renderer-list bounds-rect)) (define render-tasks-hash ((inst make-hash Boolean render-tasks))) - (define legend-entries-hash ((inst make-hash Boolean (Listof legend-entry)))) + ;; For 3D legend can be calculated once since we don't change the bounding box + (define legend (get-legend-entry-list renderer-list bounds-rect)) (: make-bm (-> Boolean Real Real Positive-Integer Positive-Integer (Instance Bitmap%))) (define (make-bm anim? angle altitude width height) @@ -102,35 +103,26 @@ width height #t #:backing-scale (or (get-display-backing-scale) 1.0))) (define dc (make-object bitmap-dc% bm)) + (define area (make-object 3d-plot-area% - bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks z-ticks z-far-ticks + bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks z-ticks z-far-ticks legend dc 0 0 width height)) (send area start-plot) (cond [(not (hash-ref render-tasks-hash (plot-animating?) #f)) - (hash-set! - legend-entries-hash (plot-animating?) - (flatten-legend-entries - (for/list : (Listof (Treeof legend-entry)) ([rend (in-list renderer-list)]) - (match-define (renderer3d rend-bounds-rect _bf _tf label-proc render-proc) rend) + (for ([rend (in-list renderer-list)]) + (match-define (renderer3d rend-bounds-rect _bf _tf label-proc render-proc) rend) + (when render-proc (send area start-renderer (if rend-bounds-rect (rect-inexact->exact rend-bounds-rect) (unknown-rect 3))) - (when render-proc (render-proc area)) - (if label-proc - (label-proc (send area get-bounds-rect)) - empty)))) + (render-proc area))) (hash-set! render-tasks-hash (plot-animating?) (send area get-render-tasks))] [else (send area set-render-tasks (hash-ref render-tasks-hash (plot-animating?)))]) (send area end-renderers) - - (define legend-entries (hash-ref legend-entries-hash (plot-animating?) #f)) - (when (and legend-entries (not (empty? legend-entries))) - (send area draw-legend legend-entries)) - (send area end-plot) bm)) @@ -153,7 +145,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Frame%))) (define (plot3d-frame renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -185,7 +177,7 @@ [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] [(and z-label (not (or (string? z-label) (pict? z-label)))) (fail/kw "#f, string or pict" '#:y-label z-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)]) + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)]) ;; make-snip will be called in a separate thread, make sure the ;; parameters have the correct values in that thread as well. @@ -216,7 +208,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor + #:legend-anchor Legend-Anchor #:out-file (U Path-String Output-Port #f) #:out-kind (U 'auto Image-File-Format) #:fgcolor Plot-Color @@ -272,7 +264,7 @@ [(and x-label (not (or (string? x-label) (pict? x-label)))) (fail/kw "#f, string or pict" '#:x-label x-label)] [(and y-label (not (or (string? y-label) (pict? y-label)))) (fail/kw "#f, string or pict" '#:y-label y-label)] [(and z-label (not (or (string? z-label) (pict? z-label)))) (fail/kw "#f, string or pict" '#:y-label z-label)] - [(not (anchor/c legend-anchor)) (fail/kw "anchor/c" '#:legend-anchor legend-anchor)] + [(not (legend-anchor/c legend-anchor)) (fail/kw "legend-anchor/c" '#:legend-anchor legend-anchor)] [(and out-kind (not (plot-file-format/c out-kind))) (fail/kw "plot-file-format/c" '#:out-kind out-kind)] [(not (plot-file-format/c out-kind)) (fail/kw "plot-file-format/c" '#:out-kind out-kind)] [(and fgcolor (not (plot-color/c fgcolor))) (fail/kw "plot-color/c" '#:fgcolor fgcolor)] diff --git a/plot-lib/plot/private/common/contract.rkt b/plot-lib/plot/private/common/contract.rkt index be401802..d814c0e6 100644 --- a/plot-lib/plot/private/common/contract.rkt +++ b/plot-lib/plot/private/common/contract.rkt @@ -34,6 +34,8 @@ (define anchor/c (one-of/c 'top-left 'top 'top-right 'left 'center 'right 'bottom-left 'bottom 'bottom-right)) +(define legend-anchor/c (or/c #f anchor/c + (list/c (one-of/c 'inside 'outside) anchor/c))) (define color/c (or/c (list/c real? real? real?) string? symbol? diff --git a/plot-lib/plot/private/common/parameter-groups.rkt b/plot-lib/plot/private/common/parameter-groups.rkt index e0877ccb..8713fa17 100644 --- a/plot-lib/plot/private/common/parameter-groups.rkt +++ b/plot-lib/plot/private/common/parameter-groups.rkt @@ -100,7 +100,7 @@ (U False Nonnegative-Real) (U False String) (U False Font-Family) - Anchor + Legend-Anchor Nonnegative-Real (List Boolean Boolean Boolean Boolean Boolean Boolean) (List Boolean Anchor Real (U Boolean 'auto) Anchor Real Boolean Anchor Real (U Boolean 'auto) Anchor Real) diff --git a/plot-lib/plot/private/common/parameters.rkt b/plot-lib/plot/private/common/parameters.rkt index dabef470..7c0eb3f8 100644 --- a/plot-lib/plot/private/common/parameters.rkt +++ b/plot-lib/plot/private/common/parameters.rkt @@ -75,7 +75,7 @@ (defparam plot-legend-font-size size (U Nonnegative-Real #f) #f) (defparam plot-legend-font-face face (U String #f) #f) (defparam plot-legend-font-family family (U Font-Family #f) #f) -(defparam plot-legend-anchor anchor Anchor 'top-left) +(defparam plot-legend-anchor anchor Legend-Anchor 'top-left) (defparam2 plot-legend-box-alpha alpha Real Nonnegative-Real 2/3 (unit-ivl 'plot-legend-box-alpha)) (defparam plot-animating? Boolean #f) diff --git a/plot-lib/plot/private/common/plot-device.rkt b/plot-lib/plot/private/common/plot-device.rkt index ecfff19f..ba3c4399 100644 --- a/plot-lib/plot/private/common/plot-device.rkt +++ b/plot-lib/plot/private/common/plot-device.rkt @@ -614,11 +614,11 @@ ;; the understanding is that Rect will be the complete dc for a legend outside the plot-area ;; and the plot-area otherwise - (: calculate-legend-parameters (-> (Listof legend-entry) Rect + (: calculate-legend-parameters (-> (Listof legend-entry) Rect Anchor (Values Rect Exact-Rational Exact-Rational Exact-Rational Nonnegative-Exact-Rational Real Real Nonnegative-Exact-Rational Real))) - (define/private (calculate-legend-parameters [legend-entries : (Listof legend-entry)][rect : Rect]) + (define/private (calculate-legend-parameters legend-entries rect legend-anchor) (define n (length legend-entries)) (define labels (map legend-entry-label legend-entries)) (match-define (vector (ivl x-min x-max) (ivl y-min y-max)) rect) @@ -650,7 +650,7 @@ (define legend-x-min (cond [(and x-min x-max) - (case (plot-legend-anchor) + (case legend-anchor [(top-left left bottom-left auto) x-min] [(top-right right bottom-right) (- x-max legend-x-size)] [(center bottom top) (- (* 1/2 (+ x-min x-max)) @@ -661,7 +661,7 @@ (define legend-y-min (cond [(and y-min y-max) - (case (plot-legend-anchor) + (case legend-anchor [(top-left top top-right auto) y-min] [(bottom-left bottom bottom-right) (- y-max legend-y-size)] [(center left right) (- (* 1/2 (+ y-min y-max)) @@ -679,7 +679,7 @@ draw-x-size label-x-min draw-x-min draw-y-size legend-y-min)) - (define/public (calculate-legend-rect legend-entries rect) + (define/public (calculate-legend-rect legend-entries rect legend-anchor) ;; Change font for correct size calculation in calculate-legend-parameters (define old-size (send (send dc get-font) get-point-size)) (define old-face (send (send dc get-font) get-face)) @@ -692,7 +692,7 @@ (define-values (legend-rect top-gap baseline-skip max-label-height draw-x-size label-x-min draw-x-min draw-y-size legend-y-min) - (calculate-legend-parameters legend-entries rect)) + (calculate-legend-parameters legend-entries rect legend-anchor)) ;; Undo change font (set-font-attribs old-size old-face old-family) @@ -700,56 +700,58 @@ legend-rect) (define/public (draw-legend legend-entries rect) - (match-define (list (legend-entry #{labels : (Listof (U String pict))} - #{draw-procs : (Listof Legend-Draw-Proc)}) - ...) - legend-entries) - - ;; Change font early for correct size calculation in calculate-legend-parameters - (define old-size (send (send dc get-font) get-point-size)) - (define old-face (send (send dc get-font) get-face)) - (define old-family (send (send dc get-font) get-family)) - (set-font-attribs - (or (plot-legend-font-size) old-size) - (or (plot-legend-font-face) old-face) - (or (plot-legend-font-family) old-family)) - - (define-values (legend-rect top-gap baseline-skip max-label-height - draw-x-size label-x-min draw-x-min - draw-y-size legend-y-min) - (calculate-legend-parameters legend-entries rect)) - - ;; legend background - (set-pen (plot-foreground) 1 'transparent) - (set-brush (plot-background) 'solid) - (set-alpha (plot-legend-box-alpha)) - (draw-rect legend-rect) - - ;; legend border - (set-minor-pen) - (set-brush (plot-background) 'transparent) - (set-alpha 3/4) - (draw-rect legend-rect) - - (set-alpha (plot-foreground-alpha)) - (set-clipping-rect legend-rect) - (for ([label (in-list labels)] [draw-proc (in-list draw-procs)] [i (in-naturals)]) - (define-values (_1 label-height _2 _3) (get-text-extent label)) - (define legend-entry-y-min (+ legend-y-min top-gap (* i baseline-skip))) - (define label-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height label-height)))) - (if (pict? label) - (draw-pict label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0) - (draw-text label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0 0 #t)) - - (define draw-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height draw-y-size)))) - - (define entry-pd (make-object plot-device% dc draw-x-min draw-y-min draw-x-size draw-y-size)) - (send entry-pd reset-drawing-params #f) - (draw-proc this draw-x-size draw-y-size) - (send entry-pd restore-drawing-params)) - - ;; reset plot font attributes - (set-font-attribs old-size old-face old-family) - - (clear-clipping-rect)) - )) ; end class + (define legend-anchor (plot-legend-anchor)) + (when legend-anchor + (match-define (list (legend-entry #{labels : (Listof (U String pict))} + #{draw-procs : (Listof Legend-Draw-Proc)}) + ...) + legend-entries) + + ;; Change font early for correct size calculation in calculate-legend-parameters + (define old-size (send (send dc get-font) get-point-size)) + (define old-face (send (send dc get-font) get-face)) + (define old-family (send (send dc get-font) get-family)) + (set-font-attribs + (or (plot-legend-font-size) old-size) + (or (plot-legend-font-face) old-face) + (or (plot-legend-font-family) old-family)) + + (define-values (legend-rect top-gap baseline-skip max-label-height + draw-x-size label-x-min draw-x-min + draw-y-size legend-y-min) + (calculate-legend-parameters legend-entries rect (if (list? legend-anchor) (cadr legend-anchor) legend-anchor))) + + ;; legend background + (set-pen (plot-foreground) 1 'transparent) + (set-brush (plot-background) 'solid) + (set-alpha (plot-legend-box-alpha)) + (draw-rect legend-rect) + + ;; legend border + (set-minor-pen) + (set-brush (plot-background) 'transparent) + (set-alpha 3/4) + (draw-rect legend-rect) + + (set-alpha (plot-foreground-alpha)) + (set-clipping-rect legend-rect) + (for ([label (in-list labels)] [draw-proc (in-list draw-procs)] [i (in-naturals)]) + (define-values (_1 label-height _2 _3) (get-text-extent label)) + (define legend-entry-y-min (+ legend-y-min top-gap (* i baseline-skip))) + (define label-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height label-height)))) + (if (pict? label) + (draw-pict label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0) + (draw-text label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0 0 #t)) + + (define draw-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height draw-y-size)))) + + (define entry-pd (make-object plot-device% dc draw-x-min draw-y-min draw-x-size draw-y-size)) + (send entry-pd reset-drawing-params #f) + (draw-proc this draw-x-size draw-y-size) + (send entry-pd restore-drawing-params)) + + ;; reset plot font attributes + (set-font-attribs old-size old-face old-family) + + (clear-clipping-rect)) + ))) ; end class diff --git a/plot-lib/plot/private/common/types.rkt b/plot-lib/plot/private/common/types.rkt index 38fde3e9..0b4bae6d 100644 --- a/plot-lib/plot/private/common/types.rkt +++ b/plot-lib/plot/private/common/types.rkt @@ -17,6 +17,7 @@ 'left 'center 'right 'bottom-left 'bottom 'bottom-right 'auto)) +(define-predicate anchor? Anchor) (deftype Color (U (List Real Real Real) @@ -92,6 +93,15 @@ (struct legend-entry ([label : (U String pict)] [draw : Legend-Draw-Proc]) #:transparent) +(deftype Legend-Anchor (U #f Anchor (List (U 'inside 'outside) Anchor))) +(define (inside-anchor [a : Legend-Anchor]) + (and a + (if (list? a) + (and (eq? (car a) 'inside) (cadr a)) + a))) +(define (outside-anchor [a : Legend-Anchor]) + (and a (list? a) (eq? (car a) 'outside) (cadr a))) + (define-type Plot-Device% (Class (init-field [dc (Instance DC<%>)] @@ -133,5 +143,5 @@ [draw-arrow-glyph (-> (Vectorof Real) Real Real Void)] [draw-glyphs (-> (Listof (Vectorof Real)) Point-Sym Nonnegative-Real Void)] [draw-pict (->* [pict (Vectorof Real)] (Anchor Real) Void)] - [calculate-legend-rect (-> (Listof legend-entry) Rect Rect)] + [calculate-legend-rect (-> (Listof legend-entry) Rect Anchor Rect)] [draw-legend (-> (Listof legend-entry) Rect Void)])) diff --git a/plot-lib/plot/private/no-gui/plot2d-untyped.rkt b/plot-lib/plot/private/no-gui/plot2d-untyped.rkt index 95c62323..dfa8aede 100644 --- a/plot-lib/plot/private/no-gui/plot2d-untyped.rkt +++ b/plot-lib/plot/private/no-gui/plot2d-untyped.rkt @@ -26,7 +26,7 @@ #:title (or/c string? pict? #f) #:x-label (or/c string? pict? #f) #:y-label (or/c string? pict? #f) - #:legend-anchor anchor/c] + #:legend-anchor legend-anchor/c] void?)] [untyped-plot-bitmap (->* [(treeof (or/c renderer2d? nonrenderer?))] @@ -39,7 +39,7 @@ #:width (or/c real? #f) #:x-label (or/c string? pict? #f) #:y-label (or/c string? pict? #f) - #:legend-anchor anchor/c] + #:legend-anchor legend-anchor/c] (is-a?/c bitmap%))] [untyped-plot-pict (->* [(treeof (or/c renderer2d? nonrenderer?))] @@ -52,7 +52,7 @@ #:height (or/c real? #f) #:width (or/c real? #f) #:y-label (or/c string? pict? #f) - #:legend-anchor anchor/c] + #:legend-anchor legend-anchor/c] pict?)])) (define untyped-plot/dc plot/dc) diff --git a/plot-lib/plot/private/no-gui/plot2d-utils.rkt b/plot-lib/plot/private/no-gui/plot2d-utils.rkt index 52431e6d..10fae833 100644 --- a/plot-lib/plot/private/no-gui/plot2d-utils.rkt +++ b/plot-lib/plot/private/no-gui/plot2d-utils.rkt @@ -12,7 +12,7 @@ "utils.rkt" typed/racket/unsafe) -(provide get-renderer-list get-bounds-rect get-ticks) +(provide get-renderer-list get-bounds-rect get-ticks get-legend-entry-list) (unsafe-provide plot-area) (: get-renderer-list (-> Any (Listof renderer2d))) @@ -68,23 +68,29 @@ (define (plot-area area renderer-list) (send area start-plot) - (define legend-entries - (flatten-legend-entries - (for/list : (Listof (Treeof legend-entry)) ([rend (in-list renderer-list)]) - (match-define (renderer2d rend-bounds-rect _bf _tf label-proc render-proc) rend) - (send area start-renderer (if rend-bounds-rect - (rect-inexact->exact rend-bounds-rect) - (unknown-rect 2))) - (when render-proc (render-proc area)) - (cond - [(and render-proc label-proc) - (define rect (send area get-bounds-rect)) - (label-proc rect)] - [else empty])))) + (for ([rend (in-list renderer-list)]) + (match-define (renderer2d rend-bounds-rect _bf _tf label-proc render-proc) rend) + ;; next step could be moved into (when render-proc, but this generates different step files) + (send area start-renderer (if rend-bounds-rect + (rect-inexact->exact rend-bounds-rect) + (unknown-rect 2))) + (when render-proc + (render-proc area))) (send area end-renderers) - - (when (not (empty? legend-entries)) - (send area draw-legend legend-entries)) - (send area end-plot)) + +(: get-legend-entry-list (-> (Listof renderer2d) Rect (Listof legend-entry))) +(define (get-legend-entry-list renderer-list outer-rect) + (match-define (vector (ivl x-min x-max) (ivl y-min y-max)) outer-rect) + + (cond + [(and x-min x-max y-min y-max) + (flatten-legend-entries + (for*/list : (Listof (Treeof legend-entry)) + ([rend (in-list renderer-list)] + [label-proc (in-value (renderer2d-label rend))] + #:when label-proc) + ;; clipping not necessary, levels are always calculated on the complete bounds-rect + (label-proc outer-rect)))] + [else '()])) diff --git a/plot-lib/plot/private/no-gui/plot2d.rkt b/plot-lib/plot/private/no-gui/plot2d.rkt index 47c0592d..4180335f 100644 --- a/plot-lib/plot/private/no-gui/plot2d.rkt +++ b/plot-lib/plot/private/no-gui/plot2d.rkt @@ -35,7 +35,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Void)) (define (plot/dc renderer-tree dc x y width height #:x-min [x-min #f] #:x-max [x-max #f] @@ -60,13 +60,14 @@ (define bounds-rect (get-bounds-rect renderer-list x-min x-max y-min y-max)) (define-values (x-ticks x-far-ticks y-ticks y-far-ticks) (get-ticks renderer-list bounds-rect)) + (define legend (get-legend-entry-list renderer-list bounds-rect)) (parameterize ([plot-title title] [plot-x-label x-label] [plot-y-label y-label] [plot-legend-anchor legend-anchor]) (define area (make-object 2d-plot-area% - bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks dc x y width height)) + bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks legend dc x y width height)) (plot-area area renderer-list))])) ;; =================================================================================================== @@ -81,7 +82,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Bitmap%))) (define (plot-bitmap renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -111,7 +112,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Pict)) (define (plot-pict renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -144,7 +145,7 @@ #:title (U String pict #f) #:x-label (U String pict #f) #:y-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Void)) (define (plot-file renderer-tree output [kind 'auto] #:x-min [x-min #f] #:x-max [x-max #f] diff --git a/plot-lib/plot/private/no-gui/plot3d-untyped.rkt b/plot-lib/plot/private/no-gui/plot3d-untyped.rkt index 8b16e7a1..77bd5d17 100644 --- a/plot-lib/plot/private/no-gui/plot3d-untyped.rkt +++ b/plot-lib/plot/private/no-gui/plot3d-untyped.rkt @@ -32,7 +32,7 @@ #:x-label (or/c string? pict? #f) #:y-label (or/c string? pict? #f) #:z-label (or/c string? pict? #f) - #:legend-anchor anchor/c] + #:legend-anchor legend-anchor/c] void?)])) (define-syntax untyped-plot3d/dc diff --git a/plot-lib/plot/private/no-gui/plot3d-utils.rkt b/plot-lib/plot/private/no-gui/plot3d-utils.rkt index 8b89b4de..6e39175b 100644 --- a/plot-lib/plot/private/no-gui/plot3d-utils.rkt +++ b/plot-lib/plot/private/no-gui/plot3d-utils.rkt @@ -12,7 +12,7 @@ "utils.rkt" typed/racket/unsafe) -(provide get-renderer-list get-bounds-rect get-ticks) +(provide get-renderer-list get-bounds-rect get-ticks get-legend-entry-list) (unsafe-provide plot-area) (: get-renderer-list (-> Any (Listof renderer3d))) @@ -78,21 +78,28 @@ (define (plot-area area renderer-list) (send area start-plot) - (define legend-entries - (flatten-legend-entries - (for/list : (Listof (Treeof legend-entry)) ([rend (in-list renderer-list)]) - (match-define (renderer3d rend-bounds-rect _bf _tf label-proc render-proc) rend) - (send area start-renderer (if rend-bounds-rect - (rect-inexact->exact rend-bounds-rect) - (unknown-rect 3))) - (when render-proc (render-proc area)) - (if label-proc - (label-proc (send area get-bounds-rect)) - empty)))) + (for ([rend (in-list renderer-list)]) + (match-define (renderer3d rend-bounds-rect _bf _tf label-proc render-proc) rend) + ;; next step could be moved into (when render-proc, but this generates different step files) + (send area start-renderer (if rend-bounds-rect + (rect-inexact->exact rend-bounds-rect) + (unknown-rect 3))) + (when render-proc + (render-proc area))) (send area end-renderers) - - (when (not (empty? legend-entries)) - (send area draw-legend legend-entries)) - (send area end-plot)) + +(: get-legend-entry-list (-> (Listof renderer3d) Rect (Listof legend-entry))) +(define (get-legend-entry-list renderer-list outer-rect) + (match-define (vector (ivl x-min x-max) (ivl y-min y-max) (ivl z-min z-max)) outer-rect) + + (cond + [(and x-min x-max y-min y-max z-min z-max) + (flatten-legend-entries + (for*/list : (Listof (Treeof legend-entry)) + ([rend (in-list renderer-list)] + [label-proc (in-value (renderer3d-label rend))] + #:when label-proc) + (label-proc outer-rect)))] + [else '()])) diff --git a/plot-lib/plot/private/no-gui/plot3d.rkt b/plot-lib/plot/private/no-gui/plot3d.rkt index ad3bf441..3d7363c4 100644 --- a/plot-lib/plot/private/no-gui/plot3d.rkt +++ b/plot-lib/plot/private/no-gui/plot3d.rkt @@ -40,7 +40,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Void)) (define (plot3d/dc renderer-tree dc x y width height #:x-min [x-min #f] #:x-max [x-max #f] @@ -73,6 +73,7 @@ (define bounds-rect (get-bounds-rect renderer-list x-min x-max y-min y-max z-min z-max)) (define-values (x-ticks x-far-ticks y-ticks y-far-ticks z-ticks z-far-ticks) (get-ticks renderer-list bounds-rect)) + (define legend-list (get-legend-entry-list renderer-list bounds-rect)) (parameterize ([plot3d-angle angle] [plot3d-altitude altitude] @@ -83,6 +84,7 @@ [plot-legend-anchor legend-anchor]) (define area (make-object 3d-plot-area% bounds-rect x-ticks x-far-ticks y-ticks y-far-ticks z-ticks z-far-ticks + legend-list dc x y width height)) (plot-area area renderer-list))])) @@ -107,7 +109,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] (Instance Bitmap%))) (define (plot3d-bitmap renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -145,7 +147,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Pict)) (define (plot3d-pict renderer-tree #:x-min [x-min #f] #:x-max [x-max #f] @@ -186,7 +188,7 @@ #:x-label (U String pict #f) #:y-label (U String pict #f) #:z-label (U String pict #f) - #:legend-anchor Anchor] + #:legend-anchor Legend-Anchor] Void)) (define (plot3d-file renderer-tree output [kind 'auto] #:x-min [x-min #f] #:x-max [x-max #f] diff --git a/plot-lib/plot/private/plot2d/plot-area.rkt b/plot-lib/plot/private/plot2d/plot-area.rkt index 18202772..91e96d18 100644 --- a/plot-lib/plot/private/plot2d/plot-area.rkt +++ b/plot-lib/plot/private/plot2d/plot-area.rkt @@ -26,6 +26,7 @@ [rx-far-ticks (Listof tick)] [ry-ticks (Listof tick)] [ry-far-ticks (Listof tick)] + [legend (Listof legend-entry)] [dc (Instance DC<%>)] [dc-x-min Real] [dc-y-min Real] @@ -72,7 +73,7 @@ (: 2d-plot-area% 2D-Plot-Area%) (define 2d-plot-area% (class object% - (init-field bounds-rect rx-ticks rx-far-ticks ry-ticks ry-far-ticks) + (init-field bounds-rect rx-ticks rx-far-ticks ry-ticks ry-far-ticks legend) (init-field dc dc-x-min dc-y-min dc-x-size dc-y-size) (super-new) @@ -657,6 +658,8 @@ (define/public (start-plot) (send pd reset-drawing-params) (send pd clear) + (when (and (not (empty? legend)) (outside-anchor (plot-legend-anchor))) + (draw-legend legend)) (draw-title) (draw-axes) (draw-ticks) @@ -672,13 +675,20 @@ (define/public (end-renderers) (clear-clip-rect) - (send pd reset-drawing-params)) + (send pd reset-drawing-params) + (when (and (not (empty? legend)) (inside-anchor (plot-legend-anchor))) + (draw-legend legend))) (define/public (draw-legend legend-entries) (define gap-size (+ (pen-gap) tick-radius)) + (define-values (x-min x-max y-min y-max) + (if (outside-anchor (plot-legend-anchor)) + (values dc-x-min (+ dc-x-min dc-x-size) + dc-y-min (+ dc-y-min dc-y-size)) + (values area-x-min area-x-max area-y-min area-y-max))) (send pd draw-legend legend-entries - (vector (ivl (+ area-x-min gap-size) (- area-x-max gap-size)) - (ivl (+ area-y-min gap-size) (- area-y-max gap-size))))) + (vector (ivl (+ x-min gap-size) (- x-max gap-size)) + (ivl (+ y-min gap-size) (- y-max gap-size))))) (define/public (end-plot) (send pd restore-drawing-params)) diff --git a/plot-lib/plot/private/plot3d/plot-area.rkt b/plot-lib/plot/private/plot3d/plot-area.rkt index c6b6f85f..98647862 100644 --- a/plot-lib/plot/private/plot3d/plot-area.rkt +++ b/plot-lib/plot/private/plot3d/plot-area.rkt @@ -97,7 +97,8 @@ [ry-ticks (Listof tick)] [ry-far-ticks (Listof tick)] [rz-ticks (Listof tick)] - [rz-far-ticks (Listof tick)]) + [rz-far-ticks (Listof tick)] + [legend (Listof legend-entry)]) (init-field [dc (Instance DC<%>)] [dc-x-min Real] [dc-y-min Real] @@ -145,7 +146,7 @@ (: 3d-plot-area% 3D-Plot-Area%) (define 3d-plot-area% (class object% - (init-field bounds-rect rx-ticks rx-far-ticks ry-ticks ry-far-ticks rz-ticks rz-far-ticks) + (init-field bounds-rect rx-ticks rx-far-ticks ry-ticks ry-far-ticks rz-ticks rz-far-ticks legend) (init-field dc dc-x-min dc-y-min dc-x-size dc-y-size) (super-new) @@ -1256,6 +1257,8 @@ (define/public (start-plot) (send pd reset-drawing-params) (send pd clear) + (when (and (not (empty? legend)) (outside-anchor (plot-legend-anchor))) + (draw-legend legend)) (draw-title) (draw-labels (get-back-label-params)) (draw-ticks (get-back-tick-params)) @@ -1275,13 +1278,20 @@ (send pd reset-drawing-params) (draw-front-axes) (draw-ticks (get-front-tick-params)) - (draw-labels (get-front-label-params))) + (draw-labels (get-front-label-params)) + (when (and (not (empty? legend)) (inside-anchor (plot-legend-anchor))) + (draw-legend legend))) (define/public (draw-legend legend-entries) (define gap-size (+ (pen-gap) tick-radius)) + (define-values (x-min x-max y-min y-max) + (if (outside-anchor (plot-legend-anchor)) + (values dc-x-min (+ dc-x-min dc-x-size) + dc-y-min (+ dc-y-min dc-y-size)) + (values area-x-min area-x-max area-y-min area-y-max))) (send pd draw-legend legend-entries - (vector (ivl (+ area-x-min gap-size) (- area-x-max gap-size)) - (ivl (+ area-y-min gap-size) (- area-y-max gap-size))))) + (vector (ivl (+ x-min gap-size) (- x-max gap-size)) + (ivl (+ y-min gap-size) (- y-max gap-size))))) (define/public (end-plot) (send pd restore-drawing-params)) diff --git a/plot-lib/plot/private/utils-and-no-gui.rkt b/plot-lib/plot/private/utils-and-no-gui.rkt index d2a44b07..e4253af2 100644 --- a/plot-lib/plot/private/utils-and-no-gui.rkt +++ b/plot-lib/plot/private/utils-and-no-gui.rkt @@ -17,7 +17,8 @@ Alphas Labels Contour-Levels - Image-File-Format) + Image-File-Format + Legend-Anchor) (require "common/math.rkt")