Update of /project/mcclim/cvsroot/mcclim
In directory common-lisp.net:/tmp/cvs-serv10559
Modified Files:
incremental-redisplay.lisp
Log Message:
Incremental redisplay changes, part i: checking for overlap.
Date: Sun May 8 20:09:11 2005
Author: gbaumann
Index: mcclim/incremental-redisplay.lisp
diff -u mcclim/incremental-redisplay.lisp:1.46 mcclim/incremental-redisplay.lisp:1.47
--- mcclim/incremental-redisplay.lisp:1.46 Tue Mar 8 11:46:16 2005
+++ mcclim/incremental-redisplay.lisp Sun May 8 20:09:11 2005
@@ -280,36 +280,33 @@
(defgeneric incremental-redisplay
(stream position erases moves draws erase-overlapping move-overlapping))
-(defmethod incremental-redisplay
- ((stream updating-output-stream-mixin) position
- erases moves draws erase-overlapping move-overlapping)
+(defmethod incremental-redisplay ((stream updating-output-stream-mixin) position
+ erases moves draws erase-overlapping move-overlapping)
(declare (ignore position))
(let ((history (stream-output-history stream)))
(with-output-recording-options (stream :record nil :draw t)
(loop
- for (nil br) in erases
- do (erase-rectangle stream br))
+ for (nil br) in erases
+ do (erase-rectangle stream br))
(loop
- for (nil old-bounding) in moves
- do (erase-rectangle stream old-bounding))
+ for (nil old-bounding) in moves
+ do (erase-rectangle stream old-bounding))
(loop
- for (nil br) in erase-overlapping
- do (erase-rectangle stream br))
+ for (nil br) in erase-overlapping
+ do (erase-rectangle stream br))
(loop
- for (nil old-bounding) in move-overlapping
- do (erase-rectangle stream old-bounding)))
+ for (nil old-bounding) in move-overlapping
+ do (erase-rectangle stream old-bounding)))
(loop
- for (r) in moves
- do (replay r stream))
+ for (r) in moves
+ do (replay r stream))
(loop
- for (r) in draws
- do (replay r stream))
- (loop
- for (r) in erase-overlapping
- do (replay history stream r))
- (loop
- for (r) in move-overlapping
- do (replay history stream r) )))
+ for (r) in draws
+ do (replay r stream))
+ (let ((res +nowhere+))
+ (loop for (r) in erase-overlapping do (setf res (region-union res r)))
+ (loop for (r) in move-overlapping do (setf res (region-union res r)))
+ (replay history stream res)) ))
(defclass updating-stream-state (complete-medium-state)
((cursor-x :accessor cursor-x :initarg :cursor-x :initform 0)
@@ -713,141 +710,113 @@
;;; work in progress
(defvar *existing-output-records* nil)
-;;; Helper functions for managing a hash table of records
+;;;
-(defun get-record-hash (record hash)
- (let ((bucket (gethash (slot-value record 'coordinates) hash)))
- (if (null bucket)
- (values nil nil)
- (let ((rec (find record bucket :test #'output-record-equal)))
- (if rec
- (values rec t)
- (values nil nil))))))
-
-(defun add-record-hash (record hash)
- (push record (gethash (slot-value record 'coordinates) hash nil)))
-
-(defun delete-record-hash (record hash)
- (let ((bucket (gethash (slot-value record 'coordinates) hash)))
- (if bucket
- (multiple-value-bind (new-bucket deleted)
- (delete-1 record bucket :test #'output-record-equal)
- (if deleted
- (progn
- (setf (gethash (slot-value record 'coordinates) hash)
- new-bucket)
- t)
- nil))
- nil)))
+(defmethod output-record-hash (record)
+ (slot-value record 'coordinates))
(defmethod compute-difference-set ((record standard-updating-output-record)
&optional (check-overlapping t)
- offset-x offset-y
- old-offset-x old-offset-y)
+ offset-x offset-y
+ old-offset-x old-offset-y)
(declare (ignore offset-x offset-y old-offset-x old-offset-y))
- (when (eq (output-record-dirty record) :clean)
- (return-from compute-difference-set (values nil nil nil nil nil)))
- (let* ((draws nil)
- (moves (explicit-moves record))
- (erases nil)
- (erase-overlapping nil)
- (move-overlapping nil)
- (stream (updating-output-stream record))
- (visible-region (pane-viewport-region stream))
- (old-children (if (slot-boundp record 'old-children)
- (old-children record)
- nil))
- (old-bounds (old-bounds record)))
- (unless (or (null visible-region)
- (region-intersects-region-p visible-region record)
- (and old-children
- (region-intersects-region-p visible-region old-bounds)))
- (return-from compute-difference-set (values nil nil nil nil nil)))
- ;; XXX This means that compute-difference-set can't be called repeatedly on
- ;; the same tree; ugh. On the other hand, if we don't clear explicit-moves,
- ;; they can hang around in the tree for later passes and cause trouble.
- (setf (explicit-moves record) nil)
- (let ((existing-output-records (make-hash-table :test 'equalp)))
- ;; Find output records in the new tree that match a record in the old
- ;; tree i.e., already have a valid display on the screen.
- (map-over-child-display
- (if old-children
- #'(lambda (r)
- (add-record-hash r existing-output-records))
- #'(lambda (r) (push (list r r) draws)))
- (sub-record record)
- visible-region)
- (when old-children
- (map-over-child-display
- #'(lambda (r)
- (unless (delete-record-hash r existing-output-records)
- (push (list r (copy-bounding-rectange r)) erases)))
- old-children
- visible-region)
- ;; Any records left in the hash table do not have a counterpart
- ;; visible on the screen and need to be drawn.
- (loop
- for bucket being the hash-values of existing-output-records
- do (loop
- for r in bucket
- do (push (list r r) draws)))))
- (when check-overlapping
- (setf erase-overlapping (nconc erases draws))
- (setf move-overlapping moves)
- (setf erases nil)
- (setf moves nil)
- (setf draws nil))
- ;; Visit this record's updating-output children and merge in the
- ;; difference set. We need to visit all updating-output records, not just
- ;; ones in the visible region, because they might have old records that
- ;; lie in the visible region and that need to be erased.
- (map-over-child-updating-output
- #'(lambda (r)
- (multiple-value-bind (e m d e-o m-o)
- (compute-difference-set r check-overlapping)
- (setf erases (nconc e erases))
- (setf moves (nconc m moves))
- (setf draws (nconc d draws))
- (setf erase-overlapping (nconc e-o erase-overlapping))
- (setf move-overlapping (nconc m-o move-overlapping))))
- (sub-record record)
- nil)
- ;; Look for updating-output children that were not visited. Their
- ;; display records need to be erased.
- (when old-children
- (flet ((erase-obsolete (dr) ;All of them
- (let ((erase-chunk (list dr (copy-bounding-rectange dr))))
- (if check-overlapping
- (push erase-chunk erase-overlapping)
- (push erase-chunk erases)))))
- (declare (dynamic-extent #'erase-obsolete))
- (map-over-child-updating-output
- #'(lambda (r)
- (when (eq (output-record-dirty r) :updating)
- (map-over-obsolete-display #'erase-obsolete
- (sub-record r)
- visible-region)))
- old-children
- visible-region)))
- ;; Traverse all the display records for this updating output node and do
- ;; the notes...
- (flet ((note-got (r)
- (note-output-record-got-sheet r stream))
- (note-lost (r)
- (note-output-record-lost-sheet r stream)))
- (declare (dynamic-extent #'note-got #'note-lost))
- (map-over-child-display #'note-got (sub-record record) nil)
- (when old-children
- (map-over-child-display #'note-lost old-children nil)
- (map-over-child-updating-output
- #'(lambda (r)
- (when (eq (output-record-dirty r) :updating)
- (map-over-obsolete-display #'note-lost
- (sub-record r)
- nil)))
- old-children
- nil)))
- (values erases moves draws erase-overlapping move-overlapping)))
+ ;; (declare (values erases moves draws erase-overlapping move-overlapping))
+ (let (was
+ is
+ (everywhere (or +everywhere+
+ (pane-viewport-region (updating-output-stream record)))))
+ ;; Collect what was there
+ (labels ((gather-was (record)
+ (cond ((displayed-output-record-p record)
+ (push record was))
+ ((updating-output-record-p record)
+ (cond ((eq :clean (output-record-dirty record))
+ (push record was))
+ ((eq :moved (output-record-dirty record))
+ (push (slot-value record 'old-bounds) was))
+ (t
+ (map-over-output-records-overlapping-region #'gather-was
+ (old-children record)
+ everywhere))))
+ (t
+ (map-over-output-records-overlapping-region #'gather-was record everywhere)) )))
+ (gather-was record))
+ ;; Collect what still is there
+ (labels ((gather-is (record)
+ (cond ((displayed-output-record-p record)
+ (push record is))
+ ((updating-output-record-p record)
+ (cond ((eq :clean (output-record-dirty record))
+ (push record is))
+ ((eq :moved (output-record-dirty record))
+ (push record is))
+ (t
+ (map-over-output-records-overlapping-region #'gather-is
+ (sub-record record)
+ everywhere))))
+ (t
+ (map-over-output-records-overlapping-region #'gather-is record everywhere) ))))
+ (gather-is record))
+ ;;
+ (let ((was-table (make-hash-table :test #'equalp))
+ (is-table (make-hash-table :test #'equalp))
+ gone
+ stay
+ come)
+ (loop for w in was do (push w (gethash (output-record-hash w) was-table)))
+ (loop for i in is do (push i (gethash (output-record-hash i) is-table)))
+ ;; gone = was \ is
+ (loop for w in was do
+ (cond ((updating-output-record-p w)
+ (unless (eq :clean (output-record-dirty w))
+ (push (old-children w) gone)))
+ (t
+ (let ((q (gethash (output-record-hash w) is-table)))
+ (unless (some #'(lambda (x) (output-record-equal w x)) q)
+ (push w gone))))))
+ ;; come = is \ was
+ ;; stay = is ^ was
+ (loop for i in is do
+ (cond ((updating-output-record-p i)
+ (if (eq :clean (output-record-dirty i))
+ (push i stay)
+ (push i come)))
+ (t
+ (let ((q (gethash (output-record-hash i) was-table)))
+ (if (some #'(lambda (x) (output-record-equal i x)) q)
+ (push i stay)
+ (push i come))))))
+ ;; Now we essentially want 'gone', 'stay', 'come'
+ (let ((gone-overlap nil)
+ (come-overlap nil))
+ (when check-overlapping
+ (setf (values gone gone-overlap)
+ (loop for k in gone
+ if (some (lambda (x) (region-intersects-region-p k x))
+ stay)
+ collect k into gone-overlap*
+ else collect k into gone*
+ finally (return (values gone* gone-overlap*))))
+ (setf (values come come-overlap)
+ (loop for k in come
+ if (some (lambda (x) (region-intersects-region-p k x))
+ stay)
+ collect k into come-overlap*
+ else collect k into come*
+ finally (return (values come* come-overlap*)))))
+ ;; Hmm, we somehow miss come-overlap ...
+ (values
+ ;; erases
+ (loop for k in gone collect (list k k))
+ ;; moves
+ nil
+ ;; draws
+ (loop for k in come collect (list k k))
+ ;; erase overlapping
+ (append (loop for k in gone-overlap collect (list k k))
+ (loop for k in come-overlap collect (list k k)))
+ ;; move overlapping
+ nil)))))
(defparameter *enable-updating-output* t
"Switch to turn on incremental redisplay")