(DEFINE-FILE-INFO READTABLE "XCL" PACKAGE "PLOT")
(il:filecreated " 1-Dec-88 12:09:49" il:{qv}<idl>next>plot-fns.\;38 112101 

      il:|changes| il:|to:|  (il:functions interval-length ti-length apply-after-method copy-plot 
                                    default-tic-list-fn copy-plot-object icon-fn track-plot-object 
                                    effective-middle-menu delete-plot-objects delete-selected-object
                                    default-menu setf-default-menu delete-plot-object)

      il:|previous| il:|date:| "30-Nov-88 18:31:02" il:{qv}<idl>next>plot-fns.\;37)


; Copyright (c) 1988 by Xerox Corporation.  All rights reserved.

(il:prettycomprint il:plot-fnscoms)

(il:rpaqq il:plot-fnscoms
          ((il:coms 

                  (il:* il:|;;| "datatype def's")

                  (il:structures plot scale-info axis-interval axis-info tic-info margin extent)
                  
                  (il:* il:|;;| "extended access functions")

                  (il:functions plot-x-lower plot-x-upper plot-y-lower plot-y-upper interval-length 
                         ti-length)
                  (il:structures plot-object plot-methods))
           (il:coms 

                  (il:* il:|;;| "plot properties ")

                  (il:functions plot-prop setf-plot-prop)
                  (il:setfs plot-prop)
                  
                  (il:* il:|;;| "plot-object props ")

                  (il:functions plot-object-prop setf-plot-object-prop)
                  (il:setfs plot-object-prop))
           (il:functions apply-after-method)
           (il:functions make-plot copy-plot)
           (il:coms 

                  (il:* il:|;;| "Plot windows")

                  (il:functions open-plot-window close-plot-window redraw-plot-window 
                         min-stream-region-size)
                  (il:functions closest-plot-object)
                  (il:functions repaint-fn close-fn copy-button-event-fn hardcopy-fn icon-fn)
                  (il:functions button-event-fn track-plot-object effective-middle-menu)
                  (il:functions print-plot-prompt))
           (il:coms 

                  (il:* il:|;;| "rendering ")

                  (il:variables *small-plot-font* *large-plot-font*)
                  (il:functions draw-plot)
                  (il:functions update-view-port box-region)
                  
                  (il:* il:|;;| "Tics")

                  (il:functions tics-p setf-tics-p)
                  (il:functions make-tic-lists normalize-tic-list float-to-string enum-to-string 
                         fnum-to-string)
                  (il:functions get-tic-list tic-list-fn setf-tic-list-fn default-tic-list-fn)
                  
                  (il:* il:|;;| "margins")

                  (il:functions compute-top-bottom-margin compute-left-right-margin)
                  (il:functions draw-top-margin draw-bottom-margin draw-left-margin draw-right-margin
                         )
                  (il:functions draw-tics-top-bottom draw-tics-left-right)
                  (il:functions draw-label-top-bottom draw-label-left-right)
                  
                  (il:* il:|;;| "Plot scaling")

                  (il:functions rescale-plot rescale-x-axis rescale-y-axis)
                  (il:functions tic-info-fn setf-tic-info-fn default-tic-info-fn)
                  (il:functions interval-fn setf-interval-fn default-interval-fn 
                         no-change-interval-fn)
                  (il:functions scale plot-objects-extent)
                  (il:functions adjust-scale-p update-extent add-extent-to-extent 
                         add-position-to-extent)
                  (il:functions extend-axis extended-interval-fn)
                  (il:functions plot-world-extent setf-plot-world-extent plot-interval 
                         setf-plot-interval)
                  (il:functions region-to-extent extent-to-region))
           (il:coms 

                  (il:* il:|;;| "plot labels")

                  (il:functions plot-label setf-plot-label))
           (il:coms 

                  (il:* il:|;;| "Plot object methods")

                  (il:functions draw-plot-object erase-plot-object highlight-plot-object 
                         lowlight-plot-object plot-object-extent distance-to-plot-object 
                         label-plot-object unlabel-plot-object move-plot-object copy-plot-object)
                  (il:functions add-plot-object add-plot-objects)
                  (il:functions delete-plot-object delete-plot-objects)
                  (il:functions relabel-selected-object delete-selected-object undelete-last-object)
                  (il:functions translate-plot-object)
                  (il:functions select-plot-object deselect-plot-object))
           (il:coms 

                  (il:* il:|;;| "Plot menus ")

                  (il:functions default-when-selected-fn)
                  (il:functions plot-menu setf-plot-menu)
                  (il:functions plot-menu-items setf-plot-menu-items add-plot-menu-items 
                         delete-plot-menu-items)
                  (il:coms (il:variables *default-middle-menu* *default-right-menu*)
                         (il:functions manually-rescale-plot ask-for-scale toggle-label ask-for-label
                                ask-for-which-label toggle-tics toggle-fixed-right-menu))
                  (il:functions default-menu setf-default-menu)
                  
                  (il:* il:|;;| "Do we want this setf?")

                  (il:setfs default-menu)
                  (il:functions fix-right-menu unfix-right-menu remove-fixed-right-menu))
           (il:coms 

                  (il:* il:|;;| "Misc functions")

                  (il:functions which-plot))
           (xcl:file-environments "PLOT-FNS")))



(il:* il:|;;| "datatype def's")


(defstruct (plot (:constructor %make-plot)
                     (:copier %copy-plot))
   objects
   window
   view-port
   prompt-window
   scale-info
   left-margin
   right-margin
   top-margin
   bottom-margin
   middle-menu
   right-menu
   other-menus
   selected-object
   save-list
   prop-list)

(defstruct (scale-info (:conc-name "SI-"))
   x-interval
   x-axis-info
   x-tic-info
   y-interval
   y-axis-info
   y-tic-info)

(defstruct (axis-interval (:conc-name "INTERVAL-")
                              (:type list))
   min
   max)

