在LispWorks中进行编程23:龟图形
Category:UI界面编写龟图形
该项目演示了如何使用名为Turtle Graphics的系统在Lisp中创建简单图形,该系统最初由Seymour Papert作为数学教学工具开发。
Turtle Graphics基于控制一个叫做乌龟的小动物,它在计算机的屏幕上移动。它可以响应一些简单的命令,比如向前 移动乌龟向前移动一定数量的单位, 向右移动乌龟顺时针旋转指定的度数。向后和向左的命令导致相反的运动。
乌龟在移动时会在屏幕上留下痕迹。你可以用下面的命令控制该抬笔和落笔 ; 当笔下来时,乌龟画线。在原始系统中,乌龟在屏幕上显示为三角形光标; 在这个项目中,乌龟是看不见的。
举个例子,这是一系列绘制房屋的命令,以及最终的结果。乌龟从左下角开始:
(forward 100)(left 90)(forward 100)(left 30)(forward 100)(left 120)(forward 100)(left 30)(forward 100)
我们将实现龟图形作为Lisp程序,因此我们可以将它们包含在Lisp程序中以允许我们制作复杂的图。
完整列表
Lisp中的Turtle图形
第一步是创建一些全局变量来跟踪我们正在绘制的窗口,称为乌龟窗格,以及乌龟的位置和角度(以弧度表示):
(defparameter turtle-pane nil)(defparameter turtle-x 320)(defparameter turtle-y 240)(defparameter turtle-theta 0)(defparameter turtle-draw t)
窗口将是640 x 480像素,所以我们已经在窗口中间启动了乌龟。我们将提供一个重置命令来重置乌龟的位置和方向:
(defun reset () (setf turtle-x 320) (setf turtle-y 240) (setf turtle-theta 0) (setf turtle-draw t))
LispWorks图形包提供了一个命令gp:draw-line来绘制一条线; 格式是:
(gp:draw-line pane from-x from-y to-x to-y)
这是前进的程序:
(defun forward (length) (let ((new-x (+ turtle-x (* length (cos turtle-theta)))) (new-y (- turtle-y (* length (sin turtle-theta))))) (if turtle-draw (gp:draw-line turtle-pane turtle-x turtle-y new-x new-y)) (setf turtle-x new-x) (setf turtle-y new-y)))
我们首先计算乌龟的新位置,new-x和new-y,从旧位置到新位置画一条线,最后更新turtle-x和turtle-y。Lisp函数cos和sin以弧度给出它们的参数的余弦和正弦。
这是正确的程序。我们将角度转换为弧度,因为我们保持以弧度为单位的turtle-theta:
(defun radians (angle) (* pi (/ angle 180))) (defun right (angle) (setf turtle-theta (- turtle-theta (radians angle))))
后退和左边的定义非常简单:
(defun back (length) (forward (- length))) (defun left (angle) (right (- angle)))
最后抬笔和落笔:
(defun penup () (setf turtle-draw nil)) (defun pendown () (setf turtle-draw t))
在窗口中绘制图形
最后,我们需要命令来创建一个窗口,我们将在其上绘制乌龟图形。主例程plot创建一个名为capi:output-pane的区域,并调用capi:contains以在纯窗口中显示它:
(defun plot () (capi:contain (make-instance ‘capi:output-pane :display-callback ‘draw) :best-width 640 :best-height 480))
创建窗口时,它会调用过程绘制来绘制图形。这是我们放置龟图形命令的地方。例如,这是绘制房屋的示例:
(defun draw (pane &optional x y width height) (declare (ignore x y width height)) (setf turtle-pane pane) (reset) (forward 100) (left 90) (forward 100) (left 30) (forward 100) (left 120) (forward 100) (left 30) (forward 100))
该抽奖过程会被这里不需要额外的参数; 该声明 忽略线停止这些生成错误消息时编译的过程。
最后,通过调用以下命令运行乌龟:
(plot)
现在让我们看一些更有趣的图形示例。
向内螺旋
向内螺旋是曲率增加的曲线。我们可以定义如下:
(defun inspi (side angle inc count) (forward side) (right angle) (if (> count 0) (inspi side (+ angle inc) inc (- count 1))))
这里边是螺旋形的各段的长度,角度为起始角,INC是增量的角度,和计数是我们重复命令的次数(否则它会永远持续下去)。
这是为angle = 20和inc = 2 绘制的调用:
(defun draw (pane &optional x y width height) (declare (ignore x y width height)) (setf turtle-pane pane) (reset) (inspi 50 2 20 1000))
结果:
以下是angle和inc的其他一些值:
- angle=0, inc=7
- angle=40, inc=30
这是一个有趣的数学问题,可以解决为什么例程给出它在这些情况下的形状。
龙曲线
这是第二个例子; 它是一条称为龙曲线的递归曲线。
它由两个例程定义; 一个左撇子版本ldragon和一个右撇子版本rdragon:
(defun ldragon (size level) (if (= level 0) (forward size) (progn (ldragon size (- level 1)) (left 90) (rdragon size (- level 1))))) (defun rdragon (size level) (if (= level 0) (forward size) (progn (ldragon size (- level 1)) (right 90) (rdragon size (- level 1)))))
这是绘制 11级曲线的绘图程序:
(defun draw (pane &optional x y width height) (declare (ignore x y width height)) (setf turtle-pane pane) (reset) (ldragon 4 11))
最后,结果如下:
参考
有关龟图形的详细描述,可以直接在Lisp中实现的许多其他示例,以及它们背后的数学,请参阅Harold Abelson和Andrea DiSessa,MIT出版社,1980年出版的“Turtle Geometry”一书,该书仍在印刷中。
http://mip.i3geek.com