;;;-*- Mode:Common-Lisp; Package:PICTURES; Base:10 -*-
;;;
;;;
;;;
;;;			 TEXAS INSTRUMENTS INCORPORATED
;;;				  P.O. BOX 149149
;;;			       AUSTIN, TEXAS 78714-9149
;;;
;;; Copyright (C)1987,1988,1989,1990 Texas Instruments Incorporated.
;;;
;;; Permission is granted to any individual or institution to use, copy, modify,
;;; and distribute this software, provided that this complete copyright and
;;; permission notice is maintained, intact, in all copies and supporting
;;; documentation.
;;;
;;; Texas Instruments Incorporated provides this software "as is" without
;;; express or implied warranty.
;;;
;;; Authors: Delmar Hager, James Dutton, Teri Crowe
;;; Contributors: Kerry Kimbrough, Patrick Hogan, Eric Mielke

(in-package "PICTURES")


(export '(
	  vertices
	  make-polypoint
	  min-x-vertex
	  min-y-vertex
	  max-x-vertex
	  max-y-vertex
	  make-polyline
	  )
	'pictures)


(defclass polypoint (extent-cache graphic)
  (
   (vertices     :type    array
		 :initform (make-array '(8) :adjustable t :fill-pointer 0)
		 :accessor vertices 
		   :documentation  "a vector of x,y points ")
   
   )
  (:documentation "The graphic class for drawing points in object coordinates"))
  
;Function: make-polypoint
;  Return a new polypoint object.
(defun make-polypoint (point-seq  &rest options) 
  "Make a polypoint with the coordinates contained in POINT-SEQ.
The following keyword OPTIONS are allowed: GSTATE PARENT SENESITIVITY TRANSFORM PLIST"
  (DECLARE (VALUES polypoint))
  (APPLY #'MAKE-INSTANCE 'polypoint
	 :point-seq point-seq
	 options))

(DEFMETHOD initialize-instance :after ((polypoint polypoint) &key point-seq)
  (SETF (vertices polypoint)  point-seq))



(DEFMETHOD (SETF vertices) (point-seq (polypoint polypoint))
  (with-slots (vertices) polypoint
    (SETF (FILL-POINTER vertices) 0)
    (DO* ((index 0 (1+ index)) )
	 ((>= index (* 2 (TRUNCATE (LENGTH point-seq) 2))) (slot-value  polypoint 'vertices))
      (VECTOR-PUSH-EXTEND (ELT point-seq index) vertices 5 ))
    point-seq))


;Method: vertex-i
;  Return the I'th vertex of the given POLYPOINT.  If there are fewer than I
;  vertices in the POLYPOINT, then nil is returned.

(defmethod vertex-i ((polypoint polypoint) i)
  (declare (type (integer 0 *) i))
  (WHEN  (< (* i 2) (LENGTH (vertices polypoint)))
    (with-slots (vertices) polypoint
      (VALUES (ELT  vertices (* i 2))  (ELT vertices (1+ (* i 2))))))
  )


(DEFMETHOD vertex-x ((polypoint polypoint) position)
  (DECLARE (VALUES local-x))
  (with-slots (vertices) polypoint
    (WHEN (< position  (length-vertices polypoint))
	(ELT vertices (* position 2)))))

(DEFMETHOD (SETF vertex-x) (x (polypoint polypoint) position)
  (DECLARE (VALUES local-x))
  (with-slots (vertices) polypoint
    (SETF (vertex-x vertices position) x)))

(DEFMETHOD vertex-y ((polypoint polypoint) position)
  (DECLARE (VALUES local-y))
    (IF (< position  (length-vertices polypoint))
	(with-slots (vertices) polypoint
	  (ELT vertices (+ (* position 2) 1)))))

(DEFMETHOD (SETF vertex-y) (y (polypoint polypoint) position)
  (DECLARE (VALUES local-y))
  (with-slots (vertices) polypoint
    (SETF (vertex-y vertices position) y)))

;Method: extent-compute
;  Compute the extent rectangle for the polypoint.

;  Note: A graphic's extent rectangle is defined in the object coordinate system.  This
;  means that each graphic should apply its own transform to its computed extent
;  before returning it.

(defmethod extent-compute ((polypoint polypoint))
  (declare (values (or null extent-rect)))
  (LET (extent-rectangle)
  (with-slots (vertices transform) polypoint
    (with-vector temp-vector
	(copy-to-vector vertices temp-vector)
      (transform-point-seq transform temp-vector)
        (SETF extent-rectangle
	    (make-extent-rect
	      :xmin (min-value-vector temp-vector 0)
	      :ymin (min-value-vector temp-vector 1)
	      :xmax (max-value-vector temp-vector 0)
	      :ymax (max-value-vector temp-vector 1)))))
  extent-rectangle))

;Method insert-vertex
; Given a x and y value an vertex can be inserted in to the x and y vectors of the object
; If i is greater than the length of the vector, the vertex points are added to the end of the x-vectors
(DEFMETHOD insert-vertex ((polypoint polypoint) new-x new-y  i)
  (DECLARE (type wcoord new-x))
  (DECLARE (type integer i))
  (extent-changed polypoint)
  (with-slots (vertices  extent) polypoint
    (insert-vertex vertices new-x new-y i)
    )
  (values new-x new-y)
  )


; Delete a vertex from the point list  
(DEFMETHOD delete-vertex ( (polypoint polypoint) i)
  (DECLARE (type integer i))
  (extent-changed polypoint)
  (MULTIPLE-VALUE-BIND (x y)  (vertex-i polypoint i)
    (with-slots (vertices extent) polypoint
      (delete-vertex vertices i))
    (VALUES x y)))



(DEFMETHOD length-vertices ((polypoint polypoint))
  (with-slots (vertices) polypoint
    (VALUES (FLOOR (FILL-POINTER vertices) 2))))

(DEFMETHOD min-x-vertex ((polypoint polypoint))
  (with-slots (vertices) polypoint
    (point-seq-x-min vertices)))

(DEFMETHOD min-y-vertex ((polypoint polypoint))
  (with-slots (vertices) polypoint
    (point-seq-y-min vertices)))

(DEFMETHOD max-x-vertex ((polypoint polypoint))
  (with-slots (vertices) polypoint
    (point-seq-x-max vertices)))

(DEFMETHOD max-y-vertex ((polypoint polypoint))
  (with-slots (vertices) polypoint
    (point-seq-y-max vertices)))




;Method: normalize-graphic
;  Normalize the POLYPOINT by applying its transform to its geometry, changing it
;  accordingly, and then setting its transform to nil (the identity transform).
;  Nothing of value is returned.

(defmethod normalize-graphic :before ((polypoint polypoint))
  (with-slots (vertices transform) polypoint
    (transform-point-seq transform vertices)
    (SETF transform nil))
  )

;Method: draw-graphic
;  Draw the POLYPOINT object in the given VIEW. If MIN-X, MIN-Y, WIDTH, and HEIGHT
;  are given, then only parts of the object that lie within the given rectangle
;  need to be drawn.

(defmethod draw-graphic ((polypoint polypoint) (view view) 
                           &optional min-x min-y width height) 
  (declare (type (or null wcoord) min-x min-y width height))
  (with-slots (vertices gstate extent) polypoint
    (WHEN   (visible-p polypoint)
      (WHEN (AND ( > (LENGTH vertices) 0) (viewable-p polypoint))
	(with-vector temp-vertices
	  (copy-to-vector vertices temp-vertices)
	  (transform-point-seq (graphic-world-transform polypoint) temp-vertices)
	  (view-draw-polypoint view temp-vertices 
			       (graphic-gstate polypoint)))))))

;Polyline Elements

;A polyline is an ordered list of points connected by straight line segments.
;The first and last points in the list are not connected by a line.
;
;The polyline class inherits from the polypoint class so that the following
;methods described for polypoints are also available for polylines.
;        

;Polyline  Definition:
(defclass polyline (polypoint)
  ()
  (:documentation "the graphic class for drawing a polyline on the screen"))

;Function: make-polyline
;  Return a new polyline object.

(defun make-polyline (point-seq  &rest options )
  "Make a polygon with the coordinates contained in POINT-SEQ.
The following keyword OPTIONS are allowed: GSTATE PARENT SENESITIVITY TRANSFORM PLIST"
  (DECLARE (VALUES polygon))
  
  (APPLY #'MAKE-INSTANCE 'polyline
	 :point-seq point-seq
	 options))




;draw a polyline

(defmethod draw-graphic ((polyline polyline) (view view)
                           &optional min-x min-y width height) 
  (declare (type (or null wcoord) min-x min-y width height))
  (with-slots (vertices gstate extent) polyline
    (WHEN (visible-p polyline)
	(WHEN (AND (viewable-p polyline)( > (LENGTH vertices) 0))
	  (with-vector temp-vertices
		     (copy-to-vector vertices temp-vertices)
		     (transform-point-seq (graphic-world-transform polyline) temp-vertices)
	  (view-draw-polyline view temp-vertices 
				    (graphic-gstate polyline)))))))


(DEFMETHOD graphic-contains-p ((polyline polyline) x y &optional (pixel 1))
  (DECLARE (type wcoord x y) )
  (when (point-in-extents-p polyline x y pixel)
      (LET ((line-width (OR (AND
			      (graphic-gstate polyline)
			      (gstate-line-width polyline) (+ (/ (* (gstate-line-width polyline) pixel)  2 ) pixel)) pixel)))
	(with-slots (vertices) polyline
	  (with-vector temp-vertices 
	    (copy-to-vector vertices temp-vertices)
	    (transform-point-seq (graphic-world-transform polyline) temp-vertices)
	    (point-near-line temp-vertices line-width x y))))))


(defstruct point x y)


(defun cross-product (p1 p2 q1 q2)
  (- (* (- (point-x p2) (point-x p1))
	(- (point-y q2) (point-y q1)))
     
     (* (- (point-x q2) (point-x q1))
	(- (point-y p2) (point-y p1)))))
  
(defun inside-p (polygon p)
  (declare (type sequence polygon)
	   (type point p))

  (LET ((poly-seq (get-global-vector)))
    (DO*
      ( 
       (last   (- (length polygon) 2))
       (i           0 (+ i 2))
       )
      ((> i last) (VALUES poly-seq))
      (VECTOR-PUSH-EXTEND  (make-point :x (ELT polygon i) :y (ELT  polygon (+ i 1))) poly-seq)
		 
      )
  ;; True if p is on a polygon edge (edge cross-product is 0) or if p is on the
  ;; same side of every polygon edge (edge cross-products have the same sign.
  
    (do*
      (current-vertex
       current-sign
       (last        (1- (LENGTH poly-seq)))
       (prev-vertex (elt poly-seq 0) current-vertex)
       (prev-sign   (SIGNUM (cross-product p (elt poly-seq last) p prev-vertex)) current-sign)
       (i           1 (1+ i)))
      
      ((or
	 ;; Point on previous edge?
	 (zerop prev-sign)
	 
	 ;; All edge cross-products the same sign?
	 (> i last))
       (PROGN
	 (return-global-vector poly-seq);release the global vector for reuse
       t))
      (setf current-sign (signum (cross-product p prev-vertex p (setf current-vertex (elt poly-seq i)))))
      (unless (= current-sign  prev-sign)
	(PROGN (return-global-vector poly-seq);release the global vector for reuse
						 (return nil))))
    
    ))












