Rotate to North

你离开我真会死。 提交于 2019-12-06 02:26:54

Well, to start with, the y-axis points north by default. So any normal translations and rotations can be reset with matrix defaultmatrix setmatrix. You can reset the scaling and rotation without modifying the translation with something like matrix currentmatrix dup 0 matrix defaultmatrix 0 4 getinterval putinterval setmatrix.

If you can ignore scaling, you could do rotateto (assuming the current position is the origin of user space) like this:

/rotateto { % angle  rotateto  -
    matrix rotate % create a rotation matrix
    dup 4
    matrix currentmatrix 4 2 getinterval %get the current translation
    putinterval setmatrix % put translation into rot. matrix and install
} bind def

To set an absolute position, you'll need to do a few transformations.

Normally, any coordinates specified refer to user space. That means they are multiplied by the Current Transformation Matrix before taking effect. To set an "absolute" position means interpreting coordinates as refering to an "absolute space". The only such priviledged space is the Default Matrix.

/setpos { % x-abs y-abs  setpos  -
    matrix defaultmatrix transform % x' y'  %to device space
    itransform % x y  %back to current user space
    translate
} def

Edit: This is tricky stuff! I was able to get your test to work with this suite of procedures.

%!
/defmat matrix defaultmatrix def
/fix { currentpoint translate } def
/rotateto { matrix rotate
    dup 4 matrix currentmatrix 4 2 getinterval
    putinterval setmatrix } def
/setpos { defmat transform itransform moveto fix } def
/left { rotate } def
/right { neg rotate } def
/is-pendown false def
/penup { /is-pendown false def } def
/pendown { /is-pendown true def } def
/done { stroke showpage } def
/init { initgraphics 306 396 moveto fix 0 setgray 2 setlinewidth } def
/pos { matrix currentmatrix 4 2 getinterval {} forall } def
/forward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def
/backward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def

/square { 4 { dup forward 90 right } repeat pop } def
init
pendown
100 100 setpos
50 square
400 400 setpos
45 right
25 square
done

One thing that tripped me up was remembering to currentpoint translate after every move (including the initial move).

For orientation, you'll have to "interpret" the matrix. I'll need to do some pondering on this before making suggestions. For a head-start, remember that a rotation matrix looks like [ cosA sinA -sinA cosA 0 0 ].

Months Later...

We're all idiots! There most certainly is an "absolute" coordinate system: Device Space! Of course the operators moveto and currentpoint interface with your program in CTM-relative coordinates, you can manually "undo" the transformation to "take an absolute position" and "redo" it later to convert to CTM-relative coords.

