|
610 | 610 | ;; =============================================================================================== |
611 | 611 | ;; Legend |
612 | 612 |
|
613 | | - ;; the folowing functions take a (Listof legend-entry) and a Rect as argument. |
| 613 | + ;; the folowing functions take a (Listof legend-entry), a Rect and Anchor as argument. |
614 | 614 | ;; the understanding is that Rect will be the complete dc for a legend outside the plot-area |
615 | 615 | ;; and the plot-area otherwise |
616 | 616 |
|
617 | 617 | (: calculate-legend-parameters (-> (Listof legend-entry) Rect Anchor |
618 | | - (Values Rect Exact-Rational Exact-Rational Exact-Rational |
619 | | - Nonnegative-Exact-Rational Real Real |
620 | | - Nonnegative-Exact-Rational Real))) |
| 618 | + (Values Rect (Listof Exact-Rational) |
| 619 | + Nonnegative-Exact-Rational (Listof Real) (Listof Real) |
| 620 | + Nonnegative-Exact-Rational (Listof Real) |
| 621 | + Boolean Nonnegative-Integer))) |
621 | 622 | (define/private (calculate-legend-parameters legend-entries rect legend-anchor) |
622 | 623 | (define n (length legend-entries)) |
623 | 624 | (define labels (map legend-entry-label legend-entries)) |
624 | 625 | (match-define (vector (ivl x-min x-max) (ivl y-min y-max)) rect) |
625 | | - |
626 | | - (define-values (max-label-width max-label-height) |
627 | | - (for/fold ([width : Exact-Rational 0] |
628 | | - [max-height : Exact-Rational 0]) |
629 | | - ([label (in-list labels)]) |
630 | | - (define-values (w h b a) (get-text-extent label)) |
631 | | - (values (max w width) (max h max-height)))) |
632 | | - |
633 | | - (define-values (horiz-gap min-label-height baseline _1) |
634 | | - (get-text-extent " ")) |
| 626 | + (cond |
| 627 | + [(and x-min x-max y-min y-max) |
| 628 | + (define-values (cols? rows cols compact?) |
| 629 | + (match (plot-legend-layout) |
| 630 | + [(list 'rows i compact) |
| 631 | + (values #f (min n i) (ceiling (/ n i)) (equal? compact 'compact))] |
| 632 | + [(list 'columns i compact) |
| 633 | + (values #t (ceiling (/ n i)) (min n i) (equal? compact 'compact))])) |
| 634 | + (define div (if cols? rows cols)) |
| 635 | + |
| 636 | + ;; get max widths and heights per row/column |
| 637 | + (define-values (max-label-widths max-label-heights) |
| 638 | + (let-values ([(width height) |
| 639 | + (for/fold ([width : (HashTable Integer Exact-Rational) #hash()] |
| 640 | + [height : (HashTable Integer Exact-Rational) #hash()]) |
| 641 | + ([label (in-list labels)] |
| 642 | + [k (in-naturals)]) |
| 643 | + (define-values (i j) |
| 644 | + (let-values ([(i j) (quotient/remainder k div)]) |
| 645 | + (if cols? (values j i) (values i j)))) |
| 646 | + (define-values (w h b a) (get-text-extent label)) |
| 647 | + (values |
| 648 | + (hash-update width j (λ ([v : Exact-Rational]) (max w v)) (λ () 0)) |
| 649 | + (hash-update height i (λ ([v : Exact-Rational]) (max h v)) (λ () 0))))]) |
| 650 | + (define widths |
| 651 | + ((inst map Exact-Rational (Pairof Integer Exact-Rational)) |
| 652 | + cdr ((inst sort (Pairof Integer Exact-Rational)) |
| 653 | + (hash->list width) < #:key car))) |
| 654 | + (define heights |
| 655 | + ((inst map Exact-Rational (Pairof Integer Exact-Rational)) |
| 656 | + cdr ((inst sort (Pairof Integer Exact-Rational)) |
| 657 | + (hash->list height) < #:key car))) |
| 658 | + (cond |
| 659 | + [compact? (values widths heights)] |
| 660 | + [else |
| 661 | + (define max-width (apply max widths)) |
| 662 | + (define max-heights (apply max heights)) |
| 663 | + (values (map (λ (_) max-width) widths) |
| 664 | + (map (λ (_) max-heights) heights))]))) |
| 665 | + |
| 666 | + ;; different gaps |
| 667 | + (define-values (horiz-gap min-label-height baseline _1) |
| 668 | + (get-text-extent " ")) |
635 | 669 |
|
636 | | - (define top-gap baseline) |
637 | | - (define bottom-gap (* 1/2 baseline)) |
638 | | - (define baseline-skip (+ max-label-height baseline)) |
639 | | - |
640 | | - (define labels-x-size (+ max-label-width horiz-gap)) |
641 | | - |
642 | | - (define draw-y-size (max 0 (- min-label-height baseline))) |
643 | | - (define draw-x-size (* 4 draw-y-size)) |
644 | | - |
645 | | - (define legend-x-size (+ horiz-gap |
646 | | - labels-x-size (* 2 horiz-gap) |
647 | | - draw-x-size horiz-gap)) |
648 | | - (define legend-y-size (+ top-gap (* n baseline-skip) bottom-gap)) |
649 | | - |
650 | | - (define legend-x-min |
651 | | - (cond |
652 | | - [(and x-min x-max) |
| 670 | + (define top-gap baseline) |
| 671 | + (define bottom-gap (* 1/2 baseline)) |
| 672 | + (define in-label-gap (* 3 horiz-gap)) |
| 673 | + (define column-gap (* 3 in-label-gap)) |
| 674 | + |
| 675 | + ;; size of legend line/square |
| 676 | + (define draw-y-size (max 0 (- min-label-height baseline))) |
| 677 | + (define draw-x-size (* 4 draw-y-size)) |
| 678 | + |
| 679 | + ;; size of complete legend-entry |
| 680 | + (define x-skips (for/list : (Listof Exact-Rational) |
| 681 | + ([w (in-list max-label-widths)]) |
| 682 | + (+ w in-label-gap draw-x-size column-gap))) |
| 683 | + (define y-skips (for/list : (Listof Exact-Rational) |
| 684 | + ([h (in-list max-label-heights)]) |
| 685 | + (+ h baseline))) |
| 686 | + |
| 687 | + ;; size of complete legend |
| 688 | + (define legend-x-size (+ horiz-gap (- column-gap) horiz-gap |
| 689 | + (for/sum : Exact-Rational ([w (in-list x-skips)]) w))) |
| 690 | + (define legend-y-size (+ top-gap bottom-gap |
| 691 | + (for/sum : Exact-Rational ([h (in-list y-skips)]) h))) |
| 692 | + |
| 693 | + ;; top-left corner of legend |
| 694 | + (define legend-x-min |
653 | 695 | (case legend-anchor |
654 | 696 | [(top-left left bottom-left auto) x-min] |
655 | 697 | [(top-right right bottom-right) (- x-max legend-x-size)] |
656 | 698 | [(center bottom top) (- (* 1/2 (+ x-min x-max)) |
657 | | - (* 1/2 legend-x-size))])] |
658 | | - [else |
659 | | - (raise-argument-error 'draw-legend "rect-known?" 1 legend-entries rect)])) |
| 699 | + (* 1/2 legend-x-size))])) |
660 | 700 |
|
661 | | - (define legend-y-min |
662 | | - (cond |
663 | | - [(and y-min y-max) |
| 701 | + (define legend-y-min |
664 | 702 | (case legend-anchor |
665 | 703 | [(top-left top top-right auto) y-min] |
666 | 704 | [(bottom-left bottom bottom-right) (- y-max legend-y-size)] |
667 | 705 | [(center left right) (- (* 1/2 (+ y-min y-max)) |
668 | | - (* 1/2 legend-y-size))])] |
669 | | - [else |
670 | | - (raise-argument-error 'draw-legend "rect-known?" 1 legend-entries rect)])) |
671 | | - |
672 | | - (define legend-rect (vector (ivl legend-x-min (+ legend-x-min legend-x-size)) |
673 | | - (ivl legend-y-min (+ legend-y-min legend-y-size)))) |
674 | | - |
675 | | - (define label-x-min (+ legend-x-min horiz-gap)) |
676 | | - (define draw-x-min (+ legend-x-min (* 2 horiz-gap) labels-x-size horiz-gap)) |
677 | | - |
678 | | - (values legend-rect top-gap baseline-skip max-label-height |
679 | | - draw-x-size label-x-min draw-x-min |
680 | | - draw-y-size legend-y-min)) |
| 706 | + (* 1/2 legend-y-size))])) |
| 707 | + |
| 708 | + (define legend-rect (vector (ivl legend-x-min (+ legend-x-min legend-x-size)) |
| 709 | + (ivl legend-y-min (+ legend-y-min legend-y-size)))) |
| 710 | + |
| 711 | + ;; per entry x/y left/top corners |
| 712 | + (define label-x-mins (for/fold ([mins : (Listof Real) (list (+ legend-x-min horiz-gap))] |
| 713 | + [prev : Real (+ legend-x-min horiz-gap)] |
| 714 | + #:result (reverse mins)) |
| 715 | + ([x (in-list x-skips)]) |
| 716 | + (define nxt (+ prev x)) |
| 717 | + (values (cons nxt mins) nxt))) |
| 718 | + (define label-y-mins (for/fold ([mins : (Listof Real) (list (+ legend-y-min top-gap))] |
| 719 | + [prev : Real (+ legend-y-min top-gap)] |
| 720 | + #:result (reverse mins)) |
| 721 | + ([y (in-list y-skips)]) |
| 722 | + (define nxt (+ prev y)) |
| 723 | + (values (cons nxt mins) nxt))) |
| 724 | + (define draw-x-mins (for/list : (Listof Real) |
| 725 | + ([x (in-list label-x-mins)] |
| 726 | + [w (in-list max-label-widths)]) (+ x w in-label-gap))) |
| 727 | + |
| 728 | + (values legend-rect max-label-heights |
| 729 | + draw-x-size label-x-mins draw-x-mins |
| 730 | + draw-y-size label-y-mins |
| 731 | + cols? div)] |
| 732 | + [else |
| 733 | + (raise-argument-error 'draw-legend "rect-known?" 1 legend-entries rect)])) |
681 | 734 |
|
682 | 735 | (define/public (calculate-legend-rect legend-entries rect legend-anchor) |
683 | 736 | ;; Change font for correct size calculation in calculate-legend-parameters |
|
689 | 742 | (or (plot-legend-font-face) old-face) |
690 | 743 | (or (plot-legend-font-family) old-family)) |
691 | 744 |
|
692 | | - (define-values (legend-rect top-gap baseline-skip max-label-height |
693 | | - draw-x-size label-x-min draw-x-min |
694 | | - draw-y-size legend-y-min) |
| 745 | + (define-values (legend-rect max-label-heights |
| 746 | + draw-x-size label-x-mins draw-x-mins |
| 747 | + draw-y-size label-y-mins |
| 748 | + cols? div) |
695 | 749 | (calculate-legend-parameters legend-entries rect legend-anchor)) |
696 | 750 |
|
697 | 751 | ;; Undo change font |
|
701 | 755 |
|
702 | 756 | (define/public (draw-legend legend-entries rect) |
703 | 757 | (define legend-anchor (plot-legend-anchor)) |
704 | | - (when legend-anchor |
| 758 | + (when (not (eq? legend-anchor 'no-legend)) |
705 | 759 | (match-define (list (legend-entry #{labels : (Listof (U String pict))} |
706 | 760 | #{draw-procs : (Listof Legend-Draw-Proc)}) |
707 | 761 | ...) |
|
716 | 770 | (or (plot-legend-font-face) old-face) |
717 | 771 | (or (plot-legend-font-family) old-family)) |
718 | 772 |
|
719 | | - (define-values (legend-rect top-gap baseline-skip max-label-height |
720 | | - draw-x-size label-x-min draw-x-min |
721 | | - draw-y-size legend-y-min) |
| 773 | + (define-values (legend-rect max-label-heights |
| 774 | + draw-x-size label-x-mins draw-x-mins |
| 775 | + draw-y-size label-y-mins |
| 776 | + cols? div) |
722 | 777 | (calculate-legend-parameters legend-entries rect (legend-anchor->anchor legend-anchor))) |
723 | 778 |
|
724 | 779 | ;; legend background |
|
735 | 790 |
|
736 | 791 | (set-alpha (plot-foreground-alpha)) |
737 | 792 | (set-clipping-rect legend-rect) |
738 | | - (for ([label (in-list labels)] [draw-proc (in-list draw-procs)] [i (in-naturals)]) |
| 793 | + (for ([label (in-list labels)] [draw-proc (in-list draw-procs)] [k (in-naturals)]) |
| 794 | + (define-values (i j) |
| 795 | + (let-values ([(i j) (quotient/remainder k div)]) |
| 796 | + (if cols? (values j i) (values i j)))) |
| 797 | + |
739 | 798 | (define-values (_1 label-height _2 _3) (get-text-extent label)) |
740 | | - (define legend-entry-y-min (+ legend-y-min top-gap (* i baseline-skip))) |
741 | | - (define label-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height label-height)))) |
| 799 | + (define label-x-min (list-ref label-x-mins j)) |
| 800 | + (define legend-entry-y-min (list-ref label-y-mins i)) |
| 801 | + (define max-label-height (list-ref max-label-heights i)) |
| 802 | + (define label-y-min (+ legend-entry-y-min |
| 803 | + (* 1/2 (- max-label-height label-height)))) |
| 804 | + |
742 | 805 | (if (pict? label) |
743 | | - (draw-pict label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0) |
744 | | - (draw-text label (vector (ann label-x-min Real) (ann label-y-min Real)) 'top-left 0 0 #t)) |
| 806 | + (draw-pict label (vector label-x-min label-y-min) 'top-left 0) |
| 807 | + (draw-text label (vector label-x-min label-y-min) 'top-left 0 0 #t)) |
745 | 808 |
|
746 | 809 | (define draw-y-min (+ legend-entry-y-min (* 1/2 (- max-label-height draw-y-size)))) |
| 810 | + (define draw-x-min (list-ref draw-x-mins j)) |
747 | 811 |
|
748 | 812 | (define entry-pd (make-object plot-device% dc draw-x-min draw-y-min draw-x-size draw-y-size)) |
749 | 813 | (send entry-pd reset-drawing-params #f) |
|
0 commit comments