Some sparse questions

Daniel Kochmański daniel at
Sat Jan 18 22:51:01 UTC 2020

Hello Andrea,

thank you for working to improve McCLIM!

Andrea De Michele writes:

> Dear all,
> I'm working on CLIM-PDF backend and I solved some problems like the
> text direction in landscape orientation, but I don't like the solution
> so I didn't submit any PR.

CLIM-PDF backend is a prototype at best. It still has numerous rough
edges. Another person who works on improving it is a github user @admich
(he sometimes drops on IRC channel too, I don't his real name).
> Instead I write here to have some feedback from you about some issues that I
> have found, some very PDF specific other in the very core of McCLIM.
> I'm start from the more pdf specific (and easier) to the more general one (and harder):
> The PDF backend is used through WITH-OUTPUT-TO-PDF-STREAM that is
> based on the analogous macro for Postscript backend defined in the
> Spec.
> I think it is better to define for PDF backend a
> WITH-OUTPUT-TO-PDF-FILE with a string or a pathname as argument
> instead of a stream. In this way the user don't have to open the file
> stream (the user needs also to known that the stream must be open with
> keyword :element-type '(unsigned-byte 8)). If we remove the
> WITH-OUTPUT-TO-PDF-STREAM McCLIM can remove the dependency to
> FLEXI-STREM system.

Macro lambda list is ((stream-var file-stream &rest options) &body body)

Word "stream" in WITH-OUTPUT-TO-PDF-STREAM refers to STREAM-VAR, because
that's on what you call the drawing operations, i.e

  (with-output-to-pdf-stream (stream file-stream)
    (draw-rectangle* stream 0 0 10 10))

Same thing goes for PostScript backend. I don't see a problem if you
write a PR which makes invoke-with-output-to-pdf-stream accept
file-stream as a string or pathname and then open the stream
automatically. That will be somewhat DWIM-y approach which doesn't
change the interface. In that case I'd like to see a similar change
proposed for function implementing:

- with-output-to-ps-stream
- with-output-to-raster-image-stream

> 2) GRAFT in PDF and Postscript Backend
> PDF and Postscript Backend define their GRAFT but then never use
> it. The stream sheet where the output is drawn is not a child of a
> graft. I think this is not correct.

Graft purpose is twofold: accessing a device properties and posing as
the display server "root window". The latter doesn't make sense for
output-only backends because there is no root window and windowing
substrate doesn't apply to them.

Having graft implementation for PDF and PS allows i.e to say, at which
millimeter is a middle of the sheet of paper. Whether it is implemented
correctly is another story. I think that (graf pdf-stream) should return
the graft.

> 3) GRAFT in general
> Now each backend define its GRAFT. I think that the backend could only
> initialize a standard-graft with the right information (in MAKE-GRAFT
> generic) like: mirror, width, height, device-millimiter-density,
> backend-transformation, and leave all the other stuff in the common
> graft module "Core/clim-basic/windowing/grafts.lisp"

Currently our grafts are stubs which allow converting physical sizes to
pixels and vice versa, but in principle they have a more profound
meaning for output operations with different coordinate systems. I've
started exploring this topic with a console backend (interactive), which
is different enough to expose many interesting implications.

My current understanding is as follows (I'll use a console example):

- device coordinates are specified by a column and a row
- sheet user coordinates are specified by x and y in pixels

Now let's draw something on a sheet:

  (draw-rectangle* sheet 0 0 100 100)

MEDIUM-TRANSFORMATION is used to transform user coordinates to device
coordinates. To be able to construct such transformation you need to
know how many horizontal pixels matches one column, how many vertical
pixels matches one row, and if there is some translation between both
(i.e pixels start at 0,0 while console starts at 1,1). One way to do
that is:

(defun make-px-to-ch-transformation (graft)
  (let* ((sx (/ (clim:graft-width  graft :units :glyph)
                (clim:graft-width  graft :units :pixel)))
         (sy (/ (clim:graft-height graft :units :glyph)
                (clim:graft-height graft :units :pixel)))
         (dx 1)
         (dy 1))
     (clim:make-scaling-transformation sx sy)
     dx dy)))

which would initialize the sheet's medium when created. Moreover, we
could imagine, that sheet user coordinates have no graft representation
(i.e they are a density independent pixels), in that case
sheet-native-transformation is initialized to transform dp to px, and
then medium-transformation is composition of the
sheet-native-transformation and a result of the above function.

Of course none of this is currently implemented, but I hope it gives you
an idea why graft's are a) useful, b) in case of non-pixel-based devices
they are essential.
> 4) transformation and region machinery
> Is it the transformation and region machinery correct? Or it is works
> only in some standard situaions? For example I think that we could
> obtain a Zoom effect simply by: (setf (sheet-transformation
> some-sheet) (make-scaling-transformation 1.5 1.5)) but this doesn't
> work on McCLIM (I try it in the listener demo of clim-tos and there it
> works). Follow some more specific topics:

I did not investigate how the zooming effect could be conformingly
achieved, but I think that modifying the SHEET-TRANSFORMATION is not the
way to do that. If I were speculating I'd be more inclined to tinker
with the medium transformation. No guarantees it will work (even if it
is allowed by the spec which I'm not sure it is).

> 4a) Setting sheet-native-transformation:
> %%SET-SHEET-NATIVE-TRANSFORMATION. I think this is not correct the
> sheet-native-transformation must be computated and only cached never
> set directly, otherwise you can lost the parent trasformation.

