;; -*- Mode: Lisp; Package: DESIGN; Syntax: Ansi-common-lisp -*-

;;;  +++ Rethink topological distances.  How is front-door represented in new scheme? [6/11/97]


;;; New measures:

;;   vis-openness: returns visibility measure (btw 0 and 1) for a space wrt to another space
;;         (i.e. measure of  area visible from other space) 

;;   vis-openness-intersection: returns visibility measure for intersection of visibilities of 
;;         space wrt other spaces (i.e. measure of area visible from all spaces); formerly
;;         called highly-open.

;;   vis-openness-union:  returns visibility measure for union of visibilities of space wrt
;;         other spaces (i.e. measure of area visible from any of other spaces)

;;   min-topological-distance:  returns integer that is minimum number of intervening spaces
;;         between two spaces

;;   min-physical-distance:  returns distance between spaces (along shortest path); measure
;;         between openings along path rather than centers of spaces 

;;   min-topological-distance-from-front-entry, -from-approach-point

;;   min-physical-distance-from-front-entry, -from-approach-point

;;   direction-changes-from-front-door, -from-approach-point

;;   topo-direction-changes-from-front-door, -from-approach-point

;;   entry-direction-change:  change in direction upon entering space along shortest physical
;;         path from another designated space

;;   topo-entry-direction-change: change in direction upon entering space along shortest
;;         topological path from another designated space

;;   entry-direction-change-from-front-door

;;   topo-entry-direction-change-from-front-door 


;;; VIS-OPEN is measure of how visually open a space is to another space or spaces.  Later
;;; write measure for PHYS-OPEN, which takes into account doors, glass panes, screens, etc.
;;; Void and closeable-void segments are treated as the same for now (call to #'voidp in
;;; #'check-visibility determines this).

;;; Note:  Need to worry about which boundary model to use...

;;; Modify to support vis-openness between design-elements? e.g. How visible is fplace from
;;; front-door?

(defmethod vis-openness ((to use-space) (from use-space) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*)
			 (viewpoint-s *default-viewpoint-s*))
  (vis-openness (territory to) (territory from)
		tile-size viewpoint-d viewpoint-n viewpoint-s))

(defmethod vis-openness ((to-region territory) (from-region territory) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*) 
			 (viewpoint-s *default-viewpoint-s*))
  (set-visible-tiles to-region from-region tile-size viewpoint-d viewpoint-n viewpoint-s)
  (visibility-measure to-region from-region tile-size viewpoint-d viewpoint-n viewpoint-s))

(defmethod vis-openness ((to use-space) (from list) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*)
			 (viewpoint-s *default-viewpoint-s*))
  ;; from arg should be list of use-spaces
  (let ((to-region (territory to)))
    (mapcar #'(lambda (x)
		(vis-openness to-region (territory x)
			      tile-size viewpoint-d viewpoint-n viewpoint-s)) from)))

(defmethod vis-openness ((to-region territory) (from list) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*)
			 (viewpoint-s *default-viewpoint-s*))
  ;; from arg should be list of regions
  (mapcar #'(lambda (x)
	      (vis-openness to-region x tile-size viewpoint-d viewpoint-n viewpoint-s))
	  from))

;; special case for segments (so can calculate vis-openness from front-door) 
;; easiest fix for now is to find exterior region for segment and use viewpoint-d of 0.0
;; not very safe because some models might not have exterior regions; have to assume they do
;; for now; probably best to add mechanism for designating viewpoints for particular
;; segments rather than just for regions 

(defun exterior-region (regions &optional design-model)
  (unless design-model (setq design-model (design-model (car regions))))
  (loop for r in regions
	when (exteriorp r)
	  do (return-from exterior-region r)))

