[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Which way is up?

There actually is a good reason for having the global window coordinate
system be increasing-Y-down.  Even if you don't allow for windows with
output histories, it is still the case that windows that output text
represent only a portion of what is in effect a larger plane of output.
Even as an old style window **MORE**'s at the bottom and wraps around,
the new output is in fact a continuation of the old.  It is a cause of
major inconvenience in such windows that the coordinate system resets
itself as the result of this essentially unpredictable event.  Once you
extend things to introduce the notion of scrolling, and especially of
asynchronous scrolling using the mouse, it becomes even more important
that the window coordinate system not change when the portion of the
infinite plane that happens to be displayed through it changes.  If you
accept that argument, then you will see why it would be counter
intuitive to have increasing-Y-up, since the coordinates would get more
and more negative after the first screenful.  For this reason, the text
cursor position is increasing-Y-down, where down extends potentially
infinitely below the bottom of the visible part of the screen.

Now, the discussion above is only concerned with the text cursor.  Your
particular need is for a primary Cartesian quadrant in which to do
graphics.  This is perfectly reasonable.  You just need to recognize
that this coordinate system is not in fact the same as the entire
window's planar coordinate system.  This is especially clear when you
consider what happens when you mix text and graphics.  The effect you
want is that the block of graphics acts like one big tall line in the
linear stack of output.  It is within this block that you want the
primary quadrant.

In the improved graphics substrate which Symbolics is developing for a
future release, all drawing is done through a general affine transform.
This provides the basis for just the facility you are looking for.  We
have included a special form which gives the program a local coordinate
system within its dynamic extent whose top is at the current cursor
position (within the infinite window history) and whose bottom (Y=0)
becomes the new cursor position when you are done.  This gives you the
best of both worlds -- within the little block, you can do graphics
without standing on your head to figure out which way to draw.  And
successive blocks that you output end up stacked in the proper
historical order and intermingled with any surrounding command echo or
other text.

Now, as to what you can do for right now, I'd suggest that you put
together a special version of dw:with-own-coordinates which in addition
to offsetting the Y position to the top of the visible viewport, negates
its direction.  Something like the following:

;;; -*- Mode: LISP; Syntax: Common-lisp; Package: DYNAMIC-WINDOWS; Base: 10; Lowercase: T -*-

(defflavor inverted-coordinates-stream () (new-coordinates-stream))

(defmethod (:convert-to-absolute-coordinates inverted-coordinates-stream) (x y ignore)
  (values (+ x x-offset)
	  (- (+ y-offset height) y)))

(defmethod (:convert-to-relative-coordinates inverted-coordinates-stream) (x y ignore)
  (values (- x x-offset)
	  (- (+ y-offset height) y)))

(compile-flavor-methods inverted-coordinates-stream)

(defmacro in-inverted-window ((stream x y width height) &body body)
  (format-output-macro-default-stream stream)
  `(in-inverted-window-internal (lambda (,stream) . ,body) ,stream ,x ,y ,width ,height))

(defun in-inverted-window-internal (continuation stream x y width height)
  (let ((xstream (make-instance 'inverted-coordinates-stream
				:stream stream
				:x-offset x :y-offset y
				:width width :height height)))
    (send xstream :home-cursor)
    (funcall continuation xstream)))

(defun inverted-test ()
  (send *standard-output* :clear-window)
  (multiple-value-bind (left top right bottom)
      (send *standard-output* :visible-cursorpos-limits)
    (in-inverted-window (*standard-output* left top (- right left) (- bottom top))
      (send *standard-output* :draw-triangle 100 100 300 100 200 150))))