CLIM mail archive
CLIM thanks and three more queries
Date: Tue, 15 Oct 1991 17:44 EDT
From: curt@eraserhead.Jpl.Nasa.Gov (Curt Eggemeyer)
[Added back CLIM@BBN.COM]
I wish to thank you for your suggestions and hints concerning my incremental
redisplay question. I'm presently converting from symbolics flavors and
window-flavor code over to a SPARC2 Allegro CL 4.01 & CLIM 1.0. The flavor
to CLOS conversion is done. Just three more questions on implementation
efficiency in CLIM, since your the expert.
First - user scroll bars rather than default margin scroll bars
In my old code I minimized my display updating by only fricking with the
graphical objects on the screen (ie: I didn't use dynamic windows - I fake
them out with my own scroll bars). The first question concerns not using
the scroll bars in the margins of the application pane. I have generated
my own scroll bar presentation type in its own pane for both horizontal and
vertical scrolling which works fine. Essentially I only present objects on
the screen when they are viewable. If they change I will do an erase-output-
record on them and then re-present them. Speedwise I think this will be
quick enough for me (generally if I used a dynamic window with hundreds of
objects changing on me constantly it was horrifically slow) this way only
those viewable will be tweaked. My scroll bar works through the translator
to command forms, but I also need it to work while I am already in the
middle of inputting an application command (I need the scroll bar
presentations to be active while the command loop accept is not looking for
it, just like the default margin scroll bars). How would I do that and still
keep the commands that my translator to command forms invoke for my scroll-bar
Scroll bars based on presentations should be controlled with presentation
*actions* instead of presentation translators*. Using actions will fix
most or all of the things you are talking about here.
Can there be multiple command tables in an application that
co-exist asynchronously? Should I use tracking-pointer in an
independent looping process? If tracking-pointer is used how do I
influence its context
to use a complex presentation type ie: my scroll-bar consists of
(or 'top-box 'middle-box 'bottom-box) [couldn't figure the form out due
to lack of example] while showing its pointer-documentation when the user is
over it? Here`s an example of one of my scroll-bar presentation-type
translators. Gesture :right is right mouse button click!
(middle-box com-page-end-position-to my-application-command-table
:documentation "Move display bottom to this position")
Second - direct manipulation of presentations appearance w/o incremental-
The above approach may be adequate speedwise in reducing the amount of display
change going on, but I will be generating mucho amounts of presentations and
deleting them. Is there a way without using the incremental mechanism to
take an already existing presentation and just modify its appearance directly
in its structure without generating a new one or is redisplay the only way?
There is no documented way to use incremental redisplay to do this.
Third - force output without CLIM buffering
Some of my time-scale presentations consists of upto 200 draw-line operations
and tens of draw-text labels (very tiny ones). CLIM screeches to a halt when
it output these presentations. I have notice my code has already gone
on to finish doing other things before these puppies actually appear on the
display. In my old case on the Symbolics it generally took a few seconds to
output these time scales. In CLIM it sometimes takes over a minute#^&#&^%^@!
Can I avoid using CLIM's sorting or buffering or queueing or whatever it is
doing to me? I will maintain pointers to my own records.
I think I already suggested two things: (1) Write your own output
history mechanism to maintain all of the graphics-y output records, such
as a quadtree or kD tree. CLIM's coordinate sorted set is optimized for
doing line-oriented output. (2) Write a "time scale" output record
class that takes as parameters a start point, an end point, intervals
between ticks, etc., and then does all of the output itself without
recording the intermediate lines, etc. See below for an example for
suggestion 2, but note the comments.
You can use FORCE-OUTPUT to flush the output buffers to the display.
;;; -*- Mode: LISP; Syntax: Common-lisp; Package: CLIM; Base: 10; Lowercase: Yes -*-
;;--- Note: I put this in the CLIM package for ease of implementation,
;;--- but you will want to do the appropriate exports.
;;--- Note: this example only draws *horizontal* time scales. I am too
;;--- lazy to provide the general case as an example.
;;--- Note well: this will only work in CLIM 1.0. Furthermore, these are
;;--- all *internal* functions, and the exact names and calling sequences
;;--- *will* change in CLIM 2.0. The changes will not be major, but this
;;--- will need to be updated.
;;--- My guess is that this will substantially improve the performance of
;;--- your program, since so many fewer output records are being consed.
;;--- It will save on both time and space.
;; This defines DRAW-TIME-SCALE and DRAW-TIME-SCALE*, and does some
;; necessary DEFGENERIC-type stuff.
(define-graphics-operation draw-time-scale (x1 y1 x2 y2 nticks &key labels)
:arguments ((point x1 y1 x2 y2))
(check-type labels (or null sequence))
(assert (= (length labels) nticks) ()
"The number of labels must be the same as the number of tick marks"))
(draw-time-scale-internal stream 0 0
x1 y1 x2 y2 nticks labels (medium-ink stream)))))
;; This defines the hooks into output recording.
(define-graphics-internal draw-time-scale-internal (x1 y1 x2 y2 nticks labels ink)
:points-to-convert (x1 y1 x2 y2)
(let ((tmhh 3)) ;half the height of a tick mark
(fix-rectangle (min x1 x2) (- (min y1 y2) tmhh)
(max x1 x2) (+ (max y1 y2) tmhh)))
;; No :HIGHLIGHTING-TEST, just use the bounding rectangle.
;; No :HIGHLIGHTING-FUNCTION, just use a rectangle to do it
;; Canned line styles.
(defvar *time-scale-line-style* (make-line-style :thickness 2))
(defvar *time-scale-tick-style* (make-line-style :thickness 1))
;; A canned *fully merged* text style.
(defvar *time-scale-label-style* (make-text-style :fix :roman :small))
;; This actually draws the time line, labels, etc. At this point, you
;; should use the (undocumented) DRAW-xxx-INTERNAL functions, since they
;; will give the best performance.
(defmethod draw-time-scale-internal ((stream graphics-output-recording) x-offset y-offset
x1 y1 x2 y2 nticks labels ink)
(letf-globally (((stream-record-p stream) nil))
(draw-line-internal stream x-offset y-offset
x1 y1 x2 y2 ink *time-scale-line-style*)
(let ((interval (truncate (- x2 x1) (1- nticks)))
(dotimes (n nticks)
(draw-line-internal stream x-offset y-offset
(+ x1 tick-offset) (- y1 3)
(+ x1 tick-offset) (+ y1 3)
(let ((label (elt labels n)))
(draw-string-internal stream x-offset y-offset
label (+ x1 tick-offset) (+ y1 4)
0 (length label) :center :top
(incf tick-offset interval)))))
;; Magic to get encapsulating streams to work with the new operation.
(defmethod draw-time-scale-method ((stream encapsulating-stream-mixin)
x1 y1 x2 y2 nticks labels)
(let ((*original-stream* (or *original-stream* stream)))
(draw-time-scale-method (slot-value stream 'stream) x1 y1 x2 y2 nticks labels)))
Main Index |