File: arrows.rkt

package info (click to toggle)
racket 7.9%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 178,684 kB
  • sloc: ansic: 282,112; lisp: 234,887; pascal: 70,954; sh: 27,112; asm: 16,268; makefile: 4,613; cpp: 2,715; ada: 1,681; javascript: 1,244; cs: 879; exp: 499; csh: 422; python: 274; xml: 106; perl: 104
file content (142 lines) | stat: -rw-r--r-- 6,327 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#lang typed/racket/base

(require typed/racket/class racket/match racket/math racket/list racket/sequence
         plot/utils
         (only-in typed/pict pict)
         "../common/type-doc.rkt"
         "../common/utils.rkt")

(provide arrows-render-fun
         arrows)

;; ===================================================================================================
;; Arrows

(: arrows-render-fun
   (-> (Listof (Pair (Vectorof Real) (Vectorof Real)))
       Plot-Color Nonnegative-Real Plot-Pen-Style
       Nonnegative-Real
       (U (List '= Nonnegative-Real) Nonnegative-Real) Nonnegative-Real
       2D-Render-Proc))
(define ((arrows-render-fun vs
                            color line-width line-style
                            alpha
                            arrow-head-size-or-scale arrow-head-angle) area)
  (match-define (vector (ivl x-min x-max) (ivl y-min y-max)) (send area get-bounds-rect))
  (when (and x-min x-max y-min y-max)
    (send area put-alpha alpha)
    (send area put-pen color line-width line-style)
    (send area put-arrow-head arrow-head-size-or-scale arrow-head-angle)
    (for ([x (in-list vs)])
      (match-define (cons (vector v1x v1y) (vector v2x v2y)) x)
      (define (f [x : Real][y : Real])
        (- (* (- v2x v1x) (- y v1y))
           (* (- v2y v1y) (- x v1x))))
      (cond
        ;only draw the arrow head if the endpoint is visible
        ;other option makes little sense since size and angle can be/is plot units-absolute
        [(and (<= x-min v2x x-max) (<= y-min v2y y-max))
         (send area put-arrow (car x) (cdr x) #t)]
        ;only draw the vector line if it is visible
        [(or (<= (* (f x-min y-min) (f x-max y-max)) 0)
             (<= (* (f x-min y-max) (f x-max y-min)) 0))
         (send area put-lines (list (car x) (cdr x)))]))))

;(Sequenceof (Sequence (Sequenceof Real))) does not work because (sequence? 5) = #t
;and so the contract can not know if '((2 2)) is three or two nested sequences
(define-type LVof (All (A) (U (Listof A) (Vectorof A))))
(:: arrows
    (->* [(U (LVof (LVof Real))
             (LVof (LVof (LVof Real))))]
         [#:x-min (U Real #f) #:x-max (U Real #f)
          #:y-min (U Real #f) #:y-max (U Real #f)
          #:color Plot-Color
          #:width Nonnegative-Real
          #:style Plot-Pen-Style
          #:alpha Nonnegative-Real
          #:arrow-head-size-or-scale (U (List '= Nonnegative-Real) Nonnegative-Real)
          #:arrow-head-angle Nonnegative-Real
          #:label (U String pict #f)]
         renderer2d))
(define (arrows vs
                #:x-min [x-min #f] #:x-max [x-max #f]
                #:y-min [y-min #f] #:y-max [y-max #f]
                #:color [color (arrows-color)]
                #:width [width (arrows-line-width)]
                #:style [style (arrows-line-style)]
                #:alpha [alpha (arrows-alpha)]
                #:arrow-head-size-or-scale [arrow-head-size-or-scale (arrow-head-size-or-scale)]
                #:arrow-head-angle [arrow-head-angle (arrow-head-angle)]
                #:label [label #f])
  (define fail/kw (make-raise-keyword-error 'lines))
  (cond
    [(and x-min (not (rational? x-min)))  (fail/kw "#f or rational" '#:x-min x-min)]
    [(and x-max (not (rational? x-max)))  (fail/kw "#f or rational" '#:x-max x-max)]
    [(and y-min (not (rational? y-min)))  (fail/kw "#f or rational" '#:y-min y-min)]
    [(and y-max (not (rational? y-max)))  (fail/kw "#f or rational" '#:y-max y-max)]
    [(not (rational? width))  (fail/kw "rational?" '#:width width)]
    [(or (> alpha 1) (not (rational? alpha)))  (fail/kw "real in [0,1]" '#:alpha alpha)]

    [else
     (define (argument-error)
       (raise-argument-error
        'arrows
        "(U (Sequenceof (Sequence Real Real)) (Sequenceof (Sequence (Sequence Real Real) (Sequence Real Real))))"
        vs))
     ;; check if we have head/tail or pair vectors, and put in standard format
     (define-values (S1 S2)
       (for/fold ([S1 : (Listof (Vectorof Real)) '()]
                  [S2 : (Listof (Pair (Vectorof Real) (Vectorof Real))) '()])
                 ([s vs])
         (define l (sequence->list s))
         (cond
           [(not  (= (length l) 2))
            (argument-error)]
           ;order important because integers are sequence?
           [(andmap real? l)
            (values (cons (sequence-head-vector 'arrows l 2) S1) S2)]
           [(andmap sequence? l)
            (define v1 (sequence-head-vector 'arrows (car l) 2))
            (define v2 (sequence-head-vector 'arrows (cadr l) 2))
            (define v3 (vector (+ (vector-ref v1 0) (vector-ref v2 0))
                               (+ (vector-ref v1 1) (vector-ref v2 1))))
            (values S1
                    (cons (cons v1 v3) S2))]
           [else (argument-error)])))

     (define vs*
       (cond
         [(empty? S2)
          (define S1* (reverse S1))
          (for/list : (Listof (Pair (Vectorof Real) (Vectorof Real)))
            ([v1 (in-list S1*)]
             [v2 (in-list (cdr S1*))])
            (cons v1 v2))]
         [else S2]))
     
     ;; calculate bound and pick right render-fun
     (define rvs
       (let ()
         (match-define (list (cons #{p1 : (Listof (Vectorof Real))}
                                   #{p2 : (Listof (Vectorof Real))}) ...)
           vs*)
         (filter vrational? (append p1 p2))))

     (cond
       [(empty? rvs) empty-renderer2d]
       [else
        (define-values (x- x+ y- y+) (get-bounds x-min x-max y-min y-max rvs))
        (renderer2d (vector (ivl x- x+) (ivl y- y+)) #f default-ticks-fun
                    (and label (λ (_) (arrow-legend-entry label color width style)))
                    (arrows-render-fun vs*
                                       color width style alpha
                                       arrow-head-size-or-scale arrow-head-angle))])]))

(define (get-bounds [x-min : (Option Real)][x-max : (Option Real)]
                    [y-min : (Option Real)][y-max : (Option Real)]
                    [rvs : (Listof (Vectorof Real))])
  (match-define (list (vector #{rxs : (Listof Real)} #{rys : (Listof Real)}) ...) rvs)
  (values (if x-min x-min (apply min* rxs))
          (if x-max x-max (apply max* rxs))
          (if y-min y-min (apply min* rys))
          (if y-max y-max (apply max* rys))))