sicp-lang/sicp

squash-inwards example of the book

jrgtt opened this issue · 11 comments

jrgtt commented

From the "A Picture Language" section, the implementation of squash-inwards is said to shape like diamonds the given painters. Like the image of Rogers:

diamond-1

(define (squash-inwards painter)
  (transform-painter painter
                     (make-vect 0.0 0.0)
                     (make-vect 0.65 0.35)
                     (make-vect 0.35 0.65)))

(paint (squash-inwards einstein))

Although with the following snippet applied to Einstein image I get the following result:

image

The other implementations of the book like flip-vert and rotate90 are working fine, but looking at the implementation of transform-painter I can't understand how it works after subtracting the mapped vector edges by the new origin. I found this question on stack overflow which sums up precisely where I lack understanding.

jrgtt commented

Hello @soegaard, I can't see the pictures :)

jrgtt commented

Thanks that works indeed. If I may ask, I cloned the repository and tried to figure out by myself how it works, but I can't make head or tails from how transform-painter actually apply the correct transformation to the frames.

For instance, I see that by calling rotate90 on the painter it produces a new relative frame with origin = (1, 0); corner1 = (0, 1); corner2 = (-1, 0);. How does that actually produces a painter rotated 90 degrees?

jrgtt commented

Hi Jens, I can't find an attached picture, perhaps it was removed by the email client? 😛

jrgtt commented

Thanks a lot for doing it, it makes sense now! I see you found my thread in reddit as well :)

This is a piece to the puzzle.

The standard coordinate system for drawing contexts in Racket has a
coordinate system with (0,0) in the upper, left corner and (w,h) in
the lower, right corner; where w and h are the width and height of the
drawing area.

Since the coordinate system in SICP has the y-axes the tradional way,
and uses the unit square to cover the entire bitmap, the procedure
make-painter-bitmap installs a transformation from the sicp coordinates
to the standard ones that both scales and flips the y-axis.
(send dc set-initial-matrix (vector w 0. 0. (* -1. h) 0. h))

Now if I disable this and instead use
(send dc set-initial-matrix (vector w 0. 0. h 0. 0))
which merely scales the coordinates, then I get a diamond shape.
Of course, the drawings are now upside down.

(define (make-painter-bitmap width height)
  (define bm (make-bitmap width height))
  (define dc (new bitmap-dc% [bitmap bm]))
  (send dc set-pen black-pen)
  (send dc set-brush black-brush)
  ; (send dc set-smoothing 'smoothed)  
  (define w (* 1. width))
  (define h (* 1. height))
  ; Map unit square to screen coordinates - also flip y-axis
  ; Initial Matrix (Logical to Device coordinates)
  ;                                   xx xy yx  yy       x0 y0
  ; (send dc set-initial-matrix (vector w  0. 0. (* -1. h) 0. h))
  (send dc set-initial-matrix (vector w  0. 0. h 0. 0))
  (values bm dc))

image

I looked into the code, here's what I found.

The transformation matrix is probably wrong. I compared this matrix with the matrix computed by my JavaScript implementation (surprisingly it works as intended). The difference, as you said in a comment, is a wrong sign. The second and the third number's sign should be flipped.

There's probably something wrong in compose-transformation. Instead of computing the transformation by compose-transformation

(define new-transformation
  (compose-transformation old-transformation transformation))
(send dc set-initial-matrix (transformation->vector new-transformation))

I tried applying transformation directly onto the old one

(define-syntax (with-transformation stx)
  ; ...
  (send dc transform (transformation->vector transformation)))

I also think frame->transformation should be (trans e1x e1y e2x e2y ox oy) instead of (trans e1x e2x e1y e2y ox oy).

Combining these two changes, it should paint the correctly squashed image.

Hope it's helpful. :)

Hi @zhaozhemin

Thanks for looking at this. Your comments in the transformation made look at the documentation of get-initial-matrix again. It seems the order of xy and yx are swapped in the documentation, so all that needed to be done was to swap those components in transformation->vector and vector->transformation.

Thanks also to @JRigotti for reporting the bug.