It is an internal interface which makes possible to update a native
transformation when we update the mirror geometry. You are right that it
should never be used to change the native transformation, it is only
used to update it when it changes (when we know what we are
doing). %% prefix means "dangerous".
> 4b) sheet-native-transformation for basic-sheet:
> (defmethod sheet-native-transformation ((sheet basic-sheet))
>   (with-slots (native-transformation) sheet
>     (unless native-transformation
>       (setf native-transformation
> 	    (if-let ((parent (sheet-parent sheet)))
> 	      (compose-transformations
>                (sheet-native-transformation parent)
>                (sheet-transformation sheet))
>               +identity-transformation+)))
>     native-transformation))
> it's not correct because didn't take in account the mirror
> transformation if the mirror of sheet it is not the same of the
> parent.  So we need to correct this or to add a method for
> mirrored-sheet-mixin.  Of course we didn't see the error because often
> McCLIM set directly sheet-native-transform as written before in 4a)

Mirrors have little to do with native transformations. See below.

As far as sheet is concerned there are three coordinate systems which do
not always match (i.e because of mirroring):

- local  - coordinates of a current drawing context
- user   - coordinates of a sheet
- native - coordinates of a graft (screen)
- device - coordinates of a medium

As we know in the sheet hierarchy we may have multiple mirrors. Let's
consider a very simple hierarchy where each next sheet is a child of the
previous one:

graft - (mirror sheet1) - sheet2 - (mirror sheet3) - sheet4

Now some shortened definitions:

SHEET-TRANSFORMATION "Returns a transformation that converts coordinates
in the sheet sheet's coordinate system into coordinates in its parent's
coordinate system."

SHEET-NATIVE-TRANSFORMATION "Returns the transformation for the sheet
sheet that converts sheet coordinates into native coordinates."

SHEET-DELTA-TRANSFORMATION "Returns a transformation that is the
composition of all of the sheet transformations between the sheets sheet
and ancestor."

SHEET-DEVICE-TRANSFORMATION "Returns the transformation used by the
graphics output routines when drawing on the mirror."


For illustration purpose let's assume that sheet4's transformation is a
translation [20,20] (layout panes do that to position their children)
and that sheet3's transformation is translation [100,100]. Other sheet

Point [0,0] in sheet4 "user" coordinates is:
- [20,20] in sheet3 "user" coordinates
- [120,120] in sheet2 "user" coordinates
- [120,120] in sheet1 "user" coordinates
- [120,120] in graft "user" coordinates

Since graft represents a screen, its "user" coordinats are native
coordianates. We completely ignore mirrors when we compute
SHEET-NATIVE-TRANSFORMATION, because it is a jump from sheet4 to the
graft. That means, that the method mentioned by you earlier is correct.

Delta transformation is a bit more interesting, because it allows to
pick another ancestor (than a graft) to whose "user" coordinates we want
to transform "our user" coordinates.

Device transformation is the most confusing though, because mirror may
have its local (in display server terms) coordinates which do not
coincide with native coordinates.

Point [0,0] in sheet4 "user" coordinates is:
- [20,20] in sheet3 "device" coordinates

Point [0,0] in sheet3 "user" coordinates is:
- [0,0] in sheet3 "device" coordinates

> 4c) What is sheet-native-region?
> From the spec: "Returns the region for the sheet sheet in native
> coordinates". This is not clear. The first time that I read it I
> understood that it is the sheet-region of the sheet transformed in
> native coordinates, that means for examples that a sheet with a region
> +everywhere+ have a native-region +everywhere+. Instead for McCLIM,
> and clim-tos too, it is the region of the mirror "for" the sheet
> i.e. the sheet-region of the sheet transformed in native coordinates
> clipped by the mirror region.  This interpretation maybe is correct
> because the spec said about sheet-device-region:
> "Returns the actual clipping region to be used when drawing on the
> mirror. This is the intersection of the user's clipping region
> (transformed by the device transformation) with the sheet's native
> region."
> Anyway, in this case, the sheet-native-region for the graft in Mcclim
> is wrong: it is +everywhere+

SHEET-NATIVE-REGION is the sheet region in screen coordinates. Graft
*is* the screen, so we simply use sheet-region. Child native region is
always clipped by its parent native region (parent is a "window" into a
deeper plane), so we use region intersections between parent and child
native regions.

Things once again get more interesting for device-region. As descibed in
the spec, it is the sheet-native-region clipped by the mirror
region. Notice, that device-region may be partially obscured by its
parent (however in practice it works poorly unless the parent is also a
mirror, you may experiment with hierarchy tool demo in clim-examples
with random mirroring arrangement - resize parent's for different
combinations parent-child mirror-notmirror).


I hope that it clarifies at least a little what is going on. I might
have made a mistake somewhere because it was a long day and I'm looking
into my notes from around 6 months ago :) and had to reread multiple
parts of the spec.

Best regards,

Daniel Kochmański ;; aka jackdaniel | Przemyśl, Poland
TurtleWare - Daniel Kochmański      |

"Be the change that you wish to see in the world." - Mahatma Gandhi

More information about the mcclim-devel mailing list