PageRenderTime 131ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/collects/drracket/arrow.rkt

http://github.com/shekari/racket
Racket | 194 lines | 129 code | 15 blank | 50 comment | 22 complexity | 2b237bdbbf513c587ce93ea9bb5774f5 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause, ISC, LGPL-2.0
  1. #lang racket/base
  2. (require racket/class
  3. racket/math
  4. racket/gui/base)
  5. (provide draw-arrow)
  6. (define largest 16383)
  7. (define smallest -16383)
  8. (define arrow-head-angle (/ pi 8))
  9. (define cos-arrow-head-angle (cos arrow-head-angle))
  10. (define sin-arrow-head-angle (sin arrow-head-angle))
  11. (define arrow-head-size 8)
  12. (define arrow-head-size-cos-arrow-head-angle (* arrow-head-size cos-arrow-head-angle))
  13. (define arrow-head-size-sin-arrow-head-angle (* arrow-head-size sin-arrow-head-angle))
  14. (define arrow-root-radius 2.5)
  15. (define arrow-root-diameter (* 2 arrow-root-radius))
  16. ; If alpha is the angle between the x axis and the Start->End vector:
  17. ;
  18. ; p2-x = end-x + arrow-head-size * cos(alpha + pi - arrow-head-angle)
  19. ; = end-x - arrow-head-size * cos(alpha - arrow-head-angle)
  20. ; = end-x - arrow-head-size * (cos(alpha) * cos(arrow-head-angle) + sin(alpha) * sin(arrow-head-angle))
  21. ; = end-x - arrow-head-size-cos-arrow-head-angle * cos-alpha - arrow-head-size-sin-arrow-head-angle * sin-alpha
  22. ; = end-x - arrow-head-size-cos-arrow-head-angle-cos-alpha - arrow-head-size-sin-arrow-head-angle-sin-alpha
  23. ;
  24. ; p2-y = end-y + arrow-head-size * sin(alpha + pi - arrow-head-angle)
  25. ; = end-y - arrow-head-size * sin(alpha - arrow-head-angle)
  26. ; = end-y - arrow-head-size * (sin(alpha) * cos(arrow-head-angle) - cos(alpha) * sin(arrow-head-angle))
  27. ; = end-y - arrow-head-size-cos-arrow-head-angle * sin-alpha + arrow-head-size-sin-arrow-head-angle * cos-alpha
  28. ; = end-y - arrow-head-size-cos-arrow-head-angle-sin-alpha + arrow-head-size-sin-arrow-head-angle-cos-alpha
  29. ;
  30. ; p3-x = end-x + arrow-head-size * cos(alpha + pi + arrow-head-angle)
  31. ; = end-x - arrow-head-size * cos(alpha + arrow-head-angle)
  32. ; = end-x - arrow-head-size * (cos(alpha) * cos(arrow-head-angle) - sin(alpha) * sin(arrow-head-angle))
  33. ; = end-x - arrow-head-size-cos-arrow-head-angle * cos-alpha + arrow-head-size-sin-arrow-head-angle * sin-alpha
  34. ; = end-x - arrow-head-size-cos-arrow-head-angle-cos-alpha + arrow-head-size-sin-arrow-head-angle-sin-alpha
  35. ;
  36. ; p3-y = end-y + arrow-head-size * sin(alpha + pi + arrow-head-angle)
  37. ; = end-y - arrow-head-size * sin(alpha + arrow-head-angle)
  38. ; = end-y - arrow-head-size * (sin(alpha) * cos(arrow-head-angle) + cos(alpha) * sin(arrow-head-angle))
  39. ; = end-y - arrow-head-size-cos-arrow-head-angle * sin-alpha - arrow-head-size-sin-arrow-head-angle * cos-alpha
  40. ; = end-y - arrow-head-size-cos-arrow-head-angle-sin-alpha - arrow-head-size-sin-arrow-head-angle-cos-alpha
  41. ; dc<%> real real real real real real -> void
  42. ; draw one arrow
  43. ; The reason of the "-0.5" in the definition of start-x and end-x in the let
  44. ; right below is because, well, after numerous experiments done under carefully
  45. ; controlled conditions by a team of independent experts, it was thought to
  46. ; be The Right Thing for the arrows to be drawn correctly, maybe.
  47. (define (draw-arrow dc uncropped-pre-start-x uncropped-pre-start-y uncropped-pre-end-x uncropped-pre-end-y dx dy)
  48. (let ([uncropped-start-x (+ uncropped-pre-start-x dx -0.5)]
  49. [uncropped-start-y (+ uncropped-pre-start-y dy)]
  50. [uncropped-end-x (+ uncropped-pre-end-x dx -0.5)]
  51. [uncropped-end-y (+ uncropped-pre-end-y dy)]
  52. [old-smoothed (send dc get-smoothing)])
  53. (let*-values ([(start-x start-y) (crop-to uncropped-start-x uncropped-start-y uncropped-end-x uncropped-end-y)]
  54. [(end-x end-y) (crop-to uncropped-end-x uncropped-end-y uncropped-start-x uncropped-start-y)])
  55. (send dc set-smoothing 'aligned)
  56. (send dc draw-line start-x start-y end-x end-y)
  57. (when (and (< smallest start-x largest)
  58. (< smallest start-y largest))
  59. (send dc draw-ellipse
  60. (- start-x arrow-root-radius) (- start-y arrow-root-radius)
  61. arrow-root-diameter arrow-root-diameter))
  62. (when (and (< smallest end-x largest)
  63. (< smallest end-y largest))
  64. (unless (and (= start-x end-x) (= start-y end-y))
  65. (let* ([offset-x (- end-x start-x)]
  66. [offset-y (- end-y start-y)]
  67. [arrow-length (sqrt (+ (* offset-x offset-x) (* offset-y offset-y)))]
  68. [cos-alpha (/ offset-x arrow-length)]
  69. [sin-alpha (/ offset-y arrow-length)]
  70. [arrow-head-size-cos-arrow-head-angle-cos-alpha (* arrow-head-size-cos-arrow-head-angle cos-alpha)]
  71. [arrow-head-size-cos-arrow-head-angle-sin-alpha (* arrow-head-size-cos-arrow-head-angle sin-alpha)]
  72. [arrow-head-size-sin-arrow-head-angle-cos-alpha (* arrow-head-size-sin-arrow-head-angle cos-alpha)]
  73. [arrow-head-size-sin-arrow-head-angle-sin-alpha (* arrow-head-size-sin-arrow-head-angle sin-alpha)]
  74. ; pt1 is the tip of the arrow, pt2 is the first point going clockwise from pt1
  75. [pt1 (make-object point% end-x end-y)]
  76. [pt2 (make-object point%
  77. (- end-x arrow-head-size-cos-arrow-head-angle-cos-alpha arrow-head-size-sin-arrow-head-angle-sin-alpha)
  78. (+ end-y (- arrow-head-size-cos-arrow-head-angle-sin-alpha) arrow-head-size-sin-arrow-head-angle-cos-alpha))]
  79. [pt3 (make-object point%
  80. (+ end-x (- arrow-head-size-cos-arrow-head-angle-cos-alpha) arrow-head-size-sin-arrow-head-angle-sin-alpha)
  81. (- end-y arrow-head-size-cos-arrow-head-angle-sin-alpha arrow-head-size-sin-arrow-head-angle-cos-alpha))])
  82. (send dc draw-polygon (list pt1 pt2 pt3)))))
  83. (send dc set-smoothing old-smoothed))))
  84. ;; crop-to : number number number number -> (values number number)
  85. ;; returns x,y if they are in the range defined by largest and smallest
  86. ;; otherwise returns the coordinates on the line from x,y to ox,oy
  87. ;; that are closest to x,y and are in the range specified by
  88. ;; largest and smallest
  89. (define (crop-to x y ox oy)
  90. (cond
  91. [(and (< smallest x largest) (< smallest y largest))
  92. (values x y)]
  93. [else
  94. (let* ([xy-pr (cons x y)]
  95. [left-i (find-intersection x y ox oy smallest smallest smallest largest)]
  96. [top-i (find-intersection x y ox oy smallest smallest largest smallest)]
  97. [right-i (find-intersection x y ox oy largest smallest largest largest)]
  98. [bottom-i (find-intersection x y ox oy smallest largest largest largest)]
  99. [d-top (and top-i (dist top-i xy-pr))]
  100. [d-bottom (and bottom-i (dist bottom-i xy-pr))]
  101. [d-left (and left-i (dist left-i xy-pr))]
  102. [d-right (and right-i (dist right-i xy-pr))])
  103. (cond
  104. [(smallest? d-top d-bottom d-left d-right)
  105. (values (car top-i) (cdr top-i))]
  106. [(smallest? d-bottom d-top d-left d-right)
  107. (values (car bottom-i) (cdr bottom-i))]
  108. [(smallest? d-left d-top d-bottom d-right)
  109. (values (car left-i) (cdr left-i))]
  110. [(smallest? d-right d-top d-bottom d-left)
  111. (values (car right-i) (cdr right-i))]
  112. [else
  113. ;; uh oh... if this case happens, that's bad news...
  114. (values x y)]))]))
  115. ;; smallest? : (union #f number)^4 -> boolean
  116. ;; returns #t if can is less and o1, o2, and o3
  117. ;; if can is #f, return #f. If o1, o2, or o3 is #f, assume that can is smaller than them
  118. (define (smallest? can o1 o2 o3)
  119. (and can
  120. (andmap ( (x) (< can x))
  121. (filter ( (x) x)
  122. (list o1 o2 o3)))))
  123. ;; inside? : (union #f (cons number number)) -> (union #f (cons number number))
  124. ;; returns the original pair if the coordinates are between smallest and largest
  125. ;; and returns #f if the pair is #f or the coordinates are outside.
  126. (define (inside? pr)
  127. (and pr
  128. (let ([x (car pr)]
  129. [y (cdr pr)])
  130. (if (and (< smallest x largest)
  131. (< smallest y largest))
  132. pr
  133. #f))))
  134. ;; find-intersection : (number^2)^2 -> (union (cons number number) #f)
  135. ;; finds the intersection between the lines specified by
  136. ;; (x1,y1) -> (x2,y2) and (x3,y3) -> (x4,y4)
  137. (define (find-intersection x1 y1 x2 y2 x3 y3 x4 y4)
  138. (cond
  139. [(and (= x1 x2) (= x3 x4))
  140. #f]
  141. [(and (= x1 x2) (not (= x3 x4)))
  142. (let* ([m2 (/ (- y3 y4) (- x3 x4))]
  143. [b2 (- y3 (* m2 x3))])
  144. (cons x1
  145. (+ (* m2 x1) b2)))]
  146. [(and (not (= x1 x2)) (= x3 x4))
  147. (let* ([m1 (/ (- y1 y2) (- x1 x2))]
  148. [b1 (- y1 (* m1 x1))])
  149. (cons x3
  150. (+ (* m1 x3) b1)))]
  151. [(and (not (= x1 x2)) (not (= x3 x4)))
  152. (let* ([m1 (/ (- y1 y2) (- x1 x2))]
  153. [b1 (- y1 (* m1 x1))]
  154. [m2 (/ (- y3 y4) (- x3 x4))]
  155. [b2 (- y3 (* m2 x3))])
  156. (if (= m1 m2)
  157. #f
  158. (let* ([x (/ (- b1 b2) (- m2 m1))]
  159. [y (+ (* m1 x) b1)])
  160. (cons x y))))]))
  161. ;; dist : (cons number number) (cons number number) -> number
  162. (define (dist p1 p2)
  163. (sqrt (+ (sqr (- (car p1) (car p2)))
  164. (sqr (- (cdr p1) (cdr p2))))))
  165. ;; localled defined test code.... :(
  166. ;; use module language to run tests
  167. (define (tests)
  168. (and (equal? (find-intersection 0 1 0 10 0 2 0 20) #f)
  169. (equal? (find-intersection 0 1 0 10 0 0 10 10) (cons 0 0))
  170. (equal? (find-intersection 0 0 10 10 0 1 0 10) (cons 0 0))
  171. (equal? (find-intersection 0 0 3 3 2 2 4 4) #f)
  172. (equal? (find-intersection -3 3 3 -3 -3 -3 3 3) (cons 0 0))
  173. (equal? (smallest? 3 1 2 3) #f)
  174. (equal? (smallest? 0 1 2 3) #t)
  175. (equal? (smallest? 1 0 2 3) #f)
  176. (equal? (smallest? 1 0 #f 4) #f)
  177. (equal? (smallest? 1 #f #f 4) #t)
  178. (equal? (smallest? 1 #f #f #f) #t)
  179. (equal? (dist (cons 1 1) (cons 4 5)) 5)))