旋转到北方

时间:2011-12-18 00:37:35

标签: postscript

在完成一系列复杂的旋转和翻译之后,我想将当前方向返回到指向页面顶部的“North”。我怎么能这样做?

显而易见的答案是跟踪每次翻译时我指向的方向,但这似乎很多工作。我希望像“0 rotateto”这样的东西让我留在当前位置,但指向页面的绝对顶部;同样,“90 rotateto”指向右侧。

我还想知道如何在一系列旋转和翻译后移动到页面上的特定点。换句话说,我正在寻找一个移动到特定点的“绝对移动”指令,而不是相对于当前平移坐标。

我的目的是实现徽标式乌龟。乌龟识别的两个命令是setpos(移动到绝对位置)和setorientation(旋转到绝对方向)。我正试图弄清楚如何在PostScript中实现这些命令。

编辑1:

谢谢luser droog,但有些事情仍然是错误的。我的库包含下面显示的乌龟命令及其PostScript等价物; rotateto和setpos是你之前给出的定义,is_penup是一个最初设置为true的全局变量,我还没有考虑pos和orientation,它报告当前的乌龟位置和方向:

init             -- %!
                    /rotateto { ... } bind def
                    /setpos { ... } def
                    newpath
                    306 396 moveto % center 8.5x11 portrait
                    0 setgray 2 setlinewidth
penup            -- % set global variable is_penup to true
pendown          -- % set global variable is_penup to false
forward n        -- if is_penup then 0 n rmoveto currentpoint translate
                                else 0 n rlineto currentpoint translate
backward n       -- if is_penup then 0 n neg rmoveto currentpoint translate
                                else 0 n neg rlineto currentpoint translate
right n          -- n neg rotate
left n           -- n rotate
setpos x y       -- x y setpos
setorientation n -- n rotateto
done             -- stroke newpage
pos              -- get current absolute x y position
orientation      -- get current absolute orientation

绘制两个正方形的示例如下所示; square命令写入四行长度为50的行,每行后跟90度右转:

init
pendown
setpos 100 100
square 50
setpos 400 400
right 45
square 25
done

但这不起作用。两个setpos命令都没有被尊重。有两个正方形,第二个正方形倾斜45度,但都从页面的中心开始。我担心每次我说currentpoint翻译都会干扰你在你的setpos命令中所做的事情。

你能提出任何建议吗?

编辑2:

我决定不担心将当前的职位和方向归还给Scheme;这对我目前的目的来说太多了,虽然它可能在将来某个时候有用。我的Scheme代码的最终版本如下所示。 Send是一个常用的实用程序,turtle-init在PostScript中定义了turtle库,其余的Scheme turtle库如下,然后是绘制两个正方形的示例程序。一切正常。

(define (send x . xs)
  (cond ((null? xs) (display x) (newline))
  (else (display x) (display " ") (apply send xs))))

(define (turtle-init)
  (for-each send '(
    "%!"
    "/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"
    "/forward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def"
    "/backward { 0 exch is-pendown { rlineto } { rmoveto } ifelse fix } def")))
(define (turtle-penup) (send "penup"))
(define (turtle-pendown) (send "pendown"))
(define (turtle-forward n) (send n "forward"))
(define (turtle-backward n) (send n "backward"))
(define (turtle-right n) (send n "right"))
(define (turtle-left n) (send n "left"))
(define (turtle-setpos x y) (send x y "setpos"))
(define (turtle-setorientation n) (send n "rotateto"))
(define (turtle-done) (send "stroke showpage"))

(define (square n)
  (do ((i 4 (- i 1))) ((zero? i))
    (turtle-forward n) (turtle-right 90)))

(define (squares)
  (turtle-init)
  (turtle-pendown)
  (turtle-setpos 100 100)
  (square 50)
  (turtle-setpos 400 400)
  (turtle-right 45)
  (square 25)
  (turtle-done))

(with-output-to-file "squares.ps"
  (lambda () (squares)))

这一切都将在1月的某个时间出现在blog;我还不确定确切的日期。

谢谢!

编辑3:

我回去看了Logo龟的文档,发现setpos尊重笔的当前状态;因此,如果笔是向下的,则setpos会从旧位置写入一条线到新位置。我改变了setpos以获得正确的行为:

/setpos { defmat transform itransform is-pendown
    { lineto } { moveto } ifelse fix } def

我还改变了turtle-init,以便它实际上调用init而不是仅仅定义它。如果你从来没有打电话,那就没什么用了。

3 个答案:

答案 0 :(得分:3)

嗯,首先,y轴默认指向北方。因此,可以使用matrix defaultmatrix setmatrix重置任何正常的翻译和轮换。您可以重置缩放和旋转,而无需使用matrix currentmatrix dup 0 matrix defaultmatrix 0 4 getinterval putinterval setmatrix等内容修改翻译。

如果您可以忽略缩放,您可以执行rotateto(假设当前位置是用户空间的来源),如下所示:

/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

要设置绝对位置,您需要进行一些转换。

通常,指定的任何坐标都指向用户空间。这意味着它们在生效之前乘以当前转换矩阵。设置“绝对”位置意味着将坐标解释为指向“绝对空间”。唯一这样的特权空间是默认矩阵。

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

编辑:这很棘手!我能够让你的测试使用这套程序。

%!
/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

让我感到震惊的一件事就是在每次搬家之后记住currentpoint translate(包括最初的搬家)。

对于orientation,您必须“解释”矩阵。 在提出建议之前,我需要对此进行一些思考。首先,请记住旋转矩阵看起来像[cosA sinA -sinA cosA 0 0]。

答案 1 :(得分:3)

几个月后......

我们都是白痴!大多数肯定 一个“绝对”坐标系:设备空间!当然,运算符movetocurrentpoint在CTM相对坐标中与程序接口,您可以手动“撤消”转换为“占据绝对位置”并稍后“重做”以转换为CTM - 相对的坐标。

此示例使用OO-Turtle和宏扩展套件来执行带嵌入式转换的Lindenmayer系统(Queen Anne's Lace)。

Queen Anne's Lace

魔术线是:

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

您可以将它们保留在堆栈中,将它们保存在变量中。只要你没有将矩阵折叠到点不再可用的位置(缩放因子为0),当itransform相对于任何矩阵恰好是当前时,它仍将引用相同的位置。

我在评论中留下了旧的,乏味的方式,所以它的完全恐怖可能会成为后代的警告。

%!
%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

答案 2 :(得分:2)

窃取肯的想法,并进一步了解你想要做的事情(来自你的blog),这是一个伪OO后记龟。

%!
/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