在完成一系列复杂的旋转和翻译之后,我想将当前方向返回到指向页面顶部的“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而不是仅仅定义它。如果你从来没有打电话,那就没什么用了。
答案 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)
几个月后......
我们都是白痴!大多数肯定 是 一个“绝对”坐标系:设备空间!当然,运算符moveto
和currentpoint
在CTM相对坐标中与程序接口,您可以手动“撤消”转换为“占据绝对位置”并稍后“重做”以转换为CTM - 相对的坐标。
此示例使用OO-Turtle和宏扩展套件来执行带嵌入式转换的Lindenmayer系统(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