/utilities/geometry.rkt

http://github.com/VincentToups/racket-lib · Racket · 498 lines · 11 code · 5 blank · 482 comment · 0 complexity · a77afb3408a9f0d8dca3167d317a2ba8 MD5 · raw file

  1. #lang racket
  2. (require functional/point-free)
  3. (require utilities/lists)
  4. (define (!= a b) (not (= a b)))
  5. (define (local-any . args)
  6. (any args))
  7. (define (member-if pred lst)
  8. #|
  9. proc member-if pred
  10. returns the sublist whose car is the first element of lst
  11. for which (pred el) == #t
  12. |#
  13. (let loop ((rst lst))
  14. (cond
  15. ((null? rst) #f)
  16. ((pred (car rst)) rst)
  17. (#t (loop (cdr rst))))))
  18. (define (elt lst ix)
  19. (let loop ((i 0)
  20. (lst lst))
  21. (cond
  22. ((empty? lst) (error "Index out of range."))
  23. ((= i ix) (car lst))
  24. (else
  25. (loop (+ i 1)
  26. (cdr lst))))))
  27. (define-syntax (named-fn stx)
  28. (syntax-case stx ()
  29. [(named-fn name args body ...)
  30. (syntax
  31. (let ((name (lambda args body ...)))
  32. name))]))
  33. (define-syntax (fn stx)
  34. (syntax-case stx ()
  35. [(fn args body ...)
  36. (syntax (lambda args body ...))]))
  37. (define-syntax (sum-of stx)
  38. (syntax-case stx (<-)
  39. [(sum-of expr (var <- list))
  40. (syntax (reduce (lambda (ac var)
  41. (+ ac expr)) list))]))
  42. (define 2pi (* 2 pi))
  43. (define ^ expt)
  44. (define make-point list)
  45. (define pt! make-point)
  46. (define (point? o)
  47. (and
  48. (list? o)
  49. (all (map number? o))))
  50. (define pt? point?)
  51. (define (norm-pt pt)
  52. (map (partial< / (pt<> pt)) pt))
  53. (define (pt+ . args)
  54. (apply map + args))
  55. (define (pt- . args)
  56. (apply map - args))
  57. (define (pt. p1 p2)
  58. (apply + (map * p1 p2)))
  59. (define (pt<> pt)
  60. (sqrt (apply + (map (partial< ^ 2) pt))))
  61. (define (pt|| pt1 pt2)
  62. (pt<> (pt- pt1 pt2)))
  63. (define (pt* a b)
  64. (cond ((and (number? a) (point? b))
  65. (map (partial< * a) b))
  66. ((and (point? a) (number? b))
  67. (map (partial< * b) a))
  68. (#t
  69. (error "Can't pt* these quantities"))))
  70. (define (make-line x1 y1 . args)
  71. (cond ((= (length args) 0)
  72. (list x1 y1));; In this case x1 and y1 are points
  73. ((= (length args) 2)
  74. (list (make-point x1 y1)
  75. (list->point args)))));; Here we are the simple constructor;
  76. (define line! make-line)
  77. (define ln! make-line)
  78. (define (line x1 y1 x2 y2)
  79. (display "line is deprecated for making lines, use line! instead\n")
  80. (line! x1 y1 x2 y2))
  81. (define (line-direction line)
  82. (norm-pt (pt- (line-pt-2 line)
  83. (line-pt-1 line))))
  84. (define (xy-at-d-along-line-from line distance from)
  85. (let ((vec (norm-pt
  86. (pt- (line-pt-2 line)
  87. (line-pt-1 line)))))
  88. (pt+ from (pt* distance vec))))
  89. (define (make-circle center radius)
  90. (list 'circle center radius))
  91. (define circle! make-circle)
  92. (define circ! make-circle)
  93. (define (circle? obj)
  94. (eq? (car obj) 'circle))
  95. (define circ? circle?)
  96. (define (circle-center circ)
  97. (cadr circ))
  98. (define (circle-radius circ)
  99. (caddr circ))
  100. (define (circle-circumference circ)
  101. (* 2pi (circle-radius circ)))
  102. (define (point-angle pt)
  103. (atan (pt-y pt) (pt-x pt)))
  104. (define (xy-at-d-along-circle-from circle distance from)
  105. (let* ((ang-from (angle-on-circle circle from))
  106. (ang-sweep (* 2pi (/ distance (circle-circumference circle))))
  107. (ang-to (+ ang-from ang-sweep)))
  108. (point-on-circle-at-angle circle ang-to)))
  109. (define (circle-bounding-box circ)
  110. (let* ((rad (circle-radius circ))
  111. (cen (circle-center circ))
  112. (bottom-left
  113. (pt- cen (pt* (pt! 1. 1.) rad)))
  114. (top-right
  115. (pt+ bottom-left (pt* (* 2 rad) (pt! 1. 1.)))))
  116. (list bottom-left top-right)))
  117. (define (angle-on-circle circle pt)
  118. (let ((pt (pt- pt (circle-center circle))))
  119. (point-angle pt)))
  120. (define (point-on-circle-at-angle circle ang)
  121. (pt+ (circle-center circle) (pt* (circle-radius circle)
  122. (make-point (cos ang) (sin ang)))))
  123. (define (3-points->circle-find-center p1 p2 p3)
  124. (let* ((a (make-line p1 p2))
  125. (b (make-line p2 p3))
  126. (ma (line-slope a))
  127. (mb (line-slope b))
  128. (xc (/
  129. (+ (* ma mb (- (pt-y p1) (pt-y p3)))
  130. (* mb (+ (pt-x p1) (pt-x p2)))
  131. (- (* ma (+ (pt-x p2) (pt-x p3)))))
  132. (* 2 (- mb ma))))
  133. (yc (- (/ (+ (pt-y p1) (pt-y p2)) 2)
  134. (* (/ 1 ma) (- xc (/ (+ (pt-x p1) (pt-x p2)) 2))))))
  135. (pt! xc yc)))
  136. (define (circle->lambda circle)
  137. (named-fn circle-fn (theta)
  138. (point-on-circle-at-angle circle theta)))
  139. (define (3-points->circle p1 p2 p3)
  140. (let* ((center (3-points->circle-find-center p1 p2 p3)))
  141. (make-circle center (point-distance center p1))))
  142. (define (point-distance p1 p2)
  143. (sqrt (apply + (map (partial< ^ 2) (map - p1 p2)))))
  144. (define (make-piecewise-line . points)
  145. (let ((corrected-pnts
  146. (cons (car points)
  147. (reverse
  148. (cdr
  149. (reverse
  150. (rep-list
  151. 2
  152. (cdr points))))))))
  153. (bunch-by (map
  154. (lambda (pnts) (apply make-point pnts))
  155. corrected-pnts) 2)))
  156. (define (pwl-get-piece pwl i)
  157. (elt pwl i))
  158. (define (piecewise-line-nth-pt pw-line n)
  159. (let ((pts (append (car pw-line)
  160. (map line-pt-2 (cdr pw-line)))))
  161. (elt pts n)))
  162. (define (line-intersects-pw-line line pw-line)
  163. (map (lambda (piece)
  164. (lines-intersection piece line))
  165. pw-line))
  166. (define (inexactify-line ln)
  167. (map (lambda (pt) (map exact->inexact pt)) ln))
  168. (define (list->point li)
  169. li)
  170. (define (line-pt-1 li)
  171. (car li))
  172. (define (line-pt-2 li)
  173. (cadr li))
  174. (define (line? o)
  175. (and (list? o)
  176. (all (map point? o))))
  177. (define ln? line?)
  178. (define (line-horizontal? line)
  179. (= (cadr (car line))
  180. (cadr (cadr line))))
  181. (define (line-vertical? line)
  182. (= (car (car line))
  183. (car (cadr line))))
  184. (define (line-slope li)
  185. (if (line-vertical? li) 'inf
  186. (let ((p1 (line-pt-1 li))
  187. (p2 (line-pt-2 li)))
  188. (/ (- (point-y p1) (point-y p2))
  189. (- (point-x p1) (point-x p2))))))
  190. (define (line-intercept li)
  191. (if (line-vertical? li) 'NaN
  192. (let* ((p (line-pt-1 li))
  193. (x (point-x p))
  194. (y (point-y p)))
  195. (- y (* (line-slope li) x)))))
  196. (define (line->lambda li)
  197. (if (line-vertical? li)
  198. (lambda (x) (point-x (line-pt-1 li)))
  199. (let ((m (line-slope li))
  200. (b (line-intercept li)))
  201. (lambda (x) (+ (* m x) b)))))
  202. (define (make-points xs ys)
  203. (map make-point xs ys))
  204. (define pts! make-points)
  205. (define (point-x point)
  206. (car point))
  207. (define (point-y point)
  208. (cadr point))
  209. (define pt-x point-x)
  210. (define pt-y point-y)
  211. (define (make-line-via-regression . points)
  212. (let ((points (if (= 1 (length points)) (car points) points)))
  213. (let* ((points (sort points (fn (p1 p2) (< (point-x p1) (point-x p2)))))
  214. (m (length points))
  215. (xs (map point-x points))
  216. (ys (map point-y points))
  217. (sx (sum-of x (x <- xs)))
  218. (sy (sum-of y (y <- ys)))
  219. (sx2 (sum-of (* x x) (x <- xs)))
  220. (sxy (sum-of xy (xy <- (map * xs ys))))
  221. (d (- (* m sx2) (* sx sx)))
  222. (intercept
  223. (/ (- (* sx2 sy) (* sx sxy)) d))
  224. (slope
  225. (/ (- (* m sxy) (* sx sy)) d))
  226. (x-start (apply min xs))
  227. (x-end (apply max xs)))
  228. (make-line (make-point
  229. x-start
  230. (+ intercept (* slope x-start)))
  231. (make-point
  232. x-end
  233. (+ intercept (* slope x-end)))))))
  234. (define (make-fn-via-regression . points)
  235. (let ((points (if (= 1 (length points)) (car points) points)))
  236. (line->lambda (apply make-line-via-regression points))))
  237. (define (get-x-interval x xs ys)
  238. (let loop ((rst-x xs)
  239. (rst-y ys))
  240. (cond ((null? rst-x) #f)
  241. ((and (> x (car rst-x)) (<= x (cadr rst-x)))
  242. (values (make-point (car rst-x)
  243. (car rst-y))
  244. (make-point (cadr rst-x)
  245. (cadr rst-y))))
  246. (else
  247. (loop (cdr rst-x)
  248. (cdr rst-y))))))
  249. (define (points->interp-fn . points)
  250. (let* ((points
  251. (sort (if (= (len points) 1) (car points) points)
  252. (lambda (e1 e2) (< (pt-x e1) (pt-x e2)))))
  253. (lines
  254. (reverse (foldl
  255. (fn (current-point lines)
  256. (cons (line! (line-pt-2 (car lines))
  257. current-point) lines))
  258. (list (line! (car points) (cadr points)))
  259. (cddr points))))
  260. (last-point (car (reverse points)))
  261. (last-line (car (reverse lines))))
  262. (named-fn interp-fn (x)
  263. (cond ((< x (pt-x (car points)))
  264. ;(disp "Before")
  265. (let ((m (line-slope (car lines)))
  266. (b (line-intercept (car lines))))
  267. (+ b (* m x))))
  268. ((> x (pt-x last-point))
  269. ;(dispf "After ~a" (pt-x last-point))
  270. (let ((m (line-slope last-line))
  271. (b (line-intercept last-line)))
  272. (+ b (* m x))))
  273. (else
  274. ;(disp "Within")
  275. (let* ((line (car (member-if (>partial within-line-domain? x) lines)))
  276. (m (line-slope line))
  277. (b (line-intercept line)))
  278. (+ b (* m x))))))))
  279. (define (point-in-box? box point)
  280. (let* ((x (car point))
  281. (y (cadr point))
  282. (boxx (map point-x box))
  283. (boxy (map point-y box))
  284. (xmin (apply min boxx))
  285. (xmax (apply max boxx))
  286. (ymin (apply min boxy))
  287. (ymax (apply max boxy)))
  288. (and (>= x xmin)
  289. (<= x xmax)
  290. (>= y ymin)
  291. (<= y ymax))))
  292. (define (sign x)
  293. (inexact->exact (/ x (abs x))))
  294. (define (box-0 f xl xr tol)
  295. (display "box-0 is deprecated. Don't use it!")
  296. (newline)
  297. (find-zeros-bracket f xl xr tol))
  298. (define (find-zeros-bracket f xl xr tol)
  299. (let* ((fl (f xl))
  300. (fr (f xr))
  301. (xm (/ (+ xr xl) 2.0))
  302. (fm (f xm)))
  303. (cond ((= (sign fl)
  304. (sign fr))
  305. (error "find-zeros-bracket You failed to bracket the root"))
  306. ((= 0.0 fl) xl)
  307. ((= 0.0 fr) xr)
  308. ((< (abs fm) tol) xm)
  309. (else
  310. ;;choose new bracket
  311. (cond
  312. ((!= (sign fl) (sign fm))
  313. (find-zeros-bracket f xl xm tol))
  314. ((!= (sign fr) (sign fm))
  315. (find-zeros-bracket f xm xr tol)))))))
  316. (define (box-intersect? box-a box-b)
  317. (apply
  318. local-any
  319. (append
  320. (map (lambda (point) (point-in-box? box-a point))
  321. box-b)
  322. (map (lambda (point) (point-in-box? box-b point))
  323. box-a))))
  324. (define (within-line-domain? x line)
  325. (let ((xs (map point-x line)))
  326. (and (>= x (apply min xs))
  327. (<= x (apply max xs)))))
  328. (define (within-line-range? y line)
  329. (let ((ys (map point-y line)))
  330. (and (>= y (apply min ys))
  331. (<= y (apply max ys)))))
  332. (define (lines-intersect? line-a line-b)
  333. (let* ((x1 (car (car line-a)))
  334. (x2 (car (cadr line-a)))
  335. (x3 (car (car line-b)))
  336. (x4 (car (cadr line-b)))
  337. (y1 (cadr (car line-a)))
  338. (y2 (cadr (cadr line-a)))
  339. (y3 (cadr (car line-b)))
  340. (y4 (cadr (cadr line-b)))
  341. (denom (- (* (- y4 y3) (- x2 x1))
  342. (* (- x4 x3) (- y2 y1))))
  343. (ua (/ (- (* (- x4 x3) (- y1 y3))
  344. (* (- y4 y3) (- x1 x3))) denom))
  345. (xint (+ x1 (* ua (- x2 x1))))
  346. (yint (+ y1 (* ua (- y2 y1)))))
  347. (and (within-line-domain? xint line-a)
  348. (within-line-domain? xint line-b)
  349. (within-line-range? yint line-a)
  350. (within-line-range? yint line-b))))
  351. (define (lines-intersection line-a line-b)
  352. (let* ((x1 (car (car line-a)))
  353. (x2 (car (cadr line-a)))
  354. (x3 (car (car line-b)))
  355. (x4 (car (cadr line-b)))
  356. (y1 (cadr (car line-a)))
  357. (y2 (cadr (cadr line-a)))
  358. (y3 (cadr (car line-b)))
  359. (y4 (cadr (cadr line-b)))
  360. (denom (- (* (- y4 y3) (- x2 x1))
  361. (* (- x4 x3) (- y2 y1))))
  362. (ua (/ (- (* (- x4 x3) (- y1 y3))
  363. (* (- y4 y3) (- x1 x3))) denom))
  364. (xint (+ x1 (* ua (- x2 x1))))
  365. (yint (+ y1 (* ua (- y2 y1)))))
  366. (if (and (or (line-vertical? line-a) (within-line-domain? xint line-a))
  367. (or (line-vertical? line-b) (within-line-domain? xint line-b))
  368. (or (line-horizontal? line-a) (within-line-range? yint line-a))
  369. (or (line-horizontal? line-b) (within-line-range? yint line-b)))
  370. (list xint yint)
  371. #f)))
  372. (define point! pt!)
  373. (provide make-point
  374. pt!
  375. point!
  376. point?
  377. pt?
  378. norm-pt
  379. pt+
  380. pt-
  381. pt.
  382. pt<>
  383. pt*
  384. pt||
  385. make-line
  386. line!
  387. ln!
  388. line-direction
  389. xy-at-d-along-line-from
  390. make-circle
  391. circle!
  392. circ!
  393. circle?
  394. circ?
  395. circle-center
  396. circle-radius
  397. circle-circumference
  398. point-angle
  399. xy-at-d-along-circle-from
  400. circle-bounding-box
  401. angle-on-circle
  402. point-on-circle-at-angle
  403. 3-points->circle-find-center
  404. circle->lambda
  405. 3-points->circle
  406. point-distance
  407. make-piecewise-line
  408. pwl-get-piece
  409. piecewise-line-nth-pt
  410. line-intersects-pw-line
  411. inexactify-line
  412. list->point
  413. line-pt-1
  414. line-pt-2
  415. line?
  416. line-horizontal?
  417. line-vertical?
  418. line-slope
  419. line-intercept
  420. line->lambda
  421. make-points
  422. pts!
  423. point-x
  424. point-y
  425. pt-x
  426. pt-y
  427. make-line-via-regression
  428. make-fn-via-regression
  429. get-x-interval
  430. points->interp-fn
  431. point-in-box?
  432. sign
  433. box-0
  434. find-zeros-bracket
  435. box-intersect?
  436. within-line-domain?
  437. within-line-range?
  438. lines-intersect?
  439. lines-intersection)