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!

	    (define-my-application-to-command-translator page-end-position-to
	       (middle-box com-page-end-position-to my-application-command-table
		    :gesture :right
		    :documentation "Move display bottom to this position")
	       (list y))

    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 -*-

(in-package "CLIM")

;;--- 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))
  :drawing-options nil
      (check-type labels (or null sequence))
      (when labels
	(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)))
	  (tick-offset 0))
      (dotimes (n nticks)
	(draw-line-internal stream x-offset y-offset
			    (+ x1 tick-offset) (- y1 3)
			    (+ x1 tick-offset) (+ y1 3)
			    ink *time-scale-tick-style*)
	(when labels
	  (let ((label (elt labels n)))
	    (draw-string-internal stream x-offset y-offset
				  label (+ x1 tick-offset) (+ y1 4)
				  0 (length label) :center :top
				  *time-scale-label-style* ink)))
	(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 | Thread Index