Update of /project/mcclim/cvsroot/mcclim In directory clnet:/tmp/cvs-serv22720
Modified Files: recording.lisp Log Message: Optimize a few cases in recompute-extent-for-changed-child, generalizing an optimization by Robert Strandh.
--- /project/mcclim/cvsroot/mcclim/recording.lisp 2007/02/05 03:06:14 1.130 +++ /project/mcclim/cvsroot/mcclim/recording.lisp 2007/03/20 01:48:38 1.131 @@ -608,10 +608,10 @@ (defmethod clear-output-record ((record basic-output-record)) (error "Cannot clear ~S." record))
-(defmethod clear-output-record :before ((record compound-output-record)) +(defmethod clear-output-record :before ((record compound-output-record)) (let ((sheet (find-output-record-sheet record))) (when sheet - (map-over-output-records #'note-output-record-lost-sheet record 0 0 sheet)))) + (map-over-output-records #'note-output-record-lost-sheet record 0 0 sheet))))
(defmethod clear-output-record :after ((record compound-output-record)) ;; XXX banish x and y @@ -774,63 +774,78 @@ (setf (rectangle-edges* record) (values new-x1 new-y1 new-x2 new-y2)))))))
- (defmethod recompute-extent-for-changed-child ((record compound-output-record) changed-child old-min-x old-min-y old-max-x old-max-y) (with-bounding-rectangle* (ox1 oy1 ox2 oy2) record (with-bounding-rectangle* (cx1 cy1 cx2 cy2) changed-child - ;; If record is currently empty, use the child's bbox directly. Else.. - ;; Does the new rectangle of the child contain the original rectangle? - ;; If so, we can use min/max to grow record's current rectangle. - ;; If not, the child has shrunk, and we need to fully recompute. - (multiple-value-bind (nx1 ny1 nx2 ny2) - (cond - ;; The child has been deleted; who knows what the - ;; new bounding box might be. - ((not (output-record-parent changed-child)) - (%tree-recompute-extent* record)) - ;; Only one child of record, and we already have the bounds. - ((eql (output-record-count record) 1) - (values cx1 cy1 cx2 cy2)) - ;; If our record occupied no space (had no children, or had only - ;; children similarly occupying no space, hackishly determined by - ;; null-bounding-rectangle-p), recompute the extent now, otherwise - ;; the next COND clause would, as an optimization, attempt to extend - ;; our current bounding rectangle, which is invalid. - ((null-bounding-rectangle-p record) - (%tree-recompute-extent* record)) - ;; In the following cases, we can grow the new bounding rectangle - ;; from its previous state: - ((or - ;; If the child was originally empty, it should not have affected - ;; previous computation of our bounding rectangle. - ;; This is hackish for reasons similar to the above. - (and (zerop old-min-x) (zerop old-min-y) - (zerop old-max-x) (zerop old-max-y)) - ;; New child bounds contain old child bounds, so use min/max - ;; to extend the already-calculated rectangle. - (and (<= cx1 old-min-x) (<= cy1 old-min-y) - (>= cx2 old-max-x) (>= cy2 old-max-y))) - (values (min cx1 ox1) (min cy1 oy1) - (max cx2 ox2) (max cy2 oy2))) - ;; No shortcuts - we must compute a new bounding box from those of - ;; all our children. We want to avoid this - in worst cases, such as - ;; a toplevel output history, large graph, or table, there may exist - ;; thousands of children. Without the above optimizations, - ;; construction becomes O(N^2) due to bounding rectangle calculation. - (t (%tree-recompute-extent* record))) - ;; XXX banish x, y - (with-slots (x y) - record - (setf x nx1 y ny1) - (setf (rectangle-edges* record) (values nx1 ny1 nx2 ny2)) - (let ((parent (output-record-parent record))) - (unless (or (null parent) - (and (= nx1 ox1) (= ny1 oy1) - (= nx2 ox2) (= nx2 oy2))) - (recompute-extent-for-changed-child parent record - ox1 oy1 ox2 oy2))))))) + (let ((child-was-empty (and (= old-min-x old-min-y) ; =( + (= old-max-x old-max-y)))) + ;; If record is currently empty, use the child's bbox directly. Else.. + ;; Does the new rectangle of the child contain the original rectangle? + ;; If so, we can use min/max to grow record's current rectangle. + ;; If not, the child has shrunk, and we need to fully recompute. + (multiple-value-bind (nx1 ny1 nx2 ny2) + (cond + ;; The child has been deleted, but none of its edges contribute + ;; to the bounding rectangle of the parent, so the bounding + ;; rectangle cannot be changed by its deletion. + ;; This is also true if the child was empty. + ((or child-was-empty + (and (output-record-parent changed-child) + (> old-min-x ox1) + (> old-min-y oy1) + (< old-max-x ox2) + (< old-max-y oy2))) + (values ox1 oy1 ox2 oy2)) + ;; The child has been deleted; who knows what the + ;; new bounding box might be. + ((not (output-record-parent changed-child)) + (%tree-recompute-extent* record)) + ;; Only one child of record, and we already have the bounds. + ((eql (output-record-count record) 1) + (values cx1 cy1 cx2 cy2)) + ;; If our record occupied no space (had no children, or had only + ;; children similarly occupying no space, hackishly determined by + ;; null-bounding-rectangle-p), recompute the extent now, otherwise + ;; the next COND clause would, as an optimization, attempt to extend + ;; our current bounding rectangle, which is invalid. + ((null-bounding-rectangle-p record) + (%tree-recompute-extent* record)) + ;; In the following cases, we can grow the new bounding rectangle + ;; from its previous state: + ((or + ;; If the child was originally empty, it should not have affected + ;; previous computation of our bounding rectangle. + child-was-empty + ;; No child edge which may have defined the bounding rectangle of + ;; the parent has shrunk inward, so min/max the new child rectangle + ;; against the existing rectangle. Other edges of the child may have + ;; moved, but this can't affect the parent bounding rectangle. + (and (or (> old-min-x ox1) (>= old-min-x cx1)) + (or (> old-min-y oy1) (>= old-min-y cy1)) + (or (< old-max-x ox2) (<= old-max-x cx2)) + (or (< old-max-y oy2) (<= old-max-y cy2)))) + ;; In these cases, we can grow the rectangle using min/max. + (values (min cx1 ox1) (min cy1 oy1) + (max cx2 ox2) (max cy2 oy2))) + ;; No shortcuts - we must compute a new bounding box from those of + ;; all our children. We want to avoid this - in worst cases, such as + ;; a toplevel output history, large graph, or table, there may exist + ;; thousands of children. Without the above optimizations, + ;; construction becomes O(N^2) due to bounding rectangle calculation. + (t (%tree-recompute-extent* record))) + ;; XXX banish x, y + (with-slots (x y) + record + (setf x nx1 y ny1) + (setf (rectangle-edges* record) (values nx1 ny1 nx2 ny2)) + (let ((parent (output-record-parent record))) + (unless (or (null parent) + (and (= nx1 ox1) (= ny1 oy1) + (= nx2 ox2) (= nx2 oy2))) + (recompute-extent-for-changed-child parent record + ox1 oy1 ox2 oy2)))))))) record)
;; There was once an :around method on recompute-extent-for-changed-child here, @@ -1975,9 +1990,9 @@ (with-slots (strings) record (if (= 1 (length strings)) (styled-string-string (first strings)) - (with-output-to-string (result) - (loop for styled-string in strings - do (write-string (styled-string-string styled-string) result)))))) + (with-output-to-string (result) + (loop for styled-string in strings + do (write-string (styled-string-string styled-string) result))))))
;;; 16.3.4. Top-Level Output Records (defclass stream-output-history-mixin ()