(defstruct (axis-info (:conc-name "AI-")
                          (:type list))
   (interval-fn 'default-interval-fn)
   (tic-info-fn 'default-tic-info-fn))

(defstruct (tic-info (:conc-name "TI-"))
   min
   max
   inc
   n)

(defstruct margin
   label
   tic-list
   (tic-list-fn 'default-tic-list-fn)
   tics-p)

(defstruct extent
   min-x
   max-x
   min-y
   max-y)



(il:* il:|;;| "extended access functions")


(defmacro plot-x-lower (plot)
   `(interval-min (si-x-interval (plot-scale-info ,plot))))

(defmacro plot-x-upper (plot)
   `(interval-max (si-x-interval (plot-scale-info ,plot))))

(defmacro plot-y-lower (plot)
   `(interval-min (si-y-interval (plot-scale-info ,plot))))

(defmacro plot-y-upper (plot)
   `(interval-max (si-y-interval (plot-scale-info ,plot))))

(defmacro interval-length (interval)
   (pt:once-only (interval)
          `(- (interval-max ,interval)
              (interval-min ,interval))))

(defmacro ti-length (ti)
   (pt:once-only (ti)
          `(- (ti-max ,ti)
              (ti-min ,ti))))

(defstruct (plot-object (:constructor nil)
                            (:copier nil))
   methods
   menu
   label
   prop-list)

(defstruct (plot-methods (:conc-name "PM-"))
   draw-fn
   erase-fn
   highlight-fn
   lowlight-fn
   extent-fn
   distance-fn
   label-fn
   move-fn
   copy-fn
   put-fn
   get-fn)



(il:* il:|;;| "plot properties ")


(defmacro plot-prop (plot prop)
   `(getf (plot-prop-list ,plot)
          ,prop))

(defun setf-plot-prop (plot prop new-value)
   (setf (getf (plot-prop-list plot)
               prop)
         new-value))

(defsetf plot-prop setf-plot-prop)



(il:* il:|;;| "plot-object props ")


(defmacro plot-object-prop (object prop)
   `(getf (plot-object-prop-list ,object)
          ,prop))

(defun setf-plot-object-prop (object prop new-value)
   (setf (getf (plot-object-prop-list object)
               prop)
         new-value))

(defsetf plot-object-prop setf-plot-object-prop)

(defmacro apply-after-method (method &rest args)
   (pt:once-only (method)
          `(and ,method (funcall ,method ,@args))))

(defun make-plot (&key region title border open-window-p
                           (initial-world-extent (make-extent :min-x 0.0 :max-x 1.0 :min-y 0.0 :max-y
                                                        1.0)))

   (il:* il:|;;| "Creates a PLOT.  If OPEN-WINDOW-P is T then the PLOT's asssociated window is opened.  The other arguments are passed to make-window.")

   (let ((plot (%make-plot :scale-info (let ((min-x (extent-min-x initial-world-extent))
                                             (max-x (extent-max-x initial-world-extent))
                                             (min-y (extent-min-y initial-world-extent))
                                             (max-y (extent-max-y initial-world-extent)))
                                            (make-scale-info :x-interval (make-axis-interval :min 
                                                                                min-x :max max-x)
                                                   :x-axis-info
                                                   (make-axis-info)
                                                   :x-tic-info
                                                   (default-tic-info-fn min-x max-x)
                                                   :y-interval
                                                   (make-axis-interval :min min-y :max max-y)
                                                   :y-axis-info
                                                   (make-axis-info)
                                                   :y-tic-info
                                                   (default-tic-info-fn min-y max-y)))
                      :left-margin
                      (make-margin)
                      :right-margin
                      (make-margin)
                      :top-margin
                      (make-margin)
                      :bottom-margin
                      (make-margin)
                      :middle-menu
                      (default-menu :middle)
                      :right-menu
                      (default-menu :right))))

        (il:* il:|;;| "Cache display parameters until OPENPLOTWINDOW is called")

        (if (or region title border)
            (setf (plot-window plot)
                  (list region title border)))
        (if open-window-p (open-plot-window plot))
        plot))

(defun copy-plot (plot &key region title border open-p)

   (il:* il:|;;| "Copies a PLOT.  Copying of PLOT-PROP's is handled as follows.  If PLOT has a COPY-FN, calls it with NEW-PLOT, PLOT as args, and expects it to copy the PLOT-PROPS intelligently, else only copies the top-level list structure.")

   (let ((new-plot (%copy-plot plot)))

        (il:* il:|;;| "Plot objects are not shared since they may be destructively modified")

        (setf (plot-objects new-plot)
              (mapcar #'(lambda (object)
                               (copy-plot-object object))
                     (plot-objects plot)))

        (il:* il:|;;| "Don't share any display associated objects")

        (setf (plot-window new-plot)
              nil)
        (setf (plot-view-port new-plot)
              nil)
        (setf (plot-prompt-window new-plot)
              nil)

        (il:* il:|;;| "scale-info and margins are side-effected, hence must be copied")

        (setf (plot-scale-info new-plot)
              (copy-scale-info (plot-scale-info plot)))
        (setf (plot-left-margin new-plot)
              (copy-margin (plot-left-margin plot)))
        (setf (plot-right-margin new-plot)
              (copy-margin (plot-right-margin plot)))
        (setf (plot-top-margin new-plot)
              (copy-margin (plot-top-margin plot)))
        (setf (plot-bottom-margin new-plot)
              (copy-margin (plot-bottom-margin plot)))

        (il:* il:|;;| "OK to share Menus")

        (setf (plot-selected-object new-plot)
              nil)
        (setf (plot-save-list new-plot)
              nil)

        (il:* il:|;;| "Hook for copying plot props")

        (setf (plot-prop-list new-plot)
              (pt:with-collection (do* ((prop-name-tail (plot-prop-list plot)
                                               (cddr prop-name-tail))
                                        (prop-name (car prop-name-tail)
                                               (car prop-name-tail))
                                        (prop-value-tail (cdr prop-name-tail)
                                               (cdr prop-name-tail))
                                        (prop-value (car prop-value-tail)
                                               (car prop-value-tail)))
                                       ((null prop-name-tail))
                                     (when prop-value
                                         (pt:collect prop-name)
                                         (pt:collect prop-value)))))
        (if (or region title border)

            (il:* il:|;;| "Cache the display parameters")

            (setf (plot-window new-plot)
                  (list region title border)))
        (apply-after-method (plot-prop plot :when-copied)
               plot new-plot)
        (if open-p (open-plot-window new-plot))
        new-plot))



(il:* il:|;;| "Plot windows")


(defun open-plot-window (plot)

   (il:* il:|;;| 
 "Open window associated with PLOT.  Creates circularities later broken by PLOT-CLOSE-FN")

   (let ((window (plot-window plot)))
        (unless (open-window-p window)
            (if (shrunken-window-p window)

                (il:* il:|;;| "Shrunken window")

                (expand-window window)

                (il:* il:|;;| "Closed window")

                (let ((prompt-window (plot-prompt-window plot))
                      reshape-p prompt-created-p)
                     (if (not (window-p window))
                         (let ((region (if (consp window)
                                           (first window)
                                           (make-region 0 0 100 100)))
                               (title (if (consp window)
                                          (second window)
                                          "Plot Window"))
                               (border (if (consp window)
                                           (third window))))
                              (if (null window)
                                  (setq reshape-p t))
                              (setf (plot-window plot)
                                    (setq window (make-window :region region :title title 
                                                        :border-size border :not-open-p t)))))

                     (il:* il:|;;| "setup plot window methods")

                     (setf (window-method window :repaint)
                           'repaint-fn)
                     (add-window-method window :reshape 'repaint-fn)
                     (add-window-method window :close 'close-fn)
                     (setf (window-method window :button-event)
                           'button-event-fn)
                     (setf (window-method window :right-button-event)
                           'button-event-fn)
                     (setf (window-method window :copy-button-event)
                           'copy-button-event-fn)
                     (setf (window-method window :hardcopy)
                           'hardcopy-fn)
                     (setf (window-method window :shrink)
                           'icon-fn)

                     (il:* il:|;;| "setup plot window props")

                     (setf (window-prop window ':plot)
                           plot)

                     (il:* il:|;;| "Rest of VIEWPORT initializations in REDRAWPLOTWINDOW")

                     (setf (plot-view-port plot)
                           (make-view-port (window-dsp window)))

                     (il:* il:|;;| "Get a prompt window, if none exists")

                     (when (null prompt-window)
                         (setf (plot-prompt-window plot)
                               (setq prompt-window (make-window :region
                                                          (make-region 0 0 100
                                                                 (height-if-window
                                                                  (font-height (default-font :display
                                                                                      ))))
                                                          :not-open-p t)))
                         (setf (stream-scroll prompt-window)
                               :on)
                         (setf (window-method prompt-window :page-full)
                               'identity)
                         (setf (window-max-size prompt-window)
                               (cons most-positive-fixnum (region-height (window-region prompt-window
                                                                                ))))
                         (setq prompt-created-p t))

                     (il:* il:|;;| "Establish a min size for the window")

                     (make-tic-lists plot)
                     (let* ((min-size (min-stream-region-size (window-dsp window)
                                             plot))
                            (min-extent (cons (width-if-window (car min-size)
                                                     (window-border window))
                                              (height-if-window (cdr min-size)
                                                     (window-title window)
                                                     (window-border window)))))
                           (if (null (attached-windows window))
                               (setf (window-min-size window)
                                     min-extent)
                               (setf (main-window-min-size window)
                                     min-extent))
                           (when (and (not reshape-p)
                                      (or (< (window-width window)
                                             (car min-size))
                                          (< (window-height window)
                                             (cdr min-size))))
                               (setq reshape-p t)
                               (print-prompt "Window too small: reshape")))
                     (if reshape-p

                         (il:* il:|;;| "Shaping window implies redrawing it")

                         (reshape-window window)
                         (let ((view-port (plot-view-port plot))
                               (selected-object (plot-selected-object plot)))
                              (open-window window)
                              (update-view-port view-port (stream-clipping-region window)
                                     plot)
                              (draw-plot plot (window-dsp window)
                                     view-port
                                     (stream-clipping-region window))
                              (if selected-object (highlight-plot-object selected-object plot))))

                     (il:* il:|;;| "Attach the promptwindow if necessary")

                     (attach-window prompt-window window :top)

                     (il:* il:|;;| "attach the fixed menu")

                     (if (plot-prop plot :fixed-right-menu-p)
                         (fix-right-menu plot))

                     (il:* il:|;;| "A user hook")

                     (apply-after-method (plot-prop plot :when-opened)
                            plot))))
        window))

(defun close-plot-window (plot)
   (let ((plot-window (plot-window plot)))

        (il:* il:|;;| "Unfix the right menu")

        (unfix-right-menu plot)

        (il:* il:|;;| "Cleanup window methods and props ")

        (when (window-p plot-window)
            (setf (window-prop plot-window :plot)
                  nil)
            (delete-window-method plot-window :reshape 'repaint-fn)
            (delete-window-method plot-window :close 'close-fn)
            (setf (window-method plot-window :button-event)
                  'il:totopw)
            (setf (window-method plot-window :repaint)
                  nil)
            (setf (window-method plot-window :right-button-event)
                  nil)
            (setf (window-method plot-window :copy-button-event)
                  nil)
            (setf (window-method plot-window :hardcopy)
                  nil)
            (setf (window-method plot-window :shrink)
                  nil)
            (detach-all-windows plot-window)
            (close-window plot-window))

        (il:* il:|;;| "A user hook")

        (apply-after-method (plot-prop plot ':when-closed)
               plot)))

(defun redraw-plot-window (plot)

   (il:* il:|;;| "Redraws the PLOTWINDOW of a PLOT")

   (let ((plot-window (plot-window plot)))
        (if (not (open-window-p plot-window))

            (il:* il:|;;| "Assumes OPENPLOTWINDOW will call REDRAWPLOTWINDOW")

            (open-plot-window plot)
            (let ((view-port (plot-view-port plot))
                  (selected-object (plot-selected-object plot))
                  min-size)

                 (il:* il:|;;| "Setup the tic lists ")

                 (make-tic-lists plot)

                 (il:* il:|;;| "Establish a min size for the WINDOW")

                 (setq min-size (min-stream-region-size (window-dsp plot-window)
                                       plot))

                 (il:* il:|;;| 
               "Uses MAINWINDOWMINSIZE since PLOTWINDOW is the main window of a group")

                 (setf (main-window-min-size plot-window)
                       (cons (width-if-window (car min-size)
                                    (window-border plot-window))
                             (height-if-window (cdr min-size)
                                    (window-title plot-window)
                                    (window-border plot-window))))
                 (if (or (< (window-width plot-window)
                            (car min-size))
                         (< (window-height plot-window)
                            (cdr min-size)))
                     (progn (print-prompt "Plotwindow too small: reshape")

                            (il:* il:|;;| "Assumes RESHAPE-WINDOW will call REDRAW-PLOT-WINDOW")

                            (reshape-window plot-window))
                     (progn (update-view-port view-port (stream-clipping-region plot-window)
                                   plot)
                            (clear-window plot-window)
                            (draw-plot plot (window-dsp plot-window)
                                   view-port
                                   (stream-clipping-region plot-window))
                            (if selected-object (highlight-plot-object selected-object view-port plot
                                                       ))))))))

(defun min-stream-region-size (stream plot)

   (il:* il:|;;| "Compute the minimun acceptable size for a plot STREAMREGION.  In the case of PLOTWINDOWS, corresponds to the min exceptable interior size of the WINDOW.  Returns a dotted pair (MINX  . MINY)")

   (il:* il:|;;| "Sizes are (width  . height) pairs")

   (let ((bottom-margin-size (compute-top-bottom-margin stream (plot-bottom-margin plot)
                                    plot))
         (left-margin-size (compute-left-right-margin stream (plot-left-margin plot)
                                  plot))
         (right-margin-size (compute-left-right-margin stream (plot-right-margin plot)
                                   plot))
         (top-margin-size (compute-top-bottom-margin stream (plot-top-margin plot)
                                 plot))
         min-x min-y)

        (il:* il:|;;| "The constant 100 is heuristic")

        (setq min-x (+ (car left-margin-size)
                       (max (car bottom-margin-size)
                            (car top-margin-size)
                            100)
                       (car right-margin-size)))
        (setq min-y (+ (cdr bottom-margin-size)
                       (max (cdr left-margin-size)
                            (cdr right-margin-size)
                            100)
                       (cdr top-margin-size)))
        (cons min-x min-y)))

(defun closest-plot-object (plot stream-position)
   (let (min min-object)
        (dolist (object (plot-objects plot))
            (let ((dist (distance-to-plot-object object stream-position plot)))
                 (if (or (null min)
                         (< dist min))
                     (setq min dist min-object object))))
        min-object))

(defun repaint-fn (window)

   (il:* il:|;;| "Redraws a PLOT WINDOW based on data stored on property list of WINDOW")

   (redraw-plot-window (window-prop window ':plot)))

(defun close-fn (window)
   (close-plot-window (window-prop window ':plot)))

(defun copy-button-event-fn (window)

   (il:* il:|;;| "Allows plots to be copy selected")

   (let ((plot (window-prop window ':plot)))
        (invert-window window)
        (until-mouse-state :up)
        (invert-window window)
        (when (inside-p window (window-cursor window))
            (let ((imageobj (case (pop-up-menu (il:constant (make-menu :items
                                                                   '(("Plot" 'plot)
                                                                     ("Bitmap" 'bitmap)))))
                                (plot (make-plot-image-object plot))
                                (bitmap (make-plot-bitmap-object plot)))))
                 (if imageobj (il:copyinsert imageobj))))))

(defun hardcopy-fn (window printer-stream)

   (il:* il:|;;| "Modified to allow hardcopy of plots on PRESS printers -- no landscape drawing")

   (il:* il:|;;| "Modified to center plot on page")

   (let ((window-region (stream-clipping-region window))
         (plot (window-prop window ':plot))
         (printer-view-port (make-view-port printer-stream))
         printer-clipping-region stream-region)
        (if (eq (il:imagestreamtype printer-stream)
                'il:interpress)
            (let ((micasperinch 2540))
                 (if (> (region-width window-region)
                        (region-height window-region))
                     (progn 
                            (il:* il:|;;| "Print in landscape mode")

                            (il:rotate.ip printer-stream 90)
                            (il:concatt.ip printer-stream)
                            (il:translate.ip printer-stream 0 (truncate (- (* 8.5 micasperinch))))
                            (il:concatt.ip printer-stream)

                            (il:* il:|;;| "Make sure the clippingregion is rational")

                            (il:dspclippingregion (make-region (truncate (* 0.5 micasperinch))
                                                         (truncate (* 0.5 micasperinch))
                                                         (truncate (* 10 micasperinch))
                                                         (truncate (* 7.5 micasperinch)))
                                   printer-stream))

                     (il:* il:|;;| "Make sure the clippingregion is rational")

                     (il:dspclippingregion (make-region (truncate (* 0.5 micasperinch))
                                                  (truncate (* 0.5 micasperinch))
                                                  (truncate (* 7.5 micasperinch))
                                                  (truncate (* 10 micasperinch)))
                            printer-stream))))
        (setq printer-clipping-region (stream-clipping-region printer-stream))

        (il:* il:|;;| "Reset the margins")

        (setf (stream-margin printer-stream :left)
              (region-left printer-clipping-region))
        (setf (stream-margin printer-stream :bottom)
              (region-bottom printer-clipping-region))
        (setf (stream-margin printer-stream :right)
              (1- (+ (region-left printer-clipping-region)
                     (region-width printer-clipping-region))))
        (setf (stream-margin printer-stream :top)
              (1- (+ (region-bottom printer-clipping-region)
                     (region-height printer-clipping-region))))
        (setq stream-region (let* ((k 
                                      (il:* il:|;;| "maintain the PLOTWINDOW's aspect ratio")

                                      (min (truncate (region-width printer-clipping-region)
                                                  (region-width window-region))
                                           (truncate (region-height printer-clipping-region)
                                                  (region-height window-region))))
                                   (swidth (* k (region-width window-region)))
                                   (sheight (* k (region-height window-region))))

                                  (il:* il:|;;| "Center plot on page")

                                  (make-region (+ (region-left printer-clipping-region)
                                                  (truncate (- (region-width printer-clipping-region)
                                                               swidth)
                                                         2))
                                         (+ (region-bottom printer-clipping-region)
                                            (truncate (- (region-height printer-clipping-region)
                                                         sheight)
                                                   2))
                                         swidth sheight)))
        (make-tic-lists plot)
        (update-view-port printer-view-port stream-region plot)
        (draw-plot plot printer-stream printer-view-port stream-region)))

(defun icon-fn (window old-icon)
   (let ((plot (window-prop window ':plot))
         (title-font (il:windowtitlefont))
         icon-width icon-height subregion iconw view-port)
        (if (> (window-width window)
               (window-height window))
            (setq icon-width (width-if-window 100)
                  icon-height
                  (height-if-window (truncate (* 100 (/ (window-height window)
                                                        (window-width window))))))
            (setq icon-width (width-if-window (truncate (* 100 (/ (window-width window)
                                                                  (window-height window)))))
                  icon-height
                  (height-if-window 100)))
        (if old-icon
            (progn (reshape-window old-icon (make-region (region-left (window-region old-icon))
                                                   (region-bottom (window-region old-icon))
                                                   icon-width icon-height))
                   (setq iconw old-icon))
            (progn (setq iconw (make-window :region (position-region icon-width icon-height)))
                   (il:dspfont title-font iconw)))
        (clear-window iconw)
        (setq subregion (make-region (truncate (* 0.1 (window-width iconw)))
                               (truncate (* 0.1 (window-height iconw)))
                               (truncate (* 0.8 (window-width iconw)))
                               (truncate (* 0.8 (window-height iconw)))))
        (setq view-port (make-view-port (window-dsp iconw)
                               subregion
                               (vp-world-region (plot-view-port plot))))
        (box-region subregion iconw)
        (let* ((objects (plot-objects plot))
               (n (length objects)))
              (if (< n 50)

                  (il:* il:|;;| "few enough objects so that all of them may be drawn")

                  (dolist (object objects)
                      (draw-plot-object object view-port plot))

                  (il:* il:|;;| "Sample the display list")

                  (let ((sampling-rate (truncate n 50))
                        (i 0))
                       (dolist (object objects)
                           (if (pt:fixnum-eq i sampling-rate)
                               (progn (draw-plot-object object view-port plot)
                                      (setq i 0))
                               (incf i))))))
        (il:centerprintinregion (or (plot-label plot ':top)
                                    (if (not (string= (window-title window)
                                                    "Plot Window"))
                                        (window-title window))
                                    "Plot Icon")
               nil iconw)
        iconw))

(defun button-event-fn (window)
   (to-top-window window)
   (let* ((plot (window-prop window ':plot))
          (view-port (plot-view-port plot))
          (prompt-window (plot-prompt-window plot))
          (selected-object (plot-selected-object plot)))
         (cond
            ((last-mouse-state-p :left)

             (il:* il:|;;| "Plot object tracking")

             (track-plot-object window))
            ((last-mouse-state-p :middle)
             (when selected-object
                 (let ((menu (effective-middle-menu selected-object plot)))
                      (when menu

                          (il:* il:|;;| "establish then break circularities")

                          (setf (menu-prop menu ':plot)
                                plot)
                          (setf (menu-prop menu ':mode)
                                :middle)
                          (pop-up-menu menu)
                          (setf (menu-prop menu ':mode)
                                nil)
                          (setf (menu-prop menu ':plot)
                                nil)))))
            ((last-mouse-state-p :right)
             (let ((right-menu (plot-right-menu plot)))
                  (if (or (plot-prop plot ':fixed-right-menu-p)
                          (> (position-y (window-cursor window))
                             (window-height window))
                          (null right-menu))

                      (il:* il:|;;| "bring up the window-manager menu")

                      (il:dowindowcom window)
                      (progn (setf (menu-prop right-menu ':plot)
                                   plot)
                             (pop-up-menu right-menu)
                             (setf (menu-prop right-menu ':plot)
                                   nil))))))))

(defun track-plot-object (window)
   (let* ((plot (window-prop window ':plot))
          (view-port (plot-view-port plot))
          (prompt-window (plot-prompt-window plot))
          (selected-object (plot-selected-object plot))
          (old-x 0)
          (old-y 0)
          (stream-subregion (vp-stream-subregion view-port))
          (position (make-position))
          new-x new-y new-selected-object)
         (loop (unless (mouse-state-p :left)
                      (return nil))
               (setf (position-x position)
                     (setq new-x (last-mouse-x window)))
               (setf (position-y position)
                     (setq new-y (last-mouse-y window)))
               (if (inside-p stream-subregion position)
                   (unless (and (pt:fixnum-eq old-x new-x)
                                (pt:fixnum-eq old-y new-y))
                       (setq new-selected-object (closest-plot-object plot position))
                       (when (and new-selected-object (not (eq new-selected-object selected-object)))
                           (if selected-object (lowlight-plot-object selected-object view-port plot))
                           (highlight-plot-object new-selected-object view-port plot)
                           (setf (plot-selected-object plot)
                                 (setq selected-object new-selected-object))

                           (il:* il:|;;| 
                         "Try to print a meaningfull message in the PLOTPROMPTWINDOW")

                           (let ((label (plot-object-label new-selected-object)))
                                (if label
                                    (print-plot-prompt label plot)
                                    (terpri prompt-window)))))
                   (when selected-object
                       (lowlight-plot-object selected-object view-port plot)
                       (setf (plot-selected-object plot)
                             (setq selected-object nil))))
               (setq old-x new-x)
               (setq old-y new-y))))

(defun effective-middle-menu (selected-object plot)
   (let* ((object-menu (plot-object-menu selected-object))
          (other-menus (plot-other-menus plot)))
         (if object-menu
             (if (menu-p object-menu)
                 object-menu

                 (il:* il:|;;| "Should be a symbol")

                 (getf other-menus object-menu))
             (let ((object-type (type-of-object selected-object)))
                  (or (getf other-menus object-type)
                      (if (pt:memq object-type *plot-object-types*)
                          (let ((default-menu (default-menu object-type)))
                               (and default-menu (setf-plot-menu plot object-type default-menu t))))
                      (plot-middle-menu plot))))))

(defun print-plot-prompt (text plot)
   (let ((prompt-window (plot-prompt-window plot)))
        (terpri prompt-window)
        (princ text prompt-window)))



(il:* il:|;;| "rendering ")


(defvar *small-plot-font* (make-font :family '(:gacha 8 :mrr)))

(defvar *large-plot-font* (make-font :family '(:gacha 12 :brr)))

(defun draw-plot (plot stream view-port stream-region)

   (il:* il:|;;| "Draws a plot on STREAM.  STREAM-REGION is the region the PLOT will occupy.  Does not clear STREAM-REGION before drawing.")

   (il:* il:|;;| "Will not check, for the moment, that the stream-region is large enough")

   (box-region (vp-stream-subregion view-port)
          stream)
   (draw-bottom-margin stream view-port stream-region plot)
   (draw-left-margin stream view-port stream-region plot)
   (draw-top-margin stream view-port stream-region plot)
   (draw-right-margin stream view-port stream-region plot)
   (dolist (object (plot-objects plot))
       (draw-plot-object object view-port plot)))

(defun update-view-port (view-port stream-region plot)
   (let* ((scale-info (plot-scale-info plot))
          (parent-stream (vp-parent-stream view-port))
          (top-margin-size (compute-top-bottom-margin parent-stream (plot-top-margin plot)
                                  plot))
          (bottom-margin-size (compute-top-bottom-margin parent-stream (plot-bottom-margin plot)
                                     plot))
          (left-margin-size (compute-left-right-margin parent-stream (plot-left-margin plot)
                                   plot))
          (right-margin-size (compute-left-right-margin parent-stream (plot-right-margin plot)
                                    plot)))
         (setf (vp-world-region view-port)
               (make-region (interval-min (si-x-interval scale-info))
                      (interval-min (si-y-interval scale-info))
                      (interval-length (si-x-interval scale-info))
                      (interval-length (si-y-interval scale-info))))
         (setf (vp-stream-subregion view-port)
               (make-region (+ (region-left stream-region)
                               (car left-margin-size))
                      (+ (region-bottom stream-region)
                         (cdr bottom-margin-size))
                      (- (region-width stream-region)
                         (+ (car left-margin-size)
                            (car right-margin-size)))
                      (- (region-height stream-region)
                         (+ (cdr bottom-margin-size)
                            (cdr top-margin-size)))))
         (compute-transform view-port)))

(defun box-region (region stream)               (il:* il:\; "Edited  5-May-87 18:16 by jop")

   (il:* il:|;;| "Draw a box around a region in STREAM")

   (let ((rleft (region-left region))
         (rbottom (region-bottom region))
         (rright (region-right region))
         (rtop (region-top region))
         (linewidth (stream-scale stream)))
        (stream-draw-line rleft rbottom rright rbottom stream :width linewidth :operation boole-1)
        (stream-draw-line rright rbottom rright rtop stream :width linewidth :operation boole-1)
        (stream-draw-line rright rtop rleft rtop stream :width linewidth :operation boole-1)
        (stream-draw-line rleft rtop rleft rbottom stream :width linewidth :operation boole-1)))



(il:* il:|;;| "Tics")


(defun tics-p (plot margin-name)
   (margin-tics-p (ecase margin-name
                      (:bottom (plot-bottom-margin plot))
                      (:left (plot-left-margin plot))
                      (:top (plot-top-margin plot))
                      (:right (plot-right-margin plot)))))

(defun setf-tics-p (plot margin-name new-state &optional no-update-p)
   (let ((margin (ecase margin-name
                     (:bottom (plot-bottom-margin plot))
                     (:left (plot-left-margin plot))
                     (:top (plot-top-margin plot))
                     (:right (plot-right-margin plot)))))
        (setf (margin-tics-p margin)
              new-state)
        (if (null no-update-p)
            (redraw-plot-window plot))
        new-state))

(defun make-tic-lists (plot)                    (il:* il:\; "Edited  7-May-87 18:08 by jop")
   (flet ((make-margin-tics (margin tic-info scale-info plot)
                 (if (margin-tics-p margin)
                     (setf (margin-tic-list margin)
                           (normalize-tic-list (get-tic-list margin tic-info scale-info plot))))))
         (let ((scale-info (plot-scale-info plot)))
              (make-margin-tics (plot-bottom-margin plot)
                     (si-x-tic-info scale-info)
                     scale-info plot)
              (make-margin-tics (plot-left-margin plot)
                     (si-y-tic-info scale-info)
                     scale-info plot)
              (make-margin-tics (plot-right-margin plot)
                     (si-y-tic-info scale-info)
                     scale-info plot)
              (make-margin-tics (plot-top-margin plot)
                     (si-x-tic-info scale-info)
                     scale-info plot)
              nil)))

(defun normalize-tic-list (tic-list)            (il:* il:\; "Edited 27-May-87 18:19 by jop")
   (mapcar #'(lambda (tic)
                    (let (value label)
                         (if (consp tic)
                             (setq value (car tic)
                                   label
                                   (cdr tic))
                             (setq value (setq label tic)))
                         (cons value (if (floatp label)
                                         (float-to-string label)
                                         label))))
          tic-list))

(defun float-to-string (x)                      (il:* il:\; "Edited  7-May-87 17:23 by jop")
   (setq x (float x))

   (il:* il:|;;| "Pure common lisp would be: ")

   (il:* il:|;;| "(string-left-trim '(#\\Space) (if (let ((abs-x (abs x))) (and (not (zerop abs-x)) (or (< abs-x 0.001) (>= abs-x 1.0E+7)))) (format nil \"~10e\" x) (format nil \"~7f\" x)))")

   (let ((string (make-array 14 :element-type 'string-char :fill-pointer 0))
         (minus-p (and (< x 0.0)
                       (setq x (- x))))
         (round 6)
         numstr intexp)
        (if (and (not (zerop x))
                 (or (< x 0.001)
                     (>= x 1.0E+7)))
            (multiple-value-bind (numstr intexp)
                   (il:fltstr x round)
                   (enum-to-string string numstr intexp minus-p))
            (multiple-value-bind (numstr intexp)
                   (il:fltstr x round)
                   (fnum-to-string string numstr intexp minus-p)))))

(defun enum-to-string (output-string mantissa-string integer-exponent minus-p)

   (il:* il:|;;| "Prints exponential notation observing rounding & exponent spacing")

   (macrolet ((push-output (char)
                     `(progn (setf (aref output-string index)
                                   ,char)
                             (setq index (1+ index)))))
          (let ((digits (length mantissa-string))
                (index 0)
                exp-offset)
               (when minus-p (push-output #\-))

               (il:* il:|;;| "Print the mantissa")

               (push-output (aref mantissa-string 0))
               (push-output #\.)
               (do ((i 1 (1+ i)))
                   ((eq i digits))
                 (push-output (aref mantissa-string i)))

               (il:* il:|;;| "Trim off extraneous zeros")

               (do ((i (1- index)
                       (1- i)))
                   ((not (eq (aref output-string i)
                             #\0))
                    (if (not (eq (aref output-string i)
                                 #\.))
                        (setq index (1+ i))
                        (setq index (+ i 2)))))

               (il:* il:|;;| "mantissa done - now for the exponent")

               (setq exp-offset (il:mkstring (- (+ integer-exponent digits)
                                                1)))
               (push-output #\E)
               (dotimes (i (length exp-offset))
                   (push-output (aref exp-offset i)))
               (setf (fill-pointer output-string)
                     index)
               output-string)))

(defun fnum-to-string (output-string mantissa-string integer-exponent minus-p)
   (macrolet ((push-output (char)
                     `(progn (setf (aref output-string index)
                                   ,char)
                             (setq index (1+ index)))))
          (let* ((digits (length mantissa-string))
                 (point-place (+ digits integer-exponent))
                 (index 0))
                (if minus-p (push-output #\-))
                (cond
                   ((< point-place 0)
                    (push-output #\0)
                    (push-output #\.)
                    (dotimes (i (- point-place))
                        (push-output #\0))
                    (dotimes (i digits)
                        (push-output (aref mantissa-string i))))
                   ((< integer-exponent 0)
                    (dotimes (i point-place)
                        (push-output (aref mantissa-string i)))
                    (push-output #\.)
                    (do ((i point-place (1+ i)))
                        ((eq i digits))
                      (push-output (aref mantissa-string i))))
                   (t (dotimes (i digits)
                          (push-output (aref mantissa-string i)))
                      (dotimes (i integer-exponent)
                          (push-output #\0))
                      (push-output #\.)
                      (push-output #\0)))
                (if (or (< point-place 0)
                        (< integer-exponent 0))

                    (il:* il:|;;| "Trim off extraneous zeros")

                    (do ((i (1- index)
                            (1- i)))
                        ((not (eq (aref output-string i)
                                  #\0))
                         (if (not (eq (aref output-string i)
                                      #\.))
                             (setq index (1+ i))
                             (setq index (+ i 2))))))
                (setf (fill-pointer output-string)
                      index)
                output-string)))

(defun get-tic-list (margin tic-info scale-info plot)
   (let ((ticmethod (margin-tic-list-fn margin)))
        (if (and (consp ticmethod)
                 (not (eq (car ticmethod)
                          'lambda)))
            ticmethod
            (funcall ticmethod tic-info scale-info plot))))

(defun tic-list-fn (plot margin-name)
   (margin-tic-list-fn (ecase margin-name
                           (:bottom (plot-bottom-margin plot))
                           (:left (plot-left-margin plot))
                           (:top (plot-top-margin plot))
                           (:right (plot-right-margin plot)))))

(defun setf-tic-list-fn (plot margin-name new-method &optional no-update-p)

   (il:* il:|;;| "NEW-METHOD may be a list of numbers, or a list of CONS pairs (VALUE  . LABEL), or a function to be APPLIED to MARGIN SCALE-INFO PLOT.")

   (let ((margin (ecase margin-name
                     (:bottom (plot-bottom-margin plot))
                     (:left (plot-left-margin plot))
                     (:top (plot-top-margin plot))
                     (:right (plot-right-margin plot)))))
        (setf (margin-tic-list-fn margin)
              new-method)
        (if (and (null no-update-p)
                 (margin-tics-p margin))
            (redraw-plot-window plot))
        new-method))

(defun default-tic-list-fn (tic-info scale-info &optional plot)
   (declare (ignore scale-info plot))

   (il:* il:|;;| "Return the default tic list based on the values of PLOTSCALE")

   (let ((tic-inc (ti-inc tic-info)))
        (cond
           ((consp tic-inc)
            tic-inc)
           ((numberp tic-inc)

            (il:* il:|;;| "Be carefull that min and max tics correspond to min and max of interval")

            (nconc (let ((x (ti-min tic-info)))
                        (pt:with-collection (dotimes (i (1- (ti-n tic-info)))
                                                (pt:collect x)
                                                (incf x tic-inc))))
                   (list (ti-max tic-info))))
           (t (error "Invalid increment: ~s" tic-inc)))))



(il:* il:|;;| "margins")


(defun compute-top-bottom-margin (stream top-bottom-margin plot)

   (il:* il:|;;| "Returns a size cons pair (width  . height) in stream coordinates")

   (let* ((small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (tics-p (margin-tics-p top-bottom-margin))
          (label (margin-label top-bottom-margin))
          (width 0)
          (small-ascent (font-ascent small-font))
          (large-height (font-height large-font))
          (height (if (or tics-p label)
                      large-height

                      (il:* il:|;;| "margin of at least one LARGE-HEIGHT")

                      (* 2 large-height))))
         (if tics-p
             (incf height (* 3 small-ascent)))
         (if label
             (setq height (+ height (* 2 large-height))
                   width
                   (string-width label large-font)))
         (cons width height)))

(defun compute-left-right-margin (stream left-right-margin plot)

   (il:* il:|;;| "Returns a size cons pair (width  . height) in stream coordinates")

   (let* ((small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (tics-p (margin-tics-p left-right-margin))
          (tic-list (margin-tic-list left-right-margin))
          (label (margin-label left-right-margin))
          (height 0)

          (il:* il:|;;| "Assumes fixed-pitch font")

          (small-width (string-width #\a small-font))
          (large-width (string-width #\a large-font))
          (width (if (or tics-p label)
                     large-width
                     (* 2 large-width))))
         (if tics-p
             (incf width (+ (* 2 small-width)
                            (let ((tic-width-max 0))
                                 (dolist (pair tic-list)
                                     (let ((length (string-width (cdr pair)
                                                          small-font)))
                                          (if (> length tic-width-max)
                                              (setq tic-width-max length))))
                                 tic-width-max))))
         (if label
             (setq width (+ width (* 2 large-width))
                   height
                   (* (length label)
                      (font-height large-font))))
         (cons width height)))

(defun draw-top-margin (stream view-port stream-region plot)

   (il:* il:|;;| "DRAW the Top MARGIN")

   (let* ((top-margin (plot-top-margin plot))
          (small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (label (margin-label top-margin))
          (x-interval (si-x-interval (plot-scale-info plot)))
          (small-ascent (font-ascent small-font))
          (top (region-top (vp-stream-subregion view-port))))
         (if (margin-tics-p top-margin)

             (il:* il:|;;| "DRAW TICS and TIC labels if necessary")

             (draw-tics-top-bottom (margin-tic-list top-margin)
                    (interval-min x-interval)
                    (interval-max x-interval)
                    (+ small-ascent top)
                    (- top small-ascent)
                    small-ascent small-font stream view-port))
         (if label
             (draw-label-top-bottom label large-font (- (region-top stream-region)
                                                        (+ (font-height large-font)
                                                           (font-ascent stream)))
                    stream-region stream))))

(defun draw-bottom-margin (stream view-port stream-region plot)

   (il:* il:|;;| "DRAW the bottom MARGIN")

   (let* ((bottom-margin (plot-bottom-margin plot))
          (small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (label (margin-label bottom-margin))
          (x-interval (si-x-interval (plot-scale-info plot)))
          (small-ascent (font-ascent small-font))
          (bottom (region-bottom (vp-stream-subregion view-port))))
         (if (margin-tics-p bottom-margin)

             (il:* il:|;;| "DRAW TICS and TIC labels if necessary")

             (draw-tics-top-bottom (margin-tic-list bottom-margin)
                    (interval-min x-interval)
                    (interval-max x-interval)
                    (+ small-ascent bottom)
                    (- bottom small-ascent)
                    (* 2 small-ascent)
                    small-font stream view-port t))
         (if label
             (draw-label-top-bottom label large-font (+ (region-bottom stream-region)
                                                        (+ (font-descent stream)
                                                           (font-height large-font)))
                    stream-region stream))))

(defun draw-left-margin (stream view-port stream-region plot)

   (il:* il:|;;| "DRAW the left MARGIN")

   (let* ((left-margin (plot-left-margin plot))
          (small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (label (margin-label left-margin))
          (y-interval (si-y-interval (plot-scale-info plot)))
          (small-width (string-width #\a small-font))
          (left (region-left (vp-stream-subregion view-port))))
         (if (margin-tics-p left-margin)

             (il:* il:|;;| "DRAW TICS and TIC labels if necessary")

             (draw-tics-left-right (margin-tic-list left-margin)
                    (interval-min y-interval)
                    (interval-max y-interval)
                    (+ small-width left)
                    (- left small-width)
                    small-width small-font stream view-port t))
         (if label
             (draw-label-left-right label large-font (+ (region-left stream-region)
                                                        (string-width #\a large-font))
                    stream-region stream))))

(defun draw-right-margin (stream view-port stream-region plot)

   (il:* il:|;;| "DRAW the right MARGIN")

   (let* ((right-margin (plot-right-margin plot))
          (small-font (coerce-font *small-plot-font* stream))
          (large-font (coerce-font *large-plot-font* stream))
          (label (margin-label right-margin))
          (y-interval (si-y-interval (plot-scale-info plot)))
          (small-width (string-width #\a small-font))
          (right (region-right (vp-stream-subregion view-port))))
         (if (margin-tics-p right-margin)

             (il:* il:|;;| "DRAW TICS and TIC labels if necessary")

             (draw-tics-left-right (margin-tic-list right-margin)
                    (interval-min y-interval)
                    (interval-max y-interval)
                    (+ small-width right)
                    (- right small-width)
                    small-width small-font stream view-port))
         (if label
             (draw-label-left-right label large-font (- (region-right stream-region)
                                                        (* 2 (string-width #\a large-font)))
                    stream-region stream))))

(defun draw-tics-top-bottom (ticlist min max top-of-tic bottom-of-tic tic-offset tic-font stream
                                       view-port bottom-p)
   (let ((font (stream-font stream)))
        (setf (stream-font stream)
              tic-font)
        (let (x-window-loc)
             (dolist (ticpair ticlist)
                 (let ((tic-value (car ticpair))
                       (tic-label (cdr ticpair)))
                      (when (and (>= tic-value min)
                                 (<= tic-value max))
                          (setq x-window-loc (world-to-stream-x tic-value view-port))

                          (il:* il:|;;| "always draw the tic mark")

                          (stream-move-to x-window-loc top-of-tic stream)
                          (stream-draw-to x-window-loc bottom-of-tic stream :width (stream-scale
                                                                                    stream)
                                 :operation boole-1)
                          (when tic-label
                              (if bottom-p
                                  (stream-move-to x-window-loc (- bottom-of-tic tic-offset)
                                         stream)
                                  (stream-move-to x-window-loc (+ top-of-tic tic-offset)
                                         stream))
                              (stream-relative-move-to (- (truncate (string-width tic-label tic-font)
                                                                 2))
                                     0 stream)
                              (princ tic-label stream))))))
        (setf (stream-font stream)
              font)))

(defun draw-tics-left-right (ticlist min max right-tic left-tic tic-offset tic-font stream 
                                       view-port left-p)
   (let ((font (stream-font stream)))
        (setf (stream-font stream)
              tic-font)
        (let (y-window-loc)
             (dolist (ticpair ticlist)
                 (let ((tic-value (car ticpair))
                       (tic-label (cdr ticpair)))
                      (when (and (>= tic-value min)
                                 (<= tic-value max))
                          (setq y-window-loc (world-to-stream-y tic-value view-port))
                          (stream-move-to left-tic y-window-loc stream)
                          (stream-draw-to right-tic y-window-loc stream :width (stream-scale stream)
                                 :operation boole-1)
                          (when tic-label
                              (if left-p
                                  (stream-move-to (- left-tic (+ tic-offset (string-width tic-label 
                                                                                   stream)))
                                         y-window-loc stream)
                                  (stream-move-to (+ right-tic tic-offset)
                                         y-window-loc stream))
                              (princ tic-label stream))))))
        (setf (stream-font stream)
              font)))

(defun draw-label-top-bottom (label label-font y-offset stream-region stream)
   (let ((font (stream-font stream)))
        (setf (stream-font stream)
              label-font)
        (stream-move-to (+ (region-left stream-region)
                           (max 0 (truncate (- (region-width stream-region)
                                               (string-width label stream))
                                         2)))
               y-offset stream)
        (princ label stream)
        (setf (stream-font stream)
              font)))

(defun draw-label-left-right (label label-font x-offset stream-region stream)
   (let ((font (stream-font stream)))
        (setf (stream-font stream)
              label-font)
        (stream-move-to x-offset (- (region-top stream-region)
                                    (truncate (- (region-height stream-region)
                                                 (* (font-height stream)
                                                    (length label)))
                                           2))
               stream)
        (let ((lf (stream-line-feed stream)))
             (dotimes (i (length label))
                 (princ (aref label i)
                        stream)
                 (stream-move-to x-offset (+ (stream-y stream)
                                             lf)
                        stream)))
        (setf (stream-font stream)
              font)))



(il:* il:|;;| "Plot scaling")


(defun rescale-plot (plot &optional (axis :both)
                              no-update-p)
   (let ((plot-extent (plot-objects-extent (plot-objects plot)
                             plot)))
        (when plot-extent
            (let ((min-x (extent-min-x plot-extent))
                  (max-x (extent-max-x plot-extent))
                  (min-y (extent-min-y plot-extent))
                  (max-y (extent-max-y plot-extent)))
                 (if (and (or (eq axis ':both)
                              (eq axis ':x))
                          (> max-x min-x))
                     (rescale-x-axis plot min-x max-x))
                 (if (and (or (eq axis ':both)
                              (eq axis ':y))
                          (> max-y min-y))
                     (rescale-y-axis plot min-y max-y))
                 (if (null no-update-p)
                     (redraw-plot-window plot))))))

(defun rescale-x-axis (plot min max &key tic-info-fn interval-fn)
   (let* ((scale-info (plot-scale-info plot))
          (axis-info (si-x-axis-info scale-info))
          (tic-info (funcall (or tic-info-fn (ai-tic-info-fn axis-info))
                           min max plot))
          (interval (funcall (or interval-fn (ai-interval-fn axis-info))
                           min max tic-info plot)))
         (setf (si-x-tic-info scale-info)
               tic-info)
         (setf (si-x-interval scale-info)
               interval)))

(defun rescale-y-axis (plot min max &key tic-info-fn interval-fn)
   (let* ((scale-info (plot-scale-info plot))
          (axis-info (si-y-axis-info scale-info))
          (tic-info (funcall (or tic-info-fn (ai-tic-info-fn axis-info))
                           min max plot))
          (interval (funcall (or interval-fn (ai-interval-fn axis-info))
                           min max tic-info plot)))
         (setf (si-y-tic-info scale-info)
               tic-info)
         (setf (si-y-interval scale-info)
               interval)))

(defun tic-info-fn (plot axis)
   (ai-tic-info-fn (ecase axis
                       (:x (si-x-axis-info (plot-scale-info plot)))
                       (:y (si-y-axis-info (plot-scale-info plot))))))

(defun setf-tic-info-fn (plot axis new-fn &optional no-update-p)
   (let ((axis-info (ecase axis
                        (:x (si-x-axis-info (plot-scale-info plot)))
                        (:y (si-y-axis-info (plot-scale-info plot))))))
        (setf (ai-tic-info-fn axis-info)
              new-fn)
        (rescale-plot plot axis no-update-p)
        new-fn))

(defun default-tic-info-fn (min max &optional plot (tics '(3 4 5 6 7 8)))
   (declare (ignore plot default-tic-info-fn))

   (il:* il:|;;| "Computes an interval that includes (MIN,MAX) and can be exactly spanned by (NTICS-1) *some increment.")

   (let* ((shortest (scale min max (car tics)))
          (shortest-length (ti-length shortest)))
         (dolist (n-tics (cdr tics))
             (let* ((current (scale min max n-tics))
                    (current-length (ti-length current)))
                   (if (< current-length shortest-length)
                       (setq shortest-length current-length shortest current))))
         shortest))

(defun interval-fn (plot axis)
   (ai-interval-fn (ecase axis
                       (:x (si-x-axis-info (plot-scale-info plot)))
                       (:y (si-y-axis-info (plot-scale-info plot))))))

(defun setf-interval-fn (plot axis new-fn &optional no-update-p)
   (let ((axis-info (ecase axis
                        (:x (si-x-axis-info (plot-scale-info plot)))
                        (:y (si-y-axis-info (plot-scale-info plot))))))
        (setf (ai-interval-fn axis-info)
              new-fn)
        (rescale-plot plot axis no-update-p)
        new-fn))

(defun default-interval-fn (min max tic-info &optional plot)
   (declare (ignore min max plot))
   (make-axis-interval :min (ti-min tic-info)
          :max
          (ti-max tic-info)))

(defun no-change-interval-fn (min max tic-info plot)
   (declare (ignore tic-info plot))
   (make-axis-interval :min min :max max))

(defun scale (min max n-tics &optional (round 
                                                  (il:* il:|;;| 
                   "Rounding Constants.  Notice that they are in decreasing order and end with 1.0")

                                                  '(5.0 2.5 2.0 1.5 1.0))
                      power)

   (il:* il:|;;| "Scaling algorithm for plots.  NTICS is the desired number of tics.  Round is a list of acceptable scaling factors.  POWER is the power of ten to use.  Returns a TICINFO including NEWMAX, NEWMIN, INC, and NTICS")

   (let* ((n-inc (1- n-tics))
          (raw-inc (/ (float (- max min))
                      n-inc))
          mantissa index)

         (il:* il:|;;| "POWER is the power of ten")

         (setq power (expt 10.0 (or power (floor (log raw-inc 10.0)))))

         (il:* il:|;;| "MANTISSA is the scale factor")

         (setq mantissa (/ raw-inc power))
         (if (> mantissa (first round))
             (setq power (* 10.0 power)
                   index
                   (last round))
             (setq index (do ((mark round (cdr mark)))
                             ((null (cdr mark))
                              mark)
                           (if (> mantissa (second mark))
                               (return mark)))))

         (il:* il:|;;| "Find new max and new min")

         (let ((new-max min)
               new-min inc factor lower-mult upper-mult)
              (loop (unless (< new-max max)
                        (return (make-tic-info :min new-min :max new-max :inc inc :n n-tics)))
                    (setq inc (* (first index)
                                 power))
                    (setq factor (/ (- (+ max min)
                                       (* n-inc inc))
                                    (* 2.0 inc)))
                    (setq new-min (* inc (setq lower-mult (ceiling factor))))
                    (if (> new-min min)
                        (setq new-min (* inc (setq lower-mult (1- lower-mult)))))
                    (if (and (>= min 0.0)
                             (minusp new-min))
                        (setq lower-mult 0 new-min 0.0))
                    (setq upper-mult (+ lower-mult n-inc))
                    (setq new-max (* inc upper-mult))
                    (if (and (<= max 0.0)
                             (> new-max 0.0))
                        (setq upper-mult 0 new-max 0.0 lower-mult (- n-inc)
                              new-min
                              (* inc lower-mult)))
                    (if (eq round index)
                        (setq index (last round)
                              power
                              (* 10.0 power))
                        (setq index (do ((mark round (cdr mark)))
                                        ((eq (cdr mark)
                                             index)
                                         mark))))))))

(defun plot-objects-extent (objects plot)

   (il:* il:|;;| "May return NIL if no objects report a bounding box.")

   (let (extent)
        (dolist (object objects)
            (let ((object-info (plot-object-extent object plot)))
                 (when object-info
                     (setq extent (if (null extent)
                                      object-info
                                      (if (
                                           (il:* il:|;;| "should be position-p")

                                           consp object-info)

                                          (il:* il:|;;| "A position reported")

                                          (if (consp extent)
                                              (make-extent :min-x (min (position-x extent)
                                                                       (position-x object-info))
                                                     :max-x
                                                     (max (position-x extent)
                                                          (position-x object-info))
                                                     :min-y
                                                     (min (position-y extent)
                                                          (position-y object-info))
                                                     :max-y
                                                     (max (position-y extent)
                                                          (position-y object-info)))
                                              (add-position-to-extent extent object-info))

                                          (il:* il:|;;| "an extent reported")

                                          (if (consp extent)
                                              (add-position-to-extent (copy-extent object-info)
                                                     extent)
                                              (add-extent-to-extent extent object-info))))))))
        (if (consp extent)
            (add-position-to-extent (plot-world-extent plot)
                   extent)
            extent)))

(defun adjust-scale-p (extent plot)

   (il:* il:|;;| "Determines whether the plotting scale must be adjusted to included the extrema 'min-x' , 'max-x' , etc.  If so returns T.  Side effects the scale-info of PLOT")

   (when extent
       (let* ((scale-info (plot-scale-info plot))
              (x-interval (si-x-interval scale-info))
              (y-interval (si-y-interval scale-info))
              min-x max-x min-y max-y changed-p)
             (if (position-p extent)
                 (setq min-x (position-x extent)
                       max-x
                       (position-x extent)
                       min-y
                       (position-y extent)
                       max-y
                       (position-y extent))
                 (setq min-x (extent-min-x extent)
                       max-x
                       (extent-max-x extent)
                       min-y
                       (extent-min-y extent)
                       max-y
                       (extent-max-y extent)))
             (when (or (< min-x (interval-min x-interval))
                       (> max-x (interval-max x-interval)))
                 (setq changed-p t)
                 (rescale-x-axis plot (min min-x (interval-min x-interval))
                        (max max-x (interval-max x-interval))))
             (when (or (< min-y (interval-min y-interval))
                       (> max-y (interval-max y-interval)))
                 (setq changed-p t)
                 (rescale-y-axis plot (min min-y (interval-min y-interval))
                        (max max-y (interval-max y-interval))))
             changed-p)))

(defun update-extent (old-extent new-extent)

   (il:* il:|;;| "side effects OLD-EXTENT")

   (if (< (extent-min-x new-extent)
          (extent-min-x old-extent))
       (setf (extent-min-x old-extent)
             (extent-min-x new-extent)))
   (if (> (extent-max-x new-extent)
          (extent-max-x old-extent))
       (setf (extent-max-x old-extent)
             (extent-max-x new-extent)))
   (if (< (extent-min-y new-extent)
          (extent-min-y old-extent))
       (setf (extent-min-y old-extent)
             (extent-min-y new-extent)))
   (if (> (extent-max-y new-extent)
          (extent-max-y old-extent))
       (setf (extent-max-y old-extent)
             (extent-max-y new-extent)))
   old-extent)

(defun add-extent-to-extent (old-extent new-extent)

   (il:* il:|;;| "side effects OLD-EXTENT")

   (if (< (extent-min-x new-extent)
          (extent-min-x old-extent))
       (setf (extent-min-x old-extent)
             (extent-min-x new-extent)))
   (if (> (extent-max-x new-extent)
          (extent-max-x old-extent))
       (setf (extent-max-x old-extent)
             (extent-max-x new-extent)))
   (if (< (extent-min-y new-extent)
          (extent-min-y old-extent))
       (setf (extent-min-y old-extent)
             (extent-min-y new-extent)))
   (if (> (extent-max-y new-extent)
          (extent-max-y old-extent))
       (setf (extent-max-y old-extent)
             (extent-max-y new-extent)))
   old-extent)

(defun add-position-to-extent (old-extent position)

   (il:* il:|;;| "side effects OLD-EXTENT")

   (let ((x (position-x position))
         (y (position-y position)))
        (if (< x (extent-min-x old-extent))
            (setf (extent-min-x old-extent)
                  x)
            (if (> x (extent-max-x old-extent))
                (setf (extent-max-x old-extent)
                      x)))
        (if (< y (extent-min-y old-extent))
            (setf (extent-min-y old-extent)
                  y)
            (if (> y (extent-max-y old-extent))
                (setf (extent-max-y old-extent)
                      y))))
   old-extent)

(defun extend-axis (plot axis &optional no-update-p)
   (ecase axis
       (:x (rescale-x-axis plot (plot-x-lower plot)
                  (plot-x-upper plot)
                  :interval-fn
                  'extended-interval-fn))
       (:y (rescale-y-axis plot (plot-y-lower plot)
                  (plot-y-upper plot)
                  :interval-fn
                  'extended-interval-fn)))
   (if (null no-update-p)
       (redraw-plot-window plot)))

(defun extended-interval-fn (min max tic-info)
   (let* ((new-min (ti-min tic-info))
          (new-max (ti-max tic-info))
          (episilon 0.05)
          (delta (* episilon (- new-max new-min))))
         (make-axis-interval :min (- new-min delta)
                :max
                (+ new-max delta))))

(defun plot-world-extent (plot)
   (make-extent :min-x (plot-x-lower plot)
          :max-x
          (plot-x-upper plot)
          :min-y
          (plot-y-lower plot)
          :max-y
          (plot-y-upper plot)))

(defun setf-plot-world-extent (plot new-extent &optional no-update-p)
   (let* ((x-lower (extent-min-x new-extent))
          (x-upper (extent-max-x new-extent))
          (y-lower (extent-min-y new-extent))
          (y-upper (extent-max-y new-extent)))
         (rescale-x-axis plot x-lower x-upper :interval-fn 'no-change-interval-fn)
         (rescale-y-axis plot y-lower y-upper :interval-fn 'no-change-interval-fn)
         (if (null no-update-p)
             (redraw-plot-window plot))))

(defun plot-interval (plot axis)
   (ecase axis
       (:x (copy-axis-interval (si-x-interval (plot-scale-info plot))))
       (:y (copy-axis-interval (si-y-interval (plot-scale-info plot))))))

(defun setf-plot-interval (plot axis new-interval &optional no-update-p)
   (ecase axis
       (:x (rescale-x-axis plot (interval-min new-interval)
                  (interval-max new-interval)
                  :interval-fn
                  'no-change-interval-fn))
       (:y (rescale-y-axis plot (interval-min new-interval)
                  (interval-max new-interval)
                  :interval-fn
                  'no-change-interval-fn)))
   (if (null no-update-p)
       (redraw-plot-window plot)))

(defun region-to-extent (region)
   (let ((min-x (region-left region))
         (min-y (region-bottom region)))
        (make-extent :min-x min-x :max-x (+ min-x (region-width region))
               :min-y min-y :max-y (+ min-y (region-height region)))))

(defun extent-to-region (extent)
   (let ((min-x (extent-min-x extent))
         (min-y (extent-min-y extent)))
        (make-region min-x min-y (- (extent-max-x extent)
                                    min-x)
               (- (extent-max-y extent)
                  min-y))))



(il:* il:|;;| "plot labels")


(defun plot-label (plot label-position)
   (margin-label (ecase label-position
                     (:bottom (plot-bottom-margin plot))
                     (:left (plot-left-margin plot))
                     (:top (plot-top-margin plot))
                     (:right (plot-right-margin plot)))))

(defun setf-plot-label (plot label-position new-label &optional no-update-p)
   (let ((margin (ecase label-position
                     (:bottom (plot-bottom-margin plot))
                     (:left (plot-left-margin plot))
                     (:top (plot-top-margin plot))
                     (:right (plot-right-margin plot)))))
        (setf (margin-label margin)
              (and new-label (string new-label)))
        (if (null no-update-p)
            (redraw-plot-window plot))))



(il:* il:|;;| "Plot object methods")


(defun draw-plot-object (object view-port plot)
   (let ((text-object (plot-object-prop object :label-object)))
        (funcall (pm-draw-fn (plot-object-methods object))
               object view-port plot)
        (if text-object (draw-plot-object text-object view-port plot))
        (apply-after-method (plot-object-prop object ':when-drawn)
               object view-port plot)))

(defun erase-plot-object (object view-port plot)
   (let ((text-object (plot-object-prop object :label-object)))
        (funcall (pm-erase-fn (plot-object-methods object))
               object view-port plot)
        (if text-object (erase-plot-object text-object view-port plot))
        (apply-after-method (plot-object-prop object ':when-erased)
               object view-port plot)))

(defun highlight-plot-object (object view-port plot)
   (let ((text-object (plot-object-prop object :label-object)))
        (funcall (pm-highlight-fn (plot-object-methods object))
               object view-port plot)
        (if text-object (highlight-plot-object text-object view-port plot))
        (apply-after-method (plot-object-prop object ':when-highlighted)
               object view-port plot)))

(defun lowlight-plot-object (object view-port plot)
   (let ((text-object (plot-object-prop object :label-object)))
        (funcall (pm-lowlight-fn (plot-object-methods object))
               object view-port plot)
        (if text-object (lowlight-plot-object text-object view-port plot))
        (apply-after-method (plot-object-prop object ':when-lowlighted)
               object view-port plot)))

(defun plot-object-extent (object plot)

   (il:* il:|;;| "May return NIL, a position or an extent")

   (funcall (pm-extent-fn (plot-object-methods object))
          object plot))

(defun distance-to-plot-object (object stream-position plot)
   (funcall (pm-distance-fn (plot-object-methods object))
          object stream-position plot))

(defun label-plot-object (object plot)
   (if (plot-object-label object)
       (progn (setf (plot-object-prop object :label-object)
                    (funcall (pm-label-fn (plot-object-methods object))
                           object plot))
              (apply-after-method (plot-object-prop object :when-labeled)
                     object plot))
       (print-plot-prompt "Object has no label" plot)))

(defun unlabel-plot-object (object plot)
   (let ((text-object (plot-object-prop object ':label-object)))
        (if text-object
            (let ((view-port (plot-view-port plot)))
                 (erase-plot-object text-object view-port plot)
                 (setf (plot-object-prop object ':label-object)
                       nil)
                 (apply-after-method (plot-object-prop object :when-unlabeled)
                        object plot))
            (print-plot-prompt "Not a labeled object" plot))))

(defun move-plot-object (object dx dy plot)
   (funcall (pm-move-fn (plot-object-methods object))
          object dx dy plot))

(defun copy-plot-object (plot-object)

   (il:* il:|;;| "Returns a copy of PLOT-OBJECT.  PLOT-OBJECT props are handled as follows.  If the PLOT-OBJECT has a COPY-FN on its prop list, apply it to NEW-PLOT-OBJECT, PLOT-OBJECT and PROP-NAME by PROP and expect it to copy the PROP's, else just copy the top-level list structure.")

   (let ((new-plot-object (funcall (pm-copy-fn (plot-object-methods plot-object))
                                 plot-object)))
        (setf (plot-object-prop-list new-plot-object)
              (pt:with-collection (do* ((prop-name-tail (plot-object-prop-list plot-object)
                                               (cddr prop-name-tail))
                                        (prop-name (car prop-name-tail)
                                               (car prop-name-tail))
                                        (prop-value-tail (cdr prop-name-tail)
                                               (cdr prop-name-tail))
                                        (prop-value (car prop-value-tail)
                                               (car prop-value-tail)))
                                       ((null prop-name-tail))
                                     (when prop-value
                                         (pt:collect prop-name)
                                         (pt:collect (if (eq prop-name :label-object)

                                                         (il:* il:|;;| "Cached label")

                                                         (copy-plot-object prop-value)
                                                         prop-value))))))
        (apply-after-method (plot-object-prop plot-object :when-copied)
               plot-object new-plot-object)
        new-plot-object))

(defun add-plot-object (object plot &optional no-update-p)
   (let ((plot-objects (plot-objects plot))
         (redraw-p nil))
        (push object (plot-objects plot))
        (if (adjust-scale-p (plot-object-extent object plot)
                   plot)
            (setq redraw-p t))
        (if (null no-update-p)
            (if (or redraw-p (not (open-window-p (plot-window plot))))
                (redraw-plot-window plot)
                (draw-plot-object object (plot-view-port plot)
                       plot)))
        (apply-after-method (plot-object-prop object :when-added)
               object plot no-update-p)
        object))

(defun add-plot-objects (objects plot &optional no-update-p)
   (let* ((plot-objects (plot-objects plot))
          (redraw-p nil))

         (il:* il:|;;| "Update display list")

         (setf (plot-objects plot)
               (append objects plot-objects))
         (if (adjust-scale-p (plot-objects-extent objects plot)
                    plot)
             (setq redraw-p t))
         (if (null no-update-p)
             (if (or redraw-p (not (open-window-p (plot-window plot))))
                 (redraw-plot-window plot)
                 (let ((view-port (plot-view-port plot)))
                      (dolist (object objects)
                          (draw-plot-object object view-port plot)))))
         (dolist (object objects)
             (apply-after-method (plot-object-prop object :when-added)
                    object plot no-update-p))
         objects))

(defun delete-plot-object (object plot &optional no-update-p no-save-p)

   (il:* il:|;;| "Delete object from display list of plot.  If (NULL NO-UPDATE-P) then update the associated display (open it if necessary). If SAVE-P then intern the object on the plot's save list.")

   (let ((plot-objects (plot-objects plot)))
        (let ((plot-window (plot-window plot))
              (view-port (plot-view-port plot))
              (selected-object (plot-selected-object plot)))
             (when (eq object selected-object)
                 (if (and (null no-update-p)
                          (open-window-p plot-window))
                     (lowlight-plot-object object view-port plot))
                 (setf (plot-selected-object plot)
                       nil))
             (setf (plot-objects plot)
                   (delete object plot-objects))
             (if (null no-save-p)
                 (push object (plot-save-list plot)))
             (if (null no-update-p)
                 (if (not (open-window-p plot-window))
                     (open-plot-window plot)
                     (erase-plot-object object view-port plot)))
             (apply-after-method (plot-object-prop object :when-deleted)
                    object plot no-update-p no-save-p)
             object)))

(defun delete-plot-objects (objects plot &optional no-update-p no-save-p)

   (il:* il:|;;| "Delete object from display list of plot.  If (NULL NO-UPDATE-P) then update the associated display (open it if necessary). If SAVE-P then intern the object on the plot's save list.")

   (let* ((plot-objects (plot-objects plot))
          (trimmed-objects (mapcan #'(lambda (object)
                                            (if (pt:memq object plot-objects)
                                                (list object)))
                                  (delete-duplicates objects))))
         (when trimmed-objects
             (let ((plot-window (plot-window plot))
                   (view-port (plot-view-port plot))
                   (selected-object (plot-selected-object plot)))
                  (when (pt:memq selected-object trimmed-objects)
                      (if (and (null no-update-p)
                               (open-window-p plot-window))
                          (lowlight-plot-object selected-object view-port plot))
                      (setf (plot-selected-object plot)
                            nil))
                  (setf (plot-objects plot)
                        (nset-difference plot-objects trimmed-objects))
                  (if (null no-save-p)
                      (setf (plot-save-list plot)
                            (append trimmed-objects (plot-save-list plot))))
                  (if (null no-update-p)
                      (if (not (open-window-p plot-window))
                          (open-plot-window plot)
                          (dolist (object trimmed-objects)
                              (erase-plot-object object view-port plot))))
                  (dolist (object trimmed-objects)
                      (apply-after-method (plot-object-prop object :when-deleted)
                             object plot no-update-p no-save-p))
                  objects))))

(defun relabel-selected-object (selected-object plot)
   (let ((plot-prompt-window (plot-prompt-window plot))
         label)

        (il:* il:|;;| "If the object is labeled, delete the label.")

        (if (plot-object-prop selected-object :label-object)
            (unlabel-plot-object selected-object plot))
        (terpri plot-prompt-window)
        (let ((new-label (il:promptforword "Type new label :" (plot-object-label selected-object)
                                "Enter NIL for no label" plot-prompt-window nil nil
                                (il:constant (list (char-code #\Newline)
                                                   (char-code #\Linefeed)
                                                   (char-code #\Escape)
                                                   (char-code #\Tab))))))
             (when (setf (plot-object-label selected-object)
                         new-label)
                   (label-plot-object selected-object plot)))))

(defun delete-selected-object (object plot &optional no-update-p no-save-p)
   (let ((plot-objects (plot-objects plot))
         (plot-window (plot-window plot)))
        (when (pt:memq object plot-objects)
            (when (eq object (plot-selected-object plot))
                (if (null no-update-p)
                    (if (open-window-p plot-window)
                        (lowlight-plot-object object plot)))
                (setf (plot-selected-object plot)
                      nil))
            (setf (plot-objects plot)
                  (delete object plot-objects))
            (if (null no-save-p)
                (push object (plot-save-list plot)))
            (if (null no-update-p)
                (if (not (open-window-p plot-window))
                    (open-plot-window plot)
                    (erase-plot-object object (plot-view-port plot)
                           plot)))
            (apply-after-method (plot-object-prop object :when-deleted)
                   object plot no-update-p no-save-p)
            object)))

(defun undelete-last-object (plot)
   (let ((object (pop (plot-save-list plot))))
        (if object
            (add-plot-object object plot)
            (print-plot-prompt "No object to undelete" plot))))

(defun translate-plot-object (object dx dy plot &optional no-update-p)
   (let ((text-object (plot-object-prop object ':label-object))
         (view-port (plot-view-port plot)))
        (if (null no-update-p)
            (let ((selected-object (plot-selected-object plot)))
                 (when (eq object selected-object)
                     (lowlight-plot-object selected-object view-port plot)
                     (setf (plot-selected-object plot)
                           nil))
                 (erase-plot-object object view-port plot)))

        (il:* il:|;;| "Destructively modify the data structure for OBJECT")

        (move-plot-object object dx dy plot)
        (if (null no-update-p)
            (draw-plot-object object view-port plot))
        (if text-object (translate-plot-object text-object dx dy plot no-update-p))
        (apply-after-method (plot-object-prop object ':when-translated)
               object dx dy plot no-update-p)))

(defun select-plot-object (plot-object plot)
   (let ((selected-object (plot-selected-object plot))
         (view-port (plot-view-port plot)))
        (when selected-object
            (lowlight-plot-object selected-object view-port)
            (setf (plot-selected-object plot)
                  nil))
        (when plot-object
            (highlight-plot-object plot-object view-port plot)
            (setf (plot-selected-object plot)
                  plot-object))))

(defun deselect-plot-object (plot-object plot)
   (let ((selected-object (plot-selected-object plot)))
        (when (and selected-object (eq selected-object plot-object))
            (lowlight-plot-object selected-object (plot-view-port plot))
            (setf (plot-selected-object plot)
                  nil))))



(il:* il:|;;| "Plot menus ")


(defun default-when-selected-fn (item menu)
   (let* ((plot (or (menu-prop menu :plot)
                    (window-prop (main-window (window-from-menu menu))
                           :plot)))
          (mode (menu-prop menu :mode))
          (selected-object (plot-selected-object plot))
          (selected-fn (second item))
          extra-args args-to-pass)
         (when (consp selected-fn)
             (setq extra-args (cdr selected-fn))
             (setq selected-fn (car selected-fn)))
         (setq args-to-pass (mapcar #'eval extra-args))
         (if (eq mode :middle)
             (progn (lowlight-plot-object selected-object (plot-view-port plot)
                           plot)
                    (setf (plot-selected-object plot)
                          nil)
                    (apply selected-fn selected-object plot args-to-pass))
             (apply selected-fn plot args-to-pass))))

(defun plot-menu (plot menu-name)
   (case menu-name
       (:middle (plot-middle-menu plot))
       (:right (plot-right-menu plot))
       (otherwise (getf (plot-other-menus plot)
                        menu-name))))

(defun setf-plot-menu (plot menu-position new-menu &optional no-update-p)
   (if (not (or (null new-menu)
                (menu-p new-menu)))
       (error "Not a menu: ~s" new-menu)
       (if (and new-menu (null (menu-when-selected-fn new-menu)))
           (setf (menu-when-selected-fn new-menu)
                 'default-when-selected-fn)))
   (case menu-position
       (:middle (setf (plot-middle-menu plot)
                      new-menu))
       (:right (setf (plot-right-menu plot)
                     new-menu))
       (otherwise (setf (getf (plot-other-menus plot)
                              menu-position)
                        new-menu)))
   (when (and (eq menu-position :right)
              (plot-prop plot :fixed-right-menu-p)
              (null no-update-p)
              (open-window-p (plot-window plot)))

       (il:* il:|;;| "Update the fixed menu")

       (unfix-right-menu plot)
       (fix-right-menu plot))
   new-menu)

(defun plot-menu-items (plot menu-position)
   (case menu-position
       (:middle (menu-items (plot-middle-menu plot)))
       (:right (menu-items (plot-right-menu plot)))
       (otherwise (let ((menu (getf (plot-other-menus plot)
                                    menu-position)))
                       (and menu (menu-items menu))))))

(defun setf-plot-menu-items (plot menu-position new-items &optional no-update-p)

   (il:* il:|;;| "Replace the cached menu with the new list of items")

   (let ((menu (case menu-position
                   (:middle (plot-middle-menu plot))
                   (:right (plot-right-menu plot))
                   (otherwise (getf (plot-other-menus plot)
                                    menu-position)))))
        (setq menu (and new-items (if menu
                                      (copy-menu menu :new-items new-items)
                                      (make-menu :items new-items))))
        (setf-plot-menu plot menu-position menu no-update-p)
        new-items))

(defun add-plot-menu-items (plot menu-name items-to-add &optional no-update-p)

   (il:* il:|;;| "Add ITEMSTOADD to end of menu MENUNAME item list")

   (let* ((menu (case menu-name
                    (:middle (plot-middle-menu plot))
                    (:right (plot-right-menu plot))
                    (otherwise (getf (plot-other-menus plot)
                                     menu-name))))
          (menu-items (plot-menu-items plot menu-name)))
         (when items-to-add
             (setq items-to-add (mapcan #'(lambda (item)
                                                 (if (not (member item menu-items :test 'equal))
                                                     (list item)))
                                       items-to-add))
             (setf-plot-menu-items plot menu-name (append menu-items items-to-add)
                    no-update-p))
         items-to-add))

(defun delete-plot-menu-items (plot menu-name items-to-delete &optional no-update-p)

   (il:* il:|;;| "Delete ITEMSTODELETE from menu MENUNAME item list.  RETURNS new item list if something deleted or else NIL.  ITEMSTODELETE may be a list of lists or of atoms, in which case the atoms are compared to secessive CARS of MENUNAME's item list")

   (let ((menu (case menu-name
                   (:middle (plot-middle-menu plot))
                   (:right (plot-right-menu plot))
                   (otherwise (getf (plot-other-menus plot)
                                    menu-name))))
         (menu-items (plot-menu-items plot menu-name)))
        (setq items-to-delete
              (mapcan #'(lambda (item)
                               (let ((target (car (if (consp item)
                                                      (member item menu-items :test 'equal)
                                                      (member item menu-items :test 'equal :key
                                                             'car)))))
                                    (if target (list target))))
                     (if (listp items-to-delete)
                         items-to-delete
                         (list items-to-delete))))
        (setf-plot-menu-items plot menu-name (set-difference menu-items items-to-delete)
               no-update-p)
        items-to-delete))

(defvar *default-middle-menu*
   (make-menu :items '(("Label" toggle-label "Toggle label on/off" (:subitems ("Relabel" 
                                                                              relabel-selected-object
                                                                                     "Change label"))
                              )
                       ("Delete" delete-selected-object "Delete object"))
          :when-selected-fn
          'default-when-selected-fn :track-cursor-p t))

(defvar *default-right-menu*
   (make-menu :items '(("Layout" make-plot-sketch "Create a sketch of the PLOT")
                       ("Redraw" redraw-plot-window "Redraw plot")
                       ("Rescale" rescale-plot "Rescale plot axes"
                              (:subitems ("X Axis" (rescale-plot :x)
                                                "Rescale X axis"
                                                (:subitems ("Automatic" (rescale-plot :x)
                                                                  "Rescale automatically")
                                                       ("Manual" (manually-rescale-plot :x)
                                                              "Rescale manually")))
                                     ("Y Axis" (rescale-plot :y)
                                            "Rescale Y axis"
                                            (:subitems ("Automatic" (rescale-plot :y)
                                                              "Rescale automatically")
                                                   ("Manual" (manually-rescale-plot :y)
                                                          "Rescale manually")))))
                       ("Extend" extend-axis "Extend plot axes on/off" (:subitems ("X Axis"
                                                                                   (extend-axis
                                                                                    :x)
                                                                                   
                                                                               "Extend X axis on/off"
                                                                                   )
                                                                              ("Y Axis" (extend-axis
                                                                                         :y)
                                                                                     
                                                                               "Extend Y axis on/off"
                                                                                     )))
                       ("Labels" ask-for-which-label "Relabel plot" (:subitems ("Title" (
                                                                                        ask-for-label
                                                                                         :top)
                                                                                      "Title plot")
                                                                           ("Left" (ask-for-label
                                                                                    :left)
                                                                                  
                                                                                 "Label left of plot"
                                                                                  )
                                                                           ("Bottom" (ask-for-label
                                                                                      :bottom)
                                                                                  
                                                                               "Label bottom of plot"
                                                                                  )
                                                                           ("Right" (ask-for-label
                                                                                     :right)
                                                                                  
                                                                                "Label right of plot"
                                                                                  )))
                       ("Tics" toggle-tics "Tics on or off" (:subitems ("Top" (toggle-tics :top)
                                                                              "Top tics on/off")
                                                                   ("Left" (toggle-tics :left)
                                                                          "Left tics on/off")
                                                                   ("Bottom" (toggle-tics :bottom)
                                                                          "Bottom tics on/off")
                                                                   ("Right" (toggle-tics :right)
                                                                          "Right tics on/off")))
                       ("Undelete" undelete-last-object "Undelete last deleted object")
                       ("Fixed Menu" toggle-fixed-right-menu "Fix right plot menu"))
          :when-selected-fn
          'default-when-selected-fn :track-cursor-p t))

(defun manually-rescale-plot (plot &optional (axis :both))
   (let ((scale-info (plot-scale-info plot))
         (plot-objects (plot-objects plot)))
        (if (or (eq axis ':both)
                (eq axis ':x))
            (let ((new-interval (ask-for-scale plot ':x)))
                 (when (> (interval-max new-interval)
                          (interval-min new-interval))
                       (setf-plot-interval plot :x new-interval t))))
        (if (or (eq axis ':both)
                (eq axis ':y))
            (let ((new-interval (ask-for-scale plot ':y)))
                 (when (> (interval-max new-interval)
                          (interval-min new-interval))
                       (setf-plot-interval plot :y new-interval t))))
        (redraw-plot-window plot)))

(defun ask-for-scale (plot axis)
   (let ((eol-chars (il:constant (list (char-code #\Newline)
                                       (char-code #\Linefeed)
                                       (char-code #\Escape)
                                       (char-code #\Tab))))
         (plot-prompt-window (plot-prompt-window plot))
         (upper (ecase axis
                    (:x (plot-x-upper plot))
                    (:y (plot-y-upper plot))))
         (lower (case axis
                    (:x (plot-x-lower plot))
                    (:y (plot-y-lower plot)))))
        (terpri plot-prompt-window)
        (setq lower (read-from-string (il:promptforword (concatenate 'string (string axis)
                                                               " axis: From ")
                                             lower "Type a number" plot-prompt-window nil nil 
                                             eol-chars)))
        (setq upper (read-from-string (il:promptforword " to " upper "Type a number" 
                                             plot-prompt-window nil nil eol-chars)))
        (make-axis-interval :min lower :max upper)))

(defun toggle-label (selected-object plot)
   (if (plot-object-prop selected-object :label-object)
       (unlabel-plot-object selected-object plot)
       (label-plot-object selected-object plot)))

(defun ask-for-label (plot margin-name)

   (il:* il:|;;| "Prompt for new label and make the required call to LABELPLOT")

   (if (eq margin-name ':title)
       (setq margin-name ':top))
   (let ((plot-prompt (plot-prompt-window plot))
         (margin (ecase margin-name
                     (:bottom (plot-bottom-margin plot))
                     (:left (plot-left-margin plot))
                     (:top (plot-top-margin plot))
                     (:right (plot-right-margin plot))))
         newlabel)
        (terpri plot-prompt)
        (setq newlabel (il:promptforword (case margin-name
                                             (:bottom "Bottom margin label?")
                                             (:left "Left margin label?")
                                             (:top "Title?")
                                             (:right "Right margin label?"))
                              (margin-label margin)
                              "Type a label" plot-prompt nil nil (il:constant (list (char-code 
                                                                                           #\Newline)
                                                                                    (char-code 
                                                                                           #\Linefeed
                                                                                           )
                                                                                    (char-code 
                                                                                           #\Escape)
                                                                                    (char-code #\Tab)
                                                                                    ))))
        (if (or (null newlabel)
                (not (string= newlabel (margin-label margin))))
            (setf-plot-label plot margin-name newlabel))))

(defun ask-for-which-label (plot)

   (il:* il:|;;| "Prompt for new label and make the required call to ASKFORLABEL")

   (let ((lmenu (il:constant (make-menu :items '(:top :left :bottom :right))))
         margin)
        (print-plot-prompt "Select a margin" plot)
        (setq margin (pop-up-menu lmenu))
        (and margin (ask-for-label plot margin))))

(defun toggle-tics (plot &optional margin-name no-update-p)
   (if (null margin-name)
       (progn (toggle-tics plot :bottom t)
              (toggle-tics plot :left))
       (setf-tics-p plot margin-name (not (tics-p plot margin-name))
              no-update-p)))

(defun toggle-fixed-right-menu (plot)
   (if (plot-prop plot :fixed-right-menu-p)
       (unfix-right-menu plot)
       (fix-right-menu plot)))

(defun default-menu (menu-position)
   (case menu-position
       (:middle (and *default-middle-menu* (copy-menu *default-middle-menu*)))
       (:right (and *default-middle-menu* (copy-menu *default-right-menu*)))
       (otherwise (if (pt:memq menu-position *plot-object-types*)
                      (let ((menu (gethash menu-position *plot-object-menus*)))
                           (and menu (copy-menu menu)))))))

(defun setf-default-menu (menu-position new-menu)
   (let ((new-value (and new-menu (copy-menu new-menu))))
        (case menu-position
            (:middle (setq *default-middle-menu* new-value))
            (:right (setq *default-right-menu* new-value))
            (otherwise (if (pt:memq menu-position *plot-object-types*)
                           (setf (gethash menu-position *plot-object-menus*)
                                 new-value))))))



(il:* il:|;;| "Do we want this setf?")


(defsetf default-menu setf-default-menu)

(defun fix-right-menu (plot)
   (if (null (plot-prop plot :fixed-right-menu-p))
       (let ((plot-window (plot-window plot)))
            (setf (plot-prop plot :fixed-right-menu-p)
                  (and (open-window-p (plot-window plot))
                       (attach-menu (plot-right-menu plot)
                              plot-window :right :top))))))

(defun unfix-right-menu (plot)
   (let ((fixed-menu (plot-prop plot :fixed-right-menu-p)))
        (when fixed-menu
            (close-window fixed-menu)
            (detach-window fixed-menu)
            (setf (plot-prop plot :fixed-right-menu-p)
                  nil))))

(defun remove-fixed-right-menu (plot)
   (let ((fixed-menu (plot-prop plot :fixed-right-menu-p)))
        (when fixed-menu
            (close-window fixed-menu)
            (detach-window fixed-menu)
            (setf (plot-prop plot :fixed-right-menu-p)
                  nil))))



(il:* il:|;;| "Misc functions")


(defun which-plot (&optional window-or-x y)     (il:* il:\; "Edited  6-May-87 09:27 by jop")

   (il:* il:|;;| "like WHICHW but returns corresponding plot.  First arg may be a window")

   (let* ((w (or (window-p window-or-x)
                 (il:whichw window-or-x y)))
          (plot (or (window-prop w :plot)
                    (window-prop (window-prop w 'il:iconfor)
                           :plot))))
         (if (typep plot 'plot)
             plot)))

(xcl:define-file-environment "PLOT-FNS" :package "PLOT"
   :compiler :compile-file
   :readtable "XCL")
(il:putprops il:plot-fns il:copyright ("Xerox Corporation" 1988))
(il:declare\: il:dontcopy
  (il:filemap (nil)))
il:stop