;;; +++ fix region-for, segment-for, find-front-door
#+ignore
(defmethod vis-openness ((to territory) (from (eql 'front-door)) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*)
			 (viewpoint-s *default-viewpoint-s*))
  viewpoint-d
  (flet ((region-for (segment dmodel)
	   (or (exterior-region (territories segment) dmodel)
			         (car (territories segment)))))
    (let ((dmodel (design-model to)))
      (vis-openness to (region-for
		       (segment-for (find-front-door dmodel) (territory-model to)) dmodel)
		  tile-size 0.0 viewpoint-n viewpoint-s))))

(defmethod vis-openness ((to use-space) (from (eql 'front-door)) &optional
			 (tile-size *default-tile-size*) (viewpoint-d *default-viewpoint-d*)
			 (viewpoint-n *default-viewpoint-n*)
			 (viewpoint-s *default-viewpoint-s*))
  (vis-openness (territory  to) from tile-size viewpoint-d viewpoint-n viewpoint-s))

;; visual openness intersection

(defmethod vis-openness-intersection ((space use-space) (other-spaces list)
				      &optional (tile-size *default-tile-size*)
				      (viewpoint-d *default-viewpoint-d*)
				      (viewpoint-n *default-viewpoint-n*)
				      (viewpoint-s *default-viewpoint-s*))
  (vis-openness-intersection (territory space)
			     (mapcar #'territory other-spaces)
			     tile-size viewpoint-d viewpoint-n viewpoint-s))

(defmethod vis-openness-intersection ((to-region territory) (other-regions list)
				      &optional (tile-size *default-tile-size*)
				      (viewpoint-d *default-viewpoint-d*)
				      (viewpoint-n *default-viewpoint-n*)
				      (viewpoint-s *default-viewpoint-s*))
  (dolist (from-region other-regions)
    (set-visible-tiles to-region from-region tile-size viewpoint-d viewpoint-n
			viewpoint-s))
  ;; count tiles visible from all regions
  (visibility-measure (tiles-visible-from-all-regions to-region other-regions)
				 to-region))

(defmethod vis-openness-union ((space use-space) (other-spaces list)
				      &optional (tile-size *default-tile-size*)
				      (viewpoint-d *default-viewpoint-d*)
				      (viewpoint-n *default-viewpoint-n*)
				      (viewpoint-s *default-viewpoint-s*))
  (vis-openness-union (territory space)
			     (mapcar #'territory other-spaces)
			     tile-size viewpoint-d viewpoint-n viewpoint-s))

(defmethod vis-openness-union ((to-region territory) (other-regions list)
				      &optional (tile-size *default-tile-size*)
				      (viewpoint-d *default-viewpoint-d*)
				      (viewpoint-n *default-viewpoint-n*)
				      (viewpoint-s *default-viewpoint-s*))
  (dolist (from-region other-regions)
    (set-visible-tiles to-region from-region tile-size viewpoint-d viewpoint-n
			viewpoint-s))
  ;; count tiles visible from any of regions
  (visibility-measure (tiles-visible-from-any-region to-region other-regions)
				 to-region))


;; TOPOLOGICAL DISTANCE:  min between 2 spaces, all between 2 spaces,
;;                        min between 1 space and each in list of spaces

;; Very dependent on territory boundaries.

(defmethod min-topological-distance ((region1 territory) (region2 territory)
				 &optional territory-model)
  ;; shortest distance path may not be shortest topological path
  ;; -2 is to remove start and end spaces
  (- (topological-length (shortest-topological-path region1 region2
				   (or territory-model (territory-model region1)))) 2))

(defmethod min-topological-distance ((space1 use-space) (space2 use-space)
				 &optional territory-model)
  (min-topological-distance (territory space1) (territory space2) territory-model))


(defmethod min-topological-distance ((region territory) (regions list)
				 &optional territory-model)
  (unless territory-model (setq territory-model (territory-model region)))
  (mapcar #'(lambda (x) (min-topological-distance region x territory-model))
	  regions))

(defmethod min-topological-distance ((space use-space) (spaces list)
				 &optional territory-model)
  (unless territory-model (setq territory-model (territory-model space)))
  (mapcar #'(lambda (x) (min-topological-distance space x territory-model)) spaces))


(defmethod topological-distances ((region1 territory) (region2 territory)
				 &optional territory-model)
  (unless territory-model (setq territory-model (territory-model region1)))
  (mapcar #'(lambda (x) (- (length (path-signature x)) 2))
	  (paths-from-x-to-y region1 region2 territory-model)))

(defmethod topological-distances ((space1 use-space) (space2 use-space)
				 &optional territory-model)
  (topological-distances (territory space1) (territory space2) territory-model))



;;; PHYSICAL DISTANCES:  along physically shortest path


(defmethod min-physical-distance ((region1 territory) (region2 territory)
				 &optional territory-model)
  (let ((path (path-from-x-to-y region1 region2 (or territory-model
						    (territory-model region1)))))
    ;; don't count first and last links in path, they're to/from centers of spaces; get
    ;; distance to openings of spaces
    (apply '+ (mapcar #'link-distance (cdr (butlast (links path)))))))

(defmethod min-physical-distance ((space1 use-space) (space2 use-space)
				 &optional territory-model)
  ;; from space1 to space2
  (min-physical-distance (territory space1) (territory space2) territory-model))

(defmethod min-physical-distance ((region territory) (regions list)
				 &optional territory-model)
  (unless territory-model (setq territory-model (territory-model region)))
  (mapcar #'(lambda (x) (min-physical-distance region x territory-model)) regions))

(defmethod min-physical-distance ((space use-space) (spaces list)
				 &optional territory-model)
  (unless territory-model (setq territory-model (territory-model space)))
  (mapcar #'(lambda (x) (min-physical-distance space x territory-model)) spaces))


;;;  DISTANCES TO/FROM FRONT ENTRY:  along physically shortest path and topologically
;;;                                  shortest path

(defmethod min-topological-distance-from-front-entry ((space use-space) &optional tmodel)
  (min-topological-distance-from-front-entry (territory space) tmodel))

(defmethod min-topological-distance-from-front-entry ((region territory) &optional tmodel)
  (let ((path (path-from-x-to-y 'front-door region (or tmodel (territory-model region)))))
    (when path (1- (topological-length path)))))		;get rid of center of space

(defmethod min-physical-distance-from-front-entry ((region territory) &optional tmodel)
  (let ((path (path-from-x-to-y 'front-door region (or tmodel (territory-model region)))))
    (apply '+ (mapcar #'link-distance (butlast (links path))))))

(defmethod min-physical-distance-from-front-entry ((space use-space) &optional tmodel)
  (min-physical-distance-from-front-entry (territory space) tmodel))



;;; CIRCUITY TO/FROM FRONT ENTRY:  along physically shortest path, topologically shortest path

;; computes changes in direction along physically shortest path

(defun direction-changes-between-x-and-y (x y bmodel)
  (path-direction-changes (path-from-x-to-y x y bmodel)))

(defmethod direction-changes-from-front-door ((x use-space) bmodel)
  (direction-changes-between-x-and-y 'front-door x bmodel))

(defmethod direction-changes-from-front-door ((x territory) bmodel)
  (direction-changes-between-x-and-y 'front-door x bmodel))

(defmethod direction-changes-from-approach-point ((x use-space) bmodel)
  (direction-changes-between-x-and-y 'approach-point x bmodel))

(defmethod direction-changes-from-approach-point ((x territory) bmodel)
  (direction-changes-between-x-and-y 'approach-point x bmodel))

;; computes changes in direction along topologically shortest path

(defun topo-direction-changes-between-x-and-y (x y bmodel)
  (path-direction-changes (shortest-topological-path x y bmodel)))

(defmethod topo-direction-changes-from-front-door ((x use-space) bmodel)
  (topo-direction-changes-between-x-and-y 'front-door x bmodel))

(defmethod topo-direction-changes-from-front-door ((x territory) bmodel)
  (topo-direction-changes-between-x-and-y 'front-door x bmodel))

(defmethod topo-direction-changes-from-approach-point ((x use-space) bmodel)
  (topo-direction-changes-between-x-and-y 'approach-point x bmodel))

(defmethod topo-direction-changes-from-approach-point ((x territory) bmodel)
  (topo-direction-changes-between-x-and-y 'approach-point x bmodel))


;;; DIRECTION OF ENTRY INTO A SPACE:  along physically shortest path, topologically shortest
;;;   path.  Measures change in direction between link to entrance segment (which corresponds
;;;   to opening into space) and normal to entrance segment.  (This assumes one enters a space
;;;   along the normal to the entrance segment; ignores last link in the path, which connects
;;;   entrance segment and center of space.)

(defun entry-direction-change-along-path (path to-region)
  (when path
    (multiple-value-bind (entrance-seg entrance-pt)
	(destination-entrance-edge path to-region)
      (let ((link (link-to-destination-space-entrance path)))
	(if link
	  (with-segment-normal entrance-seg to-region 1
	    (let ((other-endpt (other-endpoint link entrance-pt)))
	      (direction-change (point-x other-endpt) (point-y other-endpt)
				(point-x entrance-pt) (point-y entrance-pt)
				(+ (point-x entrance-pt) (vectr-x normal-vector))
				(+ (point-y entrance-pt) (vectr-y normal-vector)))))
	  ;; path only had one segment (e.g. from front door to center of space)
	  ;; if return 0.0, can't distinguish between entering on axis with normal
	  ;; if return nil, can't distinguish between having no path to region
	  0.0)))))


(defmethod entry-direction-change ((to-region territory) from  &optional bmodel)
  (entry-direction-change-along-path
    (path-from-x-to-y from to-region (or bmodel (territory-model to-region))) to-region))

(defmethod entry-direction-change ((to-space use-space) from  &optional bmodel)
  (entry-direction-change (territory to-space) from bmodel))

(defmethod topo-entry-direction-change ((to-region territory) from  &optional bmodel)
  (entry-direction-change-along-path
    (shortest-topological-path from to-region (or bmodel (territory-model to-region)))
    to-region))

(defmethod topo-entry-direction-change ((to-space use-space) from  &optional bmodel)
  (topo-entry-direction-change (territory to-space) from bmodel))


;; From front-door

(defmethod entry-direction-change-from-front-door ((region territory) &optional bmodel)
  (entry-direction-change-along-path
    (path-from-x-to-y 'front-door region (or bmodel (territory-model region)))
    region))

(defmethod entry-direction-change-from-front-door ((space use-space) &optional bmodel)
  (entry-direction-change-from-front-door (territory space) bmodel))


(defmethod topo-entry-direction-change-from-front-door ((region territory) &optional bmodel)
  (entry-direction-change-along-path
    (shortest-topological-path 'front-door region (or bmodel (territory-model region)))
    region))

(defmethod topo-entry-direction-change-from-front-door ((space use-space) &optional bmodel)
  (topo-entry-direction-change-from-front-door (territory space) bmodel))


