本书介绍了如何在PostScript中绘制图形, 当然也必然涉及一些几何.
GS>newpath
GS>144 144 moveto
GS>288 288 lineto
GS>stroke
GS>
newpath
. 这就像拿起笔准备画画.moveto
. 这就像将笔置于路径的起点.lineto
为路径添加线条. 这就像在纸上移动笔.stroke
绘制它, 或者说使其变得可见.以下命令序列可以绘制一个边长为英寸的正方形.
newpath
144 144 moveto
288 144 lineto
288 288 lineto
144 288 lineto
144 144 lineto
stroke
当然PoscScript中存在许多不同的方式来绘制这个正方形.
newpath
144 144 moveto
144 0 rlineto
0 144 rlineto
-144 0 rlineto
closepath
stroke
rlineto
是lineto
的相对 (relative) 位置版本. closepath
通过回到上一个应用moveto
的位置以封闭路径.
newpath
144 144 moveto
144 0 rlineto
0 144 rlineto
-144 0 rlineto
closepath
fill
fill
和stroke
有着不同的效果.
0.5 setgray
newpath
144 144 moveto
144 0 rlineto
0 144 rlineto
-144 0 rlineto
closepath
fill
1 0 0 setrgbcolor
newpath
144 144 moveto
144 0 rlineto
0 144 rlineto
-144 0 rlineto
closepath
fill
对于生活在北美的人而言, 既然默认的页面尺寸为, 与单位英寸打交道通常来得更加容易. 以下命令可以将基本的长度单位转换为英寸.
72 72 scale
当进行缩放的时候, 需要注意默认的线宽是一个单位. 如果什么都不做, 那么现在线宽就是英寸了.
0.01389 setlinewidth
你可以使用0.01389 setlinewidth
将其改回来, 因为约等于.
1 2 translate
你可以使用translate
进行平移, 比如这里将坐标原点右移了个单位, 上移了个单位.
72 72 scale
4.25 5.5 translate
如此原点就移动了到了页面的中心.
还有一种简单的坐标变换, 即旋转rotate
.
144 144 translate
30 rotate
newpath
0 0 moveto
144 0 lineto
144 144 lineto
0 144 lineto
0 0 lineto
stroke
旋转总是围绕当前原点进行, 注意到PostScript的角度单位是角度制而不是弧度制.
PostScript是一种完备的编程语言.
GS>3 4 add
GS<1>
你可能想问<1>
是什么意思, 它指的是栈的深度.
GS>3 4 add
GS<1>=
7
GS>
=
移除栈顶的元素, 并将其输出. ==
与=
类似, 但是输出在某种意义上更加美观, 所以你总是应该使用==
.
GS>3 4 add
GS<1>stack
7
GS<1>
stack
输出整个栈, 类似的命令还有pstack
. pstack
之于stack
就像==
之于=
.
GS>3 3 mul
GS<1>4 4 mul
GS<2>add
GS<1>sqrt
GS<1>=
5.0
GS>
linecap | 样式 |
---|---|
0 | |
1 | |
2 |
默认情况下linecap
为0
, 你可以通过setlinecap
来设置这个参数.
9 setlinewidth
% linejoin = 0 by default
newpath
0 0 moveto
0 72 lineto
72 0 lineto
stroke
linejoin
是一个与linecap
有点联系的参数, 它控制line join的样式, 其值同样仅可能为0
, 1
, 2
, 并使用setlinejoin
修改.
newpath
144 144 moveto
144 0 rlineto
0 144 rlineto
-144 0 rlineto
closepath
gsave
1 0 0 setrgbcolor
fill
grestore
0 setgray
stroke
newpath
moveto
lineto
rmoveto
rlineto
closepath
stroke
fill
translate
scale
rotate
setlinewidth
setrgbcolor
setgray
setlinejoin
setlinecap
gsave
grestore
showpage
如果是一个点而是一个向量, 那么是一个点, 其是满足的点. 我们称是平移得到的. 若给定实数以及点和, 我们可以构造一个点. 若, 那么该点在线段上. 当时, 该点即的中点. 我们可以将写成的形式.
/s 1 def
newpath
0 0 moveto
s 0 rlineto
0 s rlineto
s neg 0 rlineto
closepath
stroke
PostScript在内部(至少是隐式地)处理三种坐标系统. 第一种是物理坐标系. 物理坐标系的基本单位是像素. 第二种是页面坐标系. 其原点位于页面的左下角, 单位长度为一个Adobe点, 等于英寸. 页面可以被视为一种理想化了的物理设备. 第三种是用户坐标系, 这是用户绘制图像时使用的坐标系. 最初页面坐标系和用户坐标系是相同的, 但是特定的操作, 例如scale
, translate
, rotate
, 可以改变其间的关系.
仿射变换可以看成是一个线性变换加上一个平移分量, 其具有特定的性质: 直线经过变换仍然是直线, 平行线经常变换仍然是平行的.
确定一个仿射坐标变换所需要的数据被存储在一个长度为六的数组[a b c d e f]
里, 其被称为matrix. 命令matrix
将数组[1 0 0 1 0 0]
置于栈上, 此即恒等变换. 若此时再使用currentmatrix
命令, 则会将当前变换置于该数组之中.
GS>matrix currentmatrix ==
[1.33333337 0.0 0.0 1.33333337 0.0 0.0]
GS>
[注记: 至少若是在Windows系统之中调整缩放比例, 结果是会改变的, 当然这也是相当合理的.] defaultmatrix
和currentmatrix
有些类似, 但是顾名思义, 其将从页面到物理坐标系的变换置于栈顶的数组之中. 如果什么都没动, 这两个命令的效果显然是一样的. [译注: currentmatrix
当然就是从用户到物理坐标系的变换了.]现在我们想要解方程以用和表达和. 我们考虑将其写成的形式. 假定可逆, 我们得到也就是说, 这个逆变换同样是仿射变换. 实际上, PostScript拥有一个计算逆变换的命令, 即invertmatrix
. 另外, concatmatrix
可以用来计算变换的复合. 它们的用法如下.
M matrix invertmatrix
这可以将M
的逆变换置于栈上.A B matrix concatmatrix
这可以将A
和B
的复合置于栈上. [译注: 先施行变换A
, 后施行变换B
.]显然, 我们可以藉由这些操作来计算从用户到页面坐标系的变换.
/user-to-page-matrix {
matrix currentmatrix
matrix defaultmatrix
matrix invertmatrix
matrix concatmatrix
} def
当前变换是, 默认变换是, 逆就是, 当前变换和{默认变换的逆}复合一下就得到了.注记: 本节考虑的不是三维的仿射变换的情形, 而是仿射坐标系的诸多事宜.
实际上, 我们可以在三维的情况下用线性变换表示二维的仿射变换.可以被重写为这样之后, 仿射变换的复合就变成纯粹的矩阵乘法了.
现在我们可以检视如何以矩阵表示先前的几种简单变换.
a b scale
x rotate
a b translate
当然了, 最一般的变换如下.[a b c d e f] concat
以上这些变换的效果都是给当前矩阵左乘其变换矩阵.72 72 scale
4 5 translate
30 rotate
之后, 从用户到页面坐标系的变换矩阵应该是什么?在Racket写了点简单的程序算一算.
> (matrix-print
(matrix* (make-scale 72 72)
(make-translation 4 5)
(make-rotation (/ pi 6))))
62.353829072479584 -35.99999999999999 288
35.99999999999999 62.353829072479584 360
0 0 1
当然, 我的这个采用的是通行的左乘而不是PostScript的右乘, 所以结果应该是其转置.GS>/user-to-page-matrix {
matrix currentmatrix
matrix defaultmatrix
matrix invertmatrix
matrix concatmatrix
} def
GS>72 72 scale
GS>4 5 translate
GS>30 rotate
GS>user-to-page-matrix
GS<1>==
[62.3538284 36.0 -36.0 62.3538284 288.0 360.0]
GS>
我们再在PostScript的REPL里算一遍, 的确如此.