home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
vis-ftp.cs.umass.edu
/
vis-ftp.cs.umass.edu.tar
/
vis-ftp.cs.umass.edu
/
pub
/
Software
/
ASCENDER
/
ascendMar8.tar
/
UMass
/
ISR
/
isr2geometry.lisp
< prev
next >
Wrap
Lisp/Scheme
|
1995-04-11
|
7KB
|
165 lines
;;; -*- Mode:Common-Lisp; Package:ISR2; Base:10 -*-
;;;
;;; GEOMETRY.LISP - some basic geometric routines
(in-package "ISR2")
;;;=====================
;;; Definition of points:
;;; (point x y) - make a point
;;; (x-coord point) - return x-coordinate
;;; (y-coord point) - return y-coordinate
(defmacro make-point (x-coord y-coord) `(list ,x-coord ,y-coord))
(defmacro x-coord (point) `(car ,point))
(defmacro y-coord (point) `(cadr ,point))
(defmacro delta-x (point1 point2)
`(- (x-coord ,point2) (x-coord ,point1)))
(defmacro delta-y (point1 point2)
`(- (y-coord ,point2) (y-coord ,point1)))
(defmacro translate-point (point xinc yinc)
(let ((lp (gensym)))
`(let ((,lp ,point))
(make-point (+ (x-coord ,lp) ,xinc)
(+ (y-coord ,lp) ,yinc)))))
(defmacro point-distance (point1 point2)
(let ((lp1 (gensym)) (lp2 (gensym)))
`(let* ((,lp1 ,point1)
(,lp2 ,point2)
(dx (delta-x ,lp1 ,lp2))
(dy (delta-y ,lp1 ,lp2)))
(sqrt (+ (* dx dx) (* dy dy))))))
;;;=====================
;;; A point list is a list of points.
;;; A vertex list is a point list where the last point is implicitly
;;; connected with the first point.
(defun find-extrema (point-list)
" return as multiple values the minimum x value, minimum y value,
maximum x value, and maximum y value. (four values in all)."
(let ((x (car (first point-list)))
(y (cadr (first point-list))))
(if (null (cdr point-list))
(values x y x y)
(multiple-value-bind (lowx lowy highx highy) (find-extrema (cdr point-list))
(values (min x lowx) (min y lowy) (max x highx) (max y highy))))))
(defun clockwise-p (vertex-list)
"Returns T if the vertex-list specifies a clockwise walk, NIL otherwise."
(not (counterclockwise-p vertex-list)))
(defun counterclockwise-p (vertex-list)
"Returns T if the vertex-list specifies a counterclockwise walk, NIL otherwise."
(let ((lowx (find-extrema vertex-list)) ;;leftmost X coordinate
(len (length vertex-list)))
(multiple-value-bind (current position)
(#-:EXPLORER lispm::find #+:EXPLORER find lowx vertex-list :test #'= :key #'car)
(let ((previous (elt vertex-list (mod (1- position) len)))
(next (elt vertex-list (mod (1+ position) len))))
;;test if walk from previous to current to next is a counterclockwise cycle
(ccw-cycle-p (car previous) (cadr previous)
(car current) (cadr current)
(car next) (cadr next))))))
(defun ccw-cycle-p (x1 y1 x2 y2 x3 y3)
" Returns T iff (x1,y1)-(x2,y2)-(x3,y3)-(x1,y1) is a counterclockwise walk, NIL otherwise."
;;;;Note: computed by taking the determinant of the matrix
;;;; x1 y1 1
;;;; x2 y2 1
;;;; x3 y3 1
;;;;the determinant is positive iff p1-p2-p3 form a CCW cycle
(plusp (+ (* x1 (- y2 y3))
(* y1 (- x3 x2))
(- (* x2 y3) (* y2 x3)))))
;;;=====================
(defmacro for-each-edge ((vertex-list x1 y1 x2 y2) &body body)
" Perform the body once for each edge specified by the vertex list. Inside the
body, the variables given as X1, Y1, X2 and Y2 are bound to the endpoints of the edge.
The vertex list '((10 10)(20 10)(15 15)) specifies 3 edges:
10,10 to 20,10 ; 20,10 to 15,15 ; and 15,15 to 10,10.
Special cases:
The vertex list '((10 10)(10 20)) has only one edge,
The list '((10 10)) has no edges.
VERTEX-LIST is evaluated only once. Its value is returned from this macro."
(let ((vertices (gensym))
(vlist (gensym))
(v1 (gensym))
(v2 (gensym)))
`(let ((,vertices ,vertex-list))
(do ((,v1 (when (> (length ,vertices) 1) (car ,vertices)) (car ,vlist))
(,v2 (cadr ,vertices) (or (cadr ,vlist) (car ,vertices)))
(,vlist (when (> (length ,vertices) 2) (cdr ,vertices)) (cdr ,vlist)))
((null ,v1) ,vertices)
(let ((,x1 (car ,v1))
(,y1 (cadr ,v1))
(,x2 (car ,v2))
(,y2 (cadr ,v2)))
,@body)))))
(defun point-inside-polygon-p (x y vertex-list)
" Determines whether the point (X,Y) is inside the simple polygon (no self intersections)
specified by VERTEX-LIST. A vertex list is a list of x,y points describing a walk around
the edges of the polygon.
Example: (point-inside-polygon-p 15 15 '((10 10)(20 10)(20 20)(10 20))) --> T.
Results are unpredictable if the point is actually on an edge."
(let ((count 0))
(for-each-edge (vertex-list x1 y1 x2 y2)
(when (and (> (max y1 y2) y) (<= (min y1 y2) y))
(cond
((>= (min x1 x2) x) nil)
((<= (max x1 x2) x) (incf count))
((< (+ x1 (/ (* (- y y1) (- x2 x1)) (- y2 y1))) x) (incf count))
(t nil))))
(oddp count)))
;;;=====================
;;; some special purpose code for rectangles
(defun bounding-rect-no-extend (end1 end2 radius &key (length nil))
"Returns a rectangle bounding the line defined by end1 and end2.
The rectangle has the same length as the line, and has width 2*radius.
The rectangle is returned as (p1 p2 p3 p4), where p1-p2-p3-p4-p1 is
a counterclockwise walk around the rectangle. If end1 is the leftmost
lowest point of the line then p1 will be the leftmost lowest point
of the rectangle."
(let* ((length (or length (point-distance end1 end2)))
(xinc (* radius (/ (delta-y end1 end2) length)))
(yinc (* radius (/ (delta-x end1 end2) length))))
(if (> (delta-x end1 end2) 0)
(list (translate-point end1 xinc (- yinc))
(translate-point end2 xinc (- yinc))
(translate-point end2 (- xinc) yinc)
(translate-point end1 (- xinc) yinc))
(list (translate-point end1 (- xinc) yinc)
(translate-point end1 xinc (- yinc))
(translate-point end2 xinc (- yinc))
(translate-point end2 (- xinc) yinc)))))
(defun inside-rect-p (point rect-vertices)
"Returns t iff point lies inside or on a rectangle having the
given vertices. The vertices must specify a counterclockwise
walk around the rectangle. This test may run faster when the
distance from the first vertex to the second vertex is greater
than the distance from the second to the third."
(let* ((p1 (first rect-vertices))
(p2 (second rect-vertices))
(p4 (fourth rect-vertices))
(dx (delta-x p1 p2))
(dy (delta-y p1 p2))
(high1 (- (* dx (y-coord p4)) (* dy (x-coord p4))))
(low1 (- (* dx (y-coord p1)) (* dy (x-coord p1))))
(high2 (+ (* dy (y-coord p2)) (* dx (x-coord p2))))
(low2 (+ (* dy (y-coord p1)) (* dx (x-coord p1)))))
(and (<= low1
(- (* dx (y-coord point)) (* dy (x-coord point)))
high1)
(<= low2
(+ (* dy (y-coord point)) (* dx (x-coord point)))
high2))))