This example uses the OO-Turtle, and a macro-expansion suite to execute a Lindenmayer System with embedded transformations (Queen Anne's Lace).

And the magic lines are:

     % ...
     currentpoint transform   % save "absolute" position
     % ...
     itransform translate
     0 0 moveto     % return to saved position
     % ...

You can leave them on the stack, save them in a variable. As long as you haven't collapsed the matrix to where the point is no longer referable (a scaling factor of 0), it will still refer to the same position when itransformed relative to whatever matrix happens to be current.

I've left the old, tedious way present in the comments so the full horror of it may serve as warning to posterity.

%!
%linden.ps

%one Iteration of macro-expansion
% traverses an array, replacing tokens
% defined in the dictionary P,
% and constructs a new array
/I { % O  I  O'
    mark exch {
        P exch 2 copy known {
            get aload pop
        }{
            exch pop
        } ifelse
    } forall counttomark array astore exch pop cvx
} def
%Generate nth expansion of O
/G { % n  G  O'^n
    /O load exch
    { I } repeat
} def

% n x y  x  -
% moveto x y, generate nth expansion of O, stroke, grestore
/x { moveto gsave G exec stroke grestore } def


/Turtle <<
    /forward {
        dup Turtle /angle get cos mul
        exch Turtle /angle get sin mul
        2 copy
        Turtle /pen? get { lineto }{ moveto }ifelse
        translate
    }
    /angle 90
    /pen? false
    /right { Turtle /angle 2 copy get 4 3 roll sub put }
    /left { Turtle /angle 2 copy get 4 3 roll add put }
    >> def
/send { get exec } def
/ini { 300 400 2 copy moveto translate } def

<< %shortcuts
    /F { R Turtle /forward send }
    /+ { T Turtle /right send }
    /- { T Turtle /left send }
>> begin

ini

{ %branching stems
/V << % description of the Lindenmeyer System
    /#0 {F leaf}
    /#1 {F}

    /O { #0 } % Original
    /P << % Productions
        /#0 { #1 [ - #0 ] + #0 }
        /#1 { #1 #1 }
        >>
    /R 10
    /T 45
    /leaf { 0 0 R 2 mul 0 180 arc 0 0 moveto }
>> def
V begin
0 -300 translate
.5 .5 scale
Turtle /pen? true put

gsave
    %"Special" Definitions
    % The brackets in the procedure resulting
    % from the expansion of /O are not used to
    % construct arrays, but to save and reset
    % the position and angle of the Turtle.
    ([) {
        %matrix currentmatrix 4 2 getinterval aload pop
        currentpoint transform
        Turtle /angle get
    } def
    (]) {
        Turtle /angle 3 2 roll put
        %matrix currentmatrix % Tx Ty M
        %dup 4 5 4 roll put % Ty M
        %dup 5 4 3 roll put % M
        %setmatrix
        itransform translate
        0 0 moveto
    } def
    .5 .5 scale
    8 0 0 x % execute 8-level-deep expansion of /O in /V
grestore

end showpage
} exec

PostScript's 'moveto' is an absolute moveto, not relative. In PostScript if you want a relative moveto you use rmoveto. That said, there is basicallly no diredct access to device space. PostScript has two spaces a theoretically infinitely fine user space, and device space, which is what actually gets printed and has the resolution of the output device. The Current Transformation Matr4ix (CTM) maps user space to device space.

You can't access device space directly, all PostScript operations take place through the CTM. Thus there is no way to address an absolute point in device space, and so there is no 'absolute moveto' in the sense you are asking for, all points in user space are transformed through the CTM to get to device space.

There is no 'direction' in the sense that the Logo turtle has one either, from any point you can move directly to any other point, no need to rotate at all.

If you do rotate the CTM then yes, it really is up to you to keep track of that. You can reset the CTM in a number of ways. You can gsave/grestore round the series of operations. Note that this will reset everything, including the current point and path. If you want to preserve the current point then 'gsave.....currentpoint grestore moveto' would do that.

As luser droog notes you can use defaultmatrix setmatrix, or calculate your own matrix and use setmatrix, eg 'currentmatrix invertmatrix concat setmatrix'.

But in general you only rotate the CTM for special effects such as shearing or drawing images and so on, and the normal PostScript technique is to gsave/grestore round such sequences.

Instead of keeping track of the CTM, you could instead keep track of the turtle's current direction and apply geometry to calculate the new position after the turtle moves a given distance in that direction, which might simplify your problem.

Stealing Ken's idea, and with further insight into what you're trying to do (from your blog), Here's a pseudo-OO postscript turtle.

%!
/Turtle <<
    /forward {
        dup Turtle /angle get cos mul
        exch Turtle /angle get sin mul
        2 copy
        Turtle /pen? get { lineto }{ moveto }ifelse
        translate
    }
    /angle 90
    /pen? false
    /right { Turtle /angle 2 copy get 4 3 roll sub put }
    /left { Turtle /angle 2 copy get 4 3 roll add put }
    >> def
/send { get exec } def
/ini { 300 400 2 copy moveto translate } def

<< %shortcuts
    /F { 100 Turtle /forward send }
    /+ { 90 Turtle /right send }
    /- { 90 Turtle /left send }
>> begin

ini
Turtle /pen? true put
F + F + F + F
F + F + F + F
F + F + F + F
F + F + F + F
stroke %draws a four-square
showpage
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!