单页面 网站怎么做的,wordpress最新版中午,百度怎么发布广告,seo公司哪家好用2019独角兽企业重金招聘Python工程师标准 第九章 统计绘图窗体 除了前几章略述的绘图窗体原型提供的基本绘图工具之外#xff0c;Lisp-Stat里的统计绘图还需要用来管理数据和将那些数据转换成屏幕上的图形的工具集。这些工具由绘图原型graph-proto提供。更多的… 2019独角兽企业重金招聘Python工程师标准 第九章 统计绘图窗体 除了前几章略述的绘图窗体原型提供的基本绘图工具之外Lisp-Stat里的统计绘图还需要用来管理数据和将那些数据转换成屏幕上的图形的工具集。这些工具由绘图原型graph-proto提供。更多的专业绘图工具比如直方图和散点图矩阵它们都是基于继承自graph-proto的原型。本章的第一节描述了绘图原型第二节略述了更加专用的原型下一章将展示那些描述如何从这些原型来开发新的绘图工具类型的例子。 9.1 绘图原型 graph-proto原型实现了一个散点图该散点图用来表示在m维空间中的点和线的二维视图。该视图是这样构造的首先对数据进行中心化和尺度化然后使用线性变换比如旋转变换最后产生该变换的图形里的维度的两个维度的一个散点图。该原型的:resize和:redraw方法可以保证当窗体出现或改变大小时图形可以得到合适的重画。鼠标点击和移动方法支持以下语法即在第2.5节描述的在选择模式和刷模式里使用的绘图方式。该原型也提供了一个基本的菜单用来与图形交互。 为了给出该原型提供的机制的详细的说明本节使用一个图形来检测5.6.2节引入的stack loss数据。 9.1.1 构造一个新的图形 graph-proto原型继承自graph-window-proto原型graph-proto的:isnew方法需要一个参数即表示将被视图化的那个空间的维度的整型值m。stack loss数据由4个变量组成气流(Air)、温度(Temp)、浓度(Conc)和氨损耗(Loss)。视图化这些数据的图形可以这样构造 (setf w (send graph-proto :new 4))
#Object: 141e7e8, prototype GRAPH-PROTO 图形里的变量数目可以使用:num-variables消息来获取 (send w :num-variables)
4 但是一个图形创建该值就不能改变。 图形包含为每个维度描述的标签字符串这些字符串可以使用:variable-label消息来设置和获取。初始情况下这些字符串是空值 (send w :variable-label 0)可以通过使用一个字符串作为其第二个参数的方式来改变它的值 (send w :variable-label 0 Air)
Air 这个消息的方法是矢量化的。表示三个维度的标签可以通过下式指定 (send w :variable-label (1 2 3) (list Temp. Conc Loss))
(Temp. Conc Loss) 现在我们可以获得这4个变量了 (send w :variable-label (0 1 2 3))
(Air Temp. Conc Loss) :graph-proto原型的:isnew方法可以接受graph-window-proto原型的:isnew方法能够接受的所有关键字参数。此外:variable-label关键字可用来指定一个m维度的初始化变量标签字符串。还有一个关键字:scale-type将在接下来的9.1.3节里描述。 9.1.2 增加数据可坐标轴 点数据 一个图形里可以包含两类数据点数据和线数据。点由m维空间里的坐标和一些附加信息组成这些附加信息比如像用来绘制点的颜色和符号。初始情况下图形里不包含点数据 (send w :num-points)
0 点数据可以使用:add-points消息来添加该消息的方法需要一个参数一个m维的列表表示将要添加的点的坐标值。下边的表达式将stack loss数据添加到图形里 (def air (80 80 75 62 62 62 62 62 58 58 58 58 58 58 50 50 50 50 50 56 70))
AIR(def temp (27 27 25 24 22 23 24 24 23 18 18 17 18 19 18 18 19 19 29 29 29))
TEMP(def conc (89 88 90 87 87 87 93 93 87 80 89 88 82 93 89 86 72 79 80 82 91))
CONC(def loss (42 37 37 28 18 18 19 20 15 14 14 13 11 12 8 7 8 8 9 15 15))
LOSS(send w :add-points (list air temp conc loss))
NIL 数据集里有21个点 (send w :num-points)
21 :add-points方法会在屏幕上绘制了新的点除非提供了值为nil的:draw关键字。该方法也允许使用:point-label关键字的标签字符串列表。 尽管现在该图形包含这些数据在它的窗体上不会显示任何点。原因是这些数据点在初始情况下当做在每一个变量的单位间隔组成的一个数据范围。为了调整图形视图化的范围以适应这些数据你可以向图形发送:adjust-to-data消息你可以使用下边的表达式发送该消息 (send w :adjust-to-data)
NIL 或者也可以从图形的菜单里选择Rescale Plot菜单项。这个消息的方法将调整图形视图化的数据范围以精确地适应对应的数据的范围跨度。发送该消息之后图形应该显示了一个散点图该散点图表示出数据集中前两个变量。结果图形如图9.1所示。 当向一个图形里加入新点的时候每一个点都会分给一个默认的符号、颜色和标签对于第一个点 (send w :point-symbol 0)
DISK(send w :point-color 0)
NIL(send w :point-label 0)
0 图9.1 stack loss数据集中气流与温度变量图示 默认符号是一个叫disk的符号。默认颜色是nil意思是改点使用当前绘图窗体的颜色来绘制。默认标签是该点索引的字符串形式。通过向这些消息传递第二个参数来为这些属性指定新值。符号应该取自plot-symbol-symbols函数返回的列表。颜色值应该是nil或者由color-symbols返回的列表的一个值。:point-symbol, :point-color和:point-label消息的方法是矢量化的因此将所有21个点的符号设置到diamond变量里。这三个消息都不会引起图形重画为了看到屏幕上改变的影响你不得不向图形发送一个重画命令。 每个点还有一个状态值该值可以使用:point-stat消息来设置和获取。该状态值可以是invisible, normal, hilited和selected这四个符号中的一个。点数据状态用做链接机制的一部分它将在9.1.5节详细描述。 :point-coordinate消息可以用来为某一特定点获取和设置单一变量坐标的值。该消息的方法需要两个参数变量的索引和点数据的索引。因此第一次观察到的气流、温度、浓度和氨损耗的值是 (send w :point-coordinate 0 0)
80.0(send w :point-coordinate 1 0)
27.0(send w :point-coordinate 2 0)
89.0(send w :point-coordinate 3 0)
42.0 :point-coordinate消息的方法也是矢量化的。新的坐标值可以以第三个参数的形式来指定。在强调一次提供新值的时候方法不会重画图形。 练习 9.1略。 线数据 线数据表示从处在m维空间里叫做linestart的点位开始每个起点都包含额外的信息比如在绘制线段时使用的宽度和线型信息还有用来作为线段终点的另一个linestart的索引nil的下标表示该linestart仅用作在其它地方凯斯的线段的终点。环形定义是是运行的这不会带来什么问题因为绘图路径斤通过linestart集合一次。 当创建一个图形的时候它没有linestart (send w :num-lines)
0 stack loss数据事实上是随着时间收集到的。通过绘制一条从第一个观察点到第二个观察点、从第二个到第三个观察点等等的直线的方式来表示时间关系这可能是很有用的。:add-line消息可以增加这样一个线段序列它的方法需要一个参数一个针对linestart的坐标的m维列表的列表。因此下式将针对stack loss数据向我们的图形里添加连在一起的线段序列。这些线数据有助于说明在数据集的前10个观测量里气流和温度是下降的。 (send w :add-lines (list air temp conc loss))
NIL :add-lines消息的方法还允许使用:type关键字来提供线型这时直线会会自导图形上除非使用了值为nil的:draw关键字。 每一个linestart都有一个宽度、类型和颜色用来从一个linestart绘制到下一个linestart。对于第一个linestart对应第一个数据点 (send w :linestart-width 0)
1(send w :linestart-type 0)
SOLID(send w :linestart-color 0)
NIL 该方法也是矢量化的通过提供一个新值作为第二个参数它可以被用来改变linestart属性的值数值的改变不会引起任何绘图行为的发生。 每个linestart都包含序列里下一个linestart的索引在绘图中用来作为一个线段的终点:add-lines方法将这些linestart连接成序列因此 (send w :linestart-next 0)
1(send w :linestart-next 1)
2 序列里的最后一个linestart没有关于下一个linestart的索引 (send w :linestart-next 20)
NIL 可以通过在线段的开始处为linestart设置下一个线段的值为nil来移除一个线段。例如 (send w :linestart-next 7 nil)
NIL 上式移除了点号索引为7和8的点之间的线段为了使该改变可见你可以向图形发送:redraw消息。 就想点一样你也可以获取和改变linestart的坐标。第一个linestart的前两个坐标值可以这样给出对应气流和温度变量 (send w :linestart-coordinate 0 0)
80.0(send w :linestart-coordinate 1 0)
27.0 :linestart-coordinate消息的方法是矢量化的可以用来改变坐标的值。如果坐标改变了图形不会重画。 练习 9.2 略。 坐标轴和当前变量 由graph-proto原型实现的散点图使用:x-axis和:y-axis消息可以用来表示x和y坐标轴。不提供参数这些消息返回当前坐标轴的状态。例如 (send w :x-axis)
(NIL NIL 0) 列表的三个元素表示该坐标轴是否正处于显示状态是否有标签它使用的刻度的数目。发送一个值为t的参数将重画带坐标轴的图形。默认地不使用标签使用4为刻度 (send w :x-axis t)
(T NIL 4) 你可以使用可选的第二、三个参数来指定一个替代的选项。:y-axis方法是相同的。当坐标轴状态改变时这两个方法都向图形发送:resize和:redraw消息除非使用了值为nil的:draw关键字。为了使用该关键字你需要给出所有这3个可选参数。在加入x、y坐标轴之后我们的图形如图9.2所示。 图9.2 stack loss数据集中气流和温度变量的x坐标与y坐标图形连续观测量由直线相连 :adjust-to-data方法会将被一个图形视图化的数据范围设置到数据范围该方法通常不会产生一个效果很好的坐标轴标签。你也可以使用:range消息为每个变量获取和改变数据范围。对于气流和温度变量 (send w :range 0)
(50.0 80.0)(send w :range 1)
(17.0 29.0) 结果列表的元素表示该数据范围的高低边界。为了改变一个变量的数据范围你需要使用两个附加值新的高低边界。 (send w :range 1 15 30)
(15.0 30.0) 上式将温度变量的数据范围设置到区间[15, 30]之间。当数据范围改变的时候图形将重画除非使用了值为nil的:draw关键字。:range消息的方法是矢量化的。 get-nice-range函数可用来帮助找到数据范围和刻度的一个好的组合。该函数带3个参数区间的高、低边界端点和刻度值的整型数值。它返回一个3值列表表示包含原始区间的区间端点和接近指定数值得刻度的数值。新值应该产生合理的坐标轴。例如对于大约有4刻度值的温度变量的数据范围来说 (get-nice-range 17 27 4)
(16.0 28.0 7) 对于这个变量推荐的设置范围是[16, 28]表达式如下 (send w :range 1 16 28)
(16.0 28.0) 将y轴设置为使用7作为刻度值 (send w :y-axis t t 7)
(T T 7) 结果坐标轴标记为16, 18, 20, ..., 28. 到目前为止我们的图形只显示了我们的4变量数据集的前两个。:current-variables消息可用来设置获取构造该图形的两个当前变量。默认的情况是显示前两个变量 (send w :current-variables)
(0 1) 下边的表达式命令图形切换到使用其它两个变量。该方法向图像发送:redraw消息除非使用了值为nil的:draw关键字。 练习9.3 略。 清除绘图数据 图形里当前的数据可以使用:clear, :clear-points和:clear-lines消息来清除。:clear-points消息移除内部数据并将点数设为0该消息会重画图形除非使用了值为nil的:draw关键字。用来清除linestart数据的:clear-lines消息是相同的。:clear消息会将点数据和线数据一并移除。 这些消息对于动画是非常有用的即显示在图形里的快速变化的数据动画。鉴于之后第9.1.6节里描述的重画方法使用了双缓冲技术该技术会产生一个平滑的动画效果。 9.1.3 缩放与变换 graph-proto原型最主要的特征就是其允许对数据进行线性变换的能力尤其是旋转变换。变换过程可以分解为两个阶段。第一阶段由数据的中心化和缩放组成第二阶段由对已经中心化和缩放过的诗句使用一个变化矩阵组成。然后将显示一个散点图即该变换的结果的坐标中的两个数据。 缩放和中心化 缩放和中心化阶段引入了一个新的坐标系统即缩放坐标系。作为对比原来指定数据的那个坐标系叫做真实坐标系。可以使用人造的缩放体系但是有两个叫定缩放和变缩放的标准的缩放体系对大多数情况来说是足够用了。这些缩放类型可以通过使用:adjust-to-data方法和:scale-type方法来实现。在处理之前我们可以私用下边两个表达式将坐标轴从图形中移除 (send w :x-axis nil)
(NIL NIL 4)(send w :y-axis nil)
(NIL NIL 4) 对于变换后的数据来说坐标轴是没有意义的。 当构造一个新的图形时它的缩放类型是nil (send w :scale-type)
NIL 这意味着没有进行中心化和缩放每个维度内的视图范围设置成真实坐标系的对应维度里的数据范围。缩放坐标系里的数据范围可以通过使用:scale-range消息来获取 (send w :range 0)
(50.0 80.0) (send w :scaled-range 0)
(50.0 80.0) 因为在初始情况下没有进行缩放缩放过的和真实的数据范围是相等的。 为了检测这个新的缩放类型我们可以通过将我们的图形的缩放类型设置为variable符号来检测表达式如下 (send w :scale-type variable)
VARIABLE :scale-type消息的方法将发送:adjust-to-data消息并重画图形除非使用了值为nil的:draw关键字。对于一个使用变化的缩放的图形:adjust-to-data方法为每个变量在数据范围的中心位置进行中心化在将每个中心化后的变量缩放到[-1, 1]区间上最后将缩放后的数据范围设置到[-sqrt(m), sqrt(m)]区间。这将确保数据进行旋转之后都在缩放范围之内。在将缩放类型设置成variable之后图形的第一个维度的数据范围和缩放后的数据范围如下 (send w :range 0)
(34.99999999999999 95.0)(send w :scaled-range 0)
(-2.0 2.0) 当变量在可比较的尺度内无法量度的时候可变缩放是合适的。如果变量在可比较的尺度上是可以量度的通过在每个维度上使用相同的比例系数我们可能想要在数据内保持一定的角度这在固定缩放策略里是一体的。如果尺度类型设置为符号fixed那么:adjust-to-data方法将在数据区间的中间进行数据的中心化并为每个维度选择缩放系数为1然后为每个变量设置其缩放后的区间该数值为数据中心化的数据最大范围的sqrt(m)倍。 如果可变缩放和固定缩放体系都不能胜任你的需求你可以通过定义一个新的:adjudt-to-data方法来定义你自己的缩放体系。该方法可以使用:scale-range方法来设置一个新的缩放范围再使用:center和:scale消息来设置或者获取中心和缩放系数。对于我们的图形因为是可变缩放第一个变量的缩放系数和中心是 (send w :scale 0)
15.0(send w :center 0)
65.0 当使用:scaled-range消息设置缩放范围时原始坐标系内的数据范围可会调整以适应该变化。:scale, :center和:scaled-range等消息是矢量化的。当使用该方法设置新值时将会重画图形除非使用了值为nil的:draw关键字。 对于定义自定义的缩放体系来说另一个重要的消息是:visible-range消息。该消息的方法接受维度索引作为参数返回在那个维度里所有可见的点数据和linestart的数据范围。例如对于图形里的第一维度 (send w :visible-range 0)
(50.0 80.0) 一个图形的初始缩放类型可以这样指定——在graph-proto原型的:isnew方法里指定:scale-type关键字。 练习 9.4 略。 变换 在选定一个缩放体系之后比如说可变缩放现在我们可以使用变换了。初始情况下是没有变换的 (send w :transformation)
NIL 我们可以通过向:transformation消息传递一个参数对数据中的每一个点和linestart使用一个转换矩阵。浙江转换每一个数据点这些数据点被视为一个列向量通过在该列向量左侧乘一个变换矩阵完成变换。除非使用了值为nil的:draw关键字否则在给定一个新的变换时:transformation方法将重画该图形。举个例子如果这样给定当前变量 (send w :current-variables)
(0 1) 那么我们可以使用下边这个表达式使用一个旋转变换该变换使用浓度变量代替气流变量氨损失代替温度。 (send w :transformation#2A((0 0 -1 0)(0 0 0 -1)(1 0 0 0)(0 1 0 0)))
#2A((0 0 -1 0) (0 0 0 -1) (1 0 0 0) (0 1 0 0)) 下式将返回一个未变换状态的图形 (send w :transformation nil)
NIL 对于变换的使用一些其它方法也是可用的。为了更容易理解原始图形中的温度相对于气流的点数据与变换后图形中的氨损失相对于浓度的点数据这两种点数据之间的对应关系从第一个图形平滑地旋转到第二个图形时有用的。这样的从第一个散点图到另一个散点图的旋转叫做图形差值。通过将变换矩阵设置成一系列不同的中间旋转变换你可以执行这个旋转。另一个方法就是使用:apply-transformation消息。该消息的方法接受一个增加的变换矩阵然后通过使用该增加的变换左乘当前变换来构造一个新的图形变换。默认地该方法重画图形。因为该重画使用缓冲区下式将花费10个步骤将原始图形平滑地旋转成为变换后的图形 (let* ((c (cos (/ pi 20)))(s (sin (/ pi 20)))(m ( (* c (identity-matrix 4))(* s #2A((0 0 -1 0)(0 0 0 -1)(1 0 0 0)(0 1 0 0))))))(dotimes (i 10) (send w :apply-transformation m)))
NIL 旋转后的图形数据 :rotate-2消息可用来使用一个由两个变量索引定义的平面的二维旋转变换。该消息需要一个旋转角度作为第三个参数。例如下式将当前已经变换后的数据的0维到2维的数据旋转pi/20角度。通过在你的图形里将变换重设为nil我们可以再表达式里使用:rotate-2消息目的是将温度对气流图形平滑地变换为氨损失对浓度图形。每步都由两个二维旋转组成。图形仅在第二次旋转之后重画。 可以对:transformation和:apply-transformation方法传递一个维度km的方阵作为参数。这种情况下改变换将作用到当前数据空间的前k维上。这对于一些原型来说是很有用的比如说继承自graph-proto原型的histogram原型。还可以向:apply-transformation消息里传递一个与矩阵参数相同维度的序列这里的矩阵参数是带;basis关键字的。该序列应该包含nil和非nil元素。该变换系统会忽略基里值为nil的对应位置处旋转矩阵的元素。该特性允许低维旋转变换可以在高维图形里有效地使用。 练习 9.5 略。 获取转换后的数据 graph-proto原型提供了用来获取转换后的点数据和linestart坐标数据当前值的消息。例如第一个转换后的点数据和linestart数据可以这样给出 (send w :point-transformed-coordinate 0 0)
-0.6190476190476191(send w :point-transformed-coordinate 1 0)
-1.0000000000000002 和 (send w :linestart-transformed-coordinate 0 0)
-0.6190476190476191(send w :linestart-transformed-coordinate 1 0)
-1.0000000000000002 9.1.4 鼠标事件和鼠标模式 graph-proto原型的:do-click和:do-motion方法是用来支持这样的语法的——即使图形可以在不同的鼠标模式里。新的模式可以添加进来你可以通过使用一个由Mouse Mode菜单项产生的对话框或者向图形发送一个消息在几个可用模式之间进行切换。鼠标模式可以使用一个Lisp符号来区别。每个鼠标模式都有一个标签字符串用在用来切换模式的那个对话框里还有一个光标用来提供当前模式、自身点击和行为动作的可视化暗示。为了自定义一个图形和一个新的原型通常不需要覆盖:do-click和:do-motion方法。添加新的鼠标模式或者覆盖由标准鼠标模式使用的消息是足够的。 增加新的鼠标模式 初始情况下一个图形由两个鼠标模式选择模式和刷模式。:mouse-modes返回区别可用模式的符号列表 (send w :mouse-modes)
(SELECTING BRUSHING) :add-mouse-mode消息添加一个新的鼠标模式或者重定义一个现有的模式。该消息需要一个参数即鼠标模式符号还接受一些关键字参数。这些关键字包括用来指定标签字符串的:title用来指定光标符号的:cusor。:click和:motion关键字可以用来指定消息选择器当图形在新的模式里的时候该消息选择器用来处理点击和动作事件。 举个例子我们可以向图形里添加一个新的模式该图形仅展示在窗体上点击的鼠标的坐标。该模式可以这样添加 (send w :add-mouse-mode show-coordinates:title Show Coordinates:click :do-show-coordinates:cursor finger)
SHOW-COORDINATES 添加了这个模式之后该图形就有3个可用的模式了 (send w :mouse-modes)
(SELECTING BRUSHING SHOW-COORDINATES) 当图形处于show-coordinate模式的时候光标设成了finger(手指形)。当在该模式里点击鼠标时图形将向:do-click消息发送:do-show-coordinates消息该消息带4个参数。因为没有指定动作动作事件将被忽略。 在使用该新模式之前我们需要定义:do-show-coordinates方法。为了避免重画图形我们可以使用XOR绘图在按钮按下的时候绘制一个表示点击位置的字符串。当按钮释放的时候我们可以再次绘制字符串以移除字符串映像 (defmeth w :do-show-coordinates (x y m1 m2)(let ((s (format nil ~s (list x y)))(mode (send self :draw-mode)))(send self :draw-mode xor)(send self :draw-string s x y)(send self :while-button-down #(lambda (x y) nil))(send self :draw-string s x y)(send self :draw-mode mode)))
:DO-SHOW-COORDINATES :while-button-down动作仅仅是等待鼠标按钮弹起。 现在我们使用图形菜单提供的对话框切换到新的模式或者向图形发送一个参数为模型符号的:mouse-mode消息向图形发送一个不带参数的:mouse-mode消息将返回当前模式的符号 (send w :mouse-mode show-coordinates)
SHOW-COORDINATES(send w :mouse-mode)
SHOW-COORDINATES 我们可以展示缩放后的坐标系统里的当前变量的当前坐标而不是展示鼠标点击处的图上坐标。:canvas-to-scaled消息带两个整型参数——代表画布上的一个点返回两个实数——代表缩放后坐标系统的两个当前变量的对应的点 (send w :canvas-to-scaled 100 150)
(-0.4 -0.3904382470119522) :canvas-to-real消息试图转换回是坐标系但是它仅在图形变换为nil时才合适地工作。为了允许你在显示画布、缩放和真实坐标系我们可以定义一个:do-show-coordinates消息来检查修饰符在点击时同时按下shift时显示实坐标在alt使用时显示缩放坐标。如果没有按下这些扩展修饰符它仅显示图上坐标 (defmeth w :do-show-coordinates (x y m1 m2)(let* ((xy (cond (m1 (send self :canvas-to-real x y))(m2 (send self :canvas-to-scaled x y))(t (list x y))))(s (format nil ~s xy))(mode (send self :draw-mode)))(send self :draw-mode xor)(send self :draw-string s x y)(send self :while-button-down #(lambda (x y) nil))(send self :draw-string s x y)(send self :draw-mode mode)))
:DO-SHOW-COORDINATES :scaled-to-canvas和:real-to-canvas消息带两个实数参数表示缩放坐标或实数坐标系中当前变量的坐标然后返回响应画布坐标的列表。 为了助于开发新的鼠标模式一些其它的消息也是可用的。为了说明这些消息中的一些我们可以构造一些鼠标模式用于当鼠标在某点附近点击和按下的时候通过在附近放置一个标签来标示该点。对于使所有高亮的或选中的点显示它们的标签这一标准选项我们提供的方法是对其的替代物。首先我们可以通过使用以下表达式移除定义的模式 (send w :delete-mouse-mode show-coordinates) 该操作不会移除与模式有联系的鼠标点击或者动作消息。该消息需要每个图形至少有一个鼠标模式。其次为了识别各点我们可以制定一个新模式 (send w :add-mouse-mode identify:title Identify:click :do-identify:cursor finger) 为了确定一次点击足够接近一个点新模式的点击方法需要设置一个容差值。每个图形都保持一个容差值范围该范围可以通过:click-range消息设置或获取。该消息返回一个两个整型数的列表用像素表示的该容差值的宽度和高度。默认范围如下 (send w :click-range)
(4 4) 该范围被标准selection模式采用当鼠标点击的时候用来确定选择的点。可以通过发送带两个整型参数的消息来指定一个新的点击范围这两个整型参数是新范围的宽度和高度。 使用范围和鼠标点击位置的坐标我们可以下个图形发送:points-in-rect消息来获取落在指定矩形区域内的影响的所有点的索引列表。例如 (send w :points-in-rect 100 150 10 15) 然后返回位于左上角坐标为(100, 150)宽10像素高15像素的矩形区域内的影像的所有点的索引列表。如果该该区域内没有点数据返回nil。 使用这两个详细我们的:do-identify消息可以定义成下面的样子 (defmeth w :do-identify (x y m1 m2)(let* ((cr (send self :click-range))(p (first (send self :points-in-rect(- x (round (/ (first cr) 2)))(- y (round (/ (second cr) 2)))(first cr)(second cr)))))(if p(let ((mode (send self :draw-mode))(label (send self :point-label p)))(send self :draw-mode xor)(send self :draw-string label x y)(send self :while-button-down #(lambda (x y) nil))(send self :draw-string label x y)(send self :draw-mode mode))))) :points-in-rect消息使用中心点为鼠标点击处的矩形这里的点击位置的宽度和高度与点击范围内指定的值相等。函数first返回由:points-in-rect消息返回的列表的第一个索引如果列表为空则返回nil。因此如果在点击处附近的发现一个点变量p是一个整数如果没有这样的一个点变量p就是nil。 为了说明另一个消息drag-point我们可以通过在图形里移动数据点来定义一个鼠标模式用来编辑我们的数据。我们可以这样设置新的模式 (send w :add-mouse-mode point-moving:title Point Moving:cursor hand:click :do-point-moving) 在图形里:drag-point消息用来拖动一个点该消息带两个参数鼠标点击的图上坐标和检测某点是否接近点击位置。如果存在这样的点将在窗体周围拖拽出一个灰色的长方形直到鼠标按键释放。在此期间该点的坐标将改变同时图形重画除非使用了一个值为nil的:draw关键字。新的坐标将使用上边提到的变换消息来计算。对于一个转换后的图形;drag-point消息试图移动与当前变量相关的平面上的点并保持正交分量不变。如果变换是正交的这才会正确地起作用。:drag-point消息的方法返回被拖动的点的索引如果没有这样的足够接近点击处的点将返回nil。使用:drag-point我们可以这样为:do-point-moving消息定义一个方法 (defmeth w :do-point-moving (x y m1 m2)(let ((p (send self :drag-point x y)))(if p (format t Point ~d has been moved.~% p)))) 练习 9.6 略。 标准鼠标模式 初始情况下由graph-proto原型构造的图形包括两个模式selecting模式和brushing模式。selecting模式使用arrow光标仅需要一个点击方法:do-select-clickbrushing模式使用brush光标它的点击消息和动作消息是:do-brush-click和:do-brush-motion。 针对selecting和brushing模式的点击和动作手势是被用来设计基本的用户接口以针对这些鼠标模式。实际的选择和高亮操作是通过两个其它消息实现的:unselect-all-points和:adjust-points-in-rect。:unselect-all-points消息不带参数。:adjust-points-in-rect消息需要5个参数前4个参数矩形的整型坐标值左上角的两个坐标值还有矩形的宽和高。第5个参数是点状态符号selected或hilited中的的一个。当发生一个点击事件时该图形处于selecting模式:do-select-click方法以下边的方式使用这些消息 如果点击操作不包括扩展修饰器即shift或alt键:unselect-all-points消息发向任意当前选中点集中未被选中的点。 然后:adjust-points-in-rect消息被发送其作用范围是以点击点为中心的由当前点击范围指定的宽和高的矩形区域的坐标范围。第5个参数是selected。这个消息的方法应该会选择指定矩形范围内的所有可视点。 当鼠标按键按下时一个角固定在鼠标点击处的虚线矩形将拖拽出来并覆盖图形。当鼠标按键释放时一个带有最终矩形坐标和符号selected作为参数的:adjust-points-in-rect消息将被发送出去。 当图形处于brushing模式时:do-brush-click方法首先取消对所有点的选择操作除非给定了扩展修饰符然后该方法将发送一个以当前刷坐标和符号selected为参数的:adjust-points-in-rect消息。每次鼠标按下并移动的时候它都会持续发送该消息。:do-brush-motion方法将发送参数为当前刷坐标和符号hilited为参数的消息。当第5个参数是hilited时该消息的方法有望高亮矩形区域内所有可见的点然后将矩形外高亮的点取消高亮效果。 当前刷的位置和大小可以使用:brush消息来获取。刷由4个整数指定右下角的画布坐标x、y连接鼠标的角还有刷的宽度和高度。通过发送一个带四个整数参数即新坐标的:brush消息可以指定新刷的坐标:resize-brush消息提供了一个对话框来交互地设置新刷的大小该消息可以通过使用图形菜单里的Resize Brush菜单项来发送给该图形。 使用:unselect-all-points和:adjust-points-in-rectx消息的协议允许开发新的图形而不用修改刷模式或选择模式方法自身。例如在彩色显示器的系统上我们可以为这两个消息修改这些方法以确保选中的点着红色未选中的点着绘图色。在发送下边这条消息以确保使用彩色绘图之后 (send w :use-color t) 我们可以这样为:unselect-all-points定义消息 (defmeth w :unselect-all-points ()(send self :point-color (iseq (send self :num-points)) nil)(call-next-method)) 该方法将所有点的颜色设置为nil然后调用从graph-proto原型继承来的方法。新的:adjust-points-in-rect方法可以这样定义 (defmeth w :adjust-points-in-rect (x y w h s)(if (eq s selected)(let ((p (send self :points-in-rect x y w h)))(if p (send self :point-color p red))))(call-next-method x y w h s)) 变量p包含指定的矩形区域内的点的索引列表如果该列表不是nil的则金发送:point-color消息。 9.1.5 连接 图形里的点可能处于一些不同的状态selecting和brushing操作提供了一种容易的方式来改变图形里点的状态。为了浏览数据集的一些视图之间的关系Lisp-Stat绘图系统允许图形之间的互连操作。当两个或更多图形连接到一起时系统将试图保证连接在一起的图形里的点状态时相同的。 默认的Lisp-Stat连接系统基于一个松散的联合模型在这个模型里被连接到一起的不同图形的点通过他们的索引值彼此关联。因此在一个图形里选择索引值为3的点同时也会选中与该图形连接的图形里索引值同为3的点。在不存在点的其它属性匹配的意义下这个联合是松散的。在连接的图形中的点可以有不同的标签、颜色或者符号。其它的连接模式也是可能的其中一个可替换的连接模式将在10.6节里说明。 确定哪些图形可以连接 确定哪些图形可以连接到其它图形上的系统依赖:linked和:links两个消息。当向图形发送:links消息时如果该图形没有被连接则返回nil如果该图形被连接了它应该返回一个列表该类表包含所有连接到该图形的接收了该消息的其它图形图形自身可能是该列表的一个元素。:linked消息可以以不带参数的形式发送用来确定该图形是否已经被连接如果已经被连接了返回t否则返回nil。该消息可以接受一个可选参数该参数为t就是连接该图形该参数为nil就是解开连接。 graph-proto原型的:links和linked方法将维护一个包含已连接图形的单列表该列表由被连接的图形的:links消息返回该列表也可以通过使用linked-plots函数好获取当某个图形被连接时该图形的点的状态传递到其它被连接的图形就像由:links消息返回的结果确定的一样。 因为被连接图形的确认完全基于这两个消息定义一个可代替的策略是相当简单的。例如如果我们想确保所有与stack loss data相关的图形都与另一个数据相连而不是与所有的图形都相连我们可以这样设置一个stack loss图形的列表 (setf *stack-plots* nil) 然后给予每个stack图形它们自己的:links和:linked方法它们这样定义 (defmeth w :links ()(if (member self *stack-plots*) *stack-plots*))(defmeth w :linked (optional (link nil set))(when set(setf *stack-plots*(if link(cons self *stack-plots*)(remove self *stack-plots*)))(call-next-method))) 避免为每个新图形添加这些方法的一个方式是定义一个独立的stack loss图形原型。 点状态和连接 图形里的每个点都是四种状态中的一种对应的符号为invisible, normal, hilited和selected。graph-proto绘图方法可以绘制高亮的点和由点的符号的高亮版本选中的点。处于正常状态的点使用正常符号来绘制处于invisible状态的点不被绘制。点的状态可以通过:point-state消息来确定和绘制。对于处于stack loss数据图形里的第一个点 (send w :point-state 0)
NORMAL :point-state消息的方法是矢量化的所以它可以用来确定图形里的所有点的状态方法如下 (send w :point-state (iseq 21))
(NORMAL NORMAL NORMAL NORMAL ...) 为了改变点的状态可以向:point-state消息的第二个参数赋值为一个新状态下边的表达式将第一个点的状态改为selected。当图形里的一个点的状态发生了改变在所有被连接的图形了对应的点的状态也会发生改变。另外会向每个图形发送一个带一个参数的:adjust-screen-point方法该参数是想要改变的那个点的索引值。该方法负责改变屏幕上点的影像。 :adjust-screen-point方法采取的动作即取决于改点的新状态也取决于改点的上一个状态。当发送:adjust-screen-point消息的时候:point-state消息返回的值将表达新的状态点的上一个状态可以通过向:last-point-state消息发送一个参数来获取该参数是改点的索引值。 为了在所有连接在一起的图形该处最佳的对应关系:adjust-screen-ponit方法试图尽可能快地重画点数据以表达它们之间的任何状态变化。对于有些状态改变来说这是相当简单的。如果点的状态在normal, hilited和selected这些值之间变化的话那么:adjust-screen-point方法将使用上一章引入的:replace-symbol方法来改变屏幕上显示的符号。不幸的是如果点的新状态是不可见的不重画图形而从屏幕上清晰地移除改点是很困难的。由于每次将一点设置成不可见状态都要进行重画操作是很浪费的所以:adjust-screen-point方法采取了一个间接的方法它向图形发送一个标识位来指示是否需要调整以适合窗体:needs-adjusting消息用来设置和获取这个标识位的值。在将一个点的状态设置为不可见后该标识位的值为t (send w :needs-adjusting)
NIL (send w :point-state 0 invisible)
INVISIBLE (send w :needs-adjusting)
T 在方便的时候也可以向图形发送:adjust-screen消息如果需要的话该消息的方法将检查该标志位、重设它然后重画图形。因此在将一个点的状态设置为不可见之后在对所有需要改变状态的点进行调整之后:adjust-points-in-rect方法将向所有连接的图形发送:adjust-screen消息。 作为这些想法的一个简单的展示我们可以为我们的图形定义一个新的:adjust-screen-point方法该方法将所有高亮的点和选中的点设置为红色然后将所有未高亮的点设置为nil (defmeth w :adjust-screen-point (i)(let* ((state (send self :point-state i))(color (if (member state (selected hilited)) red)))(send self :point-color i color))(call-next-method i)) 该方法与早先我们使用的那个为:adjust-points-in-rect方法修改的方法有些不同之前的方法仅当在图形自身内部的点的状态被刷模式或选择模式改变的时候才会着色而现在的方法可以确保即使是在被连接的图形里由刷模式或选择模式改变状态的点其颜色也会调整。 练习 9.7 略。 高级状态消息 对于获取和改变点的状态还有一些其它消息是可用的。:point-showing, :point-hilited和:point-selected消息需要一个点的索引做参数然后返回t或nil来分别指示点的状态是否是可见的、高亮的或被选中的对这些消息的第二个参数赋值为t或nil将会对其状态做合适的改变。这3个方法都是矢量化的这3个方法在返回之前都会发送:adjust-screen消息。 :selection消息返回当前被选中的所有点的列表如果向该消息传递一个索引列表作为它的可选参数的话该消息将取消所有选中的点然后选中参数列表里指定的所有可见的点他在返回前将发送:adjust-screen消息。:points-selected消息的方法与:selection方法是等效的:points-hilited和:points-showing的方法也是相同的。 :erase-selection方法将所有当前选中的点的状态改变成不可见状态:show-all-points方法将所有点的状态改为正常状态:focus-on-selection方法将所有未选中的方法设置为不可见状态。这3个方法在返回之前都发送:adjust-screen消息。 这里有两个有用的谓词消息是可用的。:any-points-selected-p消息的方法在图形里任何一个点被选中时都返回t否则返回nil。:all-points-showing-p消息在所有点都是可见状态小返回t任意一个点为不可见状态则返回nil。 9.1.6 窗体布局、缩放和重画 缩放方法 graph-proto原型的:resize方法基于一定的假设该假设是关于图形的布局的。图形的内容点数据与线数据的影像被限制在一个叫做内容矩形的矩形里。如果图形里包含坐标轴它们将立即在内容矩形的外部被画出来。由内容和坐标轴组成的整个图形被一个边缘包裹起来这个边缘被用来束缚图形像标准旋转图形的旋转控制这个边缘沿着窗体的画布的左侧、上侧、右侧和下侧都有一个固定的像素数的留白。当该图形变换大小的时候:resize消息会保持边缘不变然后内容矩形的大小以填充画布的剩余部分。内容矩形的精确的大小和形状依赖于该空间是否需要x和y坐标轴坐标轴是否拥有标签内容矩形是否保持固定的方位比例。对于一个固定的方位比例内容多边形是这样的一个最大矩形它是能装下边缘的并为坐标轴留下空间的那个最大正方形。如果该图形允许方位比例改变内容多边形将是满足这个限制的最大的矩形。图9.3针对一个带有坐标轴和固定方位比例的图形的边缘和内容矩形。 图9.3 带有坐标轴和固定方位比例的图形的边缘和内容矩形 内容矩形可以通过使用:content-rect消息来设置和获取。不适用参数的话该消息将返回内容矩形的坐标的列表比如说一个包含左上角两个坐标值、宽度和高度的列表 (send w :content-rect)
(0 0 250 251) 如果该消息使用四个参数来调用的话它将会把内容矩形设置成指定的坐标。 图形的边缘可以使用:margin消息来设置和获取。不适用参数调用该消息返回一个四个整型数表示的列表代表边缘距离画布的上侧、顶侧、右侧和下侧的距离的像素值。初始情况下一个从graph-proto原型创建的图形的边缘其值都是0 (send w :margin)
(0 0 0 0) 为了设置一个新的边缘可以给:margin消息赋予4个参数。 图形的方位比例可以使用:fixed-aspect消息确定和设置对于固定的方位比例该结果是一个非空值对于一个可变的方位比例该结果返回nil。对于我们的例子方位比例初始情况下是可变的 (send w :fixed-aspect)
NIL 为了改变方位比例你可以向该消息发送一个nil和一个非空的参数以下表达式给予我们的图形一个固定的方位比例。 如果使用:margin, :fixed-aspect, x-axis 或 :y-axis消息布局特征的任意一个发生改变的话那么这些消息的方法将向图形发送:resize和:redraw消息除非:draw关键字被赋予了值为nil的参数。 在返回之前:resize方法将向图形发送:redraw-overlays消息这里的叠置(Overlays)将在9.1.7节里描述它提供方便的方法来实现图形控制比如对旋转图形的旋转控制。 重画方法 绘图原型的重画方法:redraw将重画过程打断成几个阶段。首先它发送:redraw-backgroud消息该消息的方法将擦除画布然后当前图形设置需要的所有坐标轴。下一步:redraw消息向图形发送:redraw-overlays消息。最后向图形发送:redraw-content消息该消息对应的方法负责点数据和线数据自身。:redraw, :redraw-content和:redraw-backgroud消息对应的方法都使用了缓冲区。 将背景绘制与内容绘制分离的原因是绘制坐标轴和叠置操作可能很耗时并且仅有内容改变时这是不需要的。结果像:adjust-screen和:rotate-2这样的消息对应的方法仅需要向图形发送:redraw-content消息以改变图形的内容。 开发新图形时很少要覆盖:graph-proto的:redraw方法通常写一个新的:redraw-content方法就最够了。 下层消息 为了有助于绘图方法graph-proto原型转换系统保持将待转换的点数据和线数据坐标的整型版本即转换后的坐标数据为整型值。整型数的范围即所谓的画布范围与每个变量都有关系。该系统将被变换坐标值从被缩放的比例缩放到画布的比例。那么如果一个变量的范围是从0到200那么在-1.0到1.0范围内坐标值为0.5的坐标对应的画布坐标是150。 变量的画布范围可以使用:canvas-range消息进行获取和设置。对于我们的事例图形里的第一个变量它的范围就是 (send w :canvas-range 0)
(0 232) 对于当前变量的第一个:resize方法将画布范围设置为从0到内容矩形宽度这样的范围对于所有其它变量来说该范围被设置到从0到内容多边形高度这一范围中。 点数据和直线中的点数据的画布坐标可以使用:point-canvas-coordinate和:linestart-canvas-coordinate消息来获取。这些消息可以向我们在上边介绍的其它的坐标消息一样来使用。第一个点的前两个变量的画布坐标可以这样获取 (send w :point-canvas-coordinate 0 0)
80 (send w :point-canvas-coordinate 1 0)
58 绘图系统还会使用一个在内容矩形里的叫做内容原点的点该原点可以使用:content-origin消息来设置和获取 (send w :content-origin)
(9 250) :resize方法将该原点放置在内容矩形的左上角稍低点的位置。:redraw-content方法使用画布坐标作为偏移量。 在这个上下文里使用的一个终极消息是:content-variables该消息对应的方法与:current-variables方法等价除非在这样的情况下——当该变量变化时它没有重画图形。 9.1.7 图形叠置 叠置可以被视为一张透明的纸这张纸在内容绘制之前放置在图形的背景上这些纸保持一定的顺序并从下向上绘制。为了不被图形的内容覆盖叠置层通常只在图形的边缘里绘制。 图形叠置的一个关键的特性是它们的拦截或者说是过滤鼠标点击的能力。graph-proto原型的:do-click方法允许每个叠置层从顶层逐层向下来接收或者传递一个鼠标点击事件。如果该点击事件未被任何叠置层接收那么它仅能传递到当前鼠标模式上。该特性使使用叠置层来实现简单的图形控制成为可能比如让某个图形执行特殊的动作。对于旋转图形的旋转操作就是以叠置层实现的。 图形叠置层是一个对象它继承自graph-overlay-proto原型。通过向图形发送带一个参数的:add-overlay消息该参数是叠置层对象来向一个图形里添加叠置层。该操作将新的叠置层置于所有其它叠置层之上通过向图形发送带一个参数的:delete-overlay消息来移除叠置层该参数是要移除的叠置层对象。 当图形放缩的时候:resize方法在返回之前向图形发送:resize-overlays消息该消息对应的方法向图形里的每个叠置层发送:resize消息。当图形重画时:redraw方法向图形发送:redraw-overlays消息该消息对应的方法向每个叠置层发送:redraw消息从最底层的叠置层开始一直向上直到叠置堆栈的最顶端。 graph-proto :do-click方法向叠置层发送:do-click消息该消息带它接受到的一些参数该过程从最上边的叠置层开始持续穿过其它叠置层直到返回一个非nil值作为点击的结果。如果所有的叠置层对点击动作的反应都返回nil那么对于当前的鼠标模式该click事件将被传递给click方法。 举个例子我们可以向我们的氨气损耗数据图形里添加一个叠置层那个图形包含一个按钮的和一个跟着一个标签的正方形。当在正方形里发生点击事件时图形将接受指令运行一个平滑差值算法该算法从温度对气流的图形到氨气损失到浓度的图形。为了避免图形缩放中需要重新定位按钮我们可以将它放置在图形的左上角位置。 (let ((h ( (send w :text-ascent) (send w :text-descent))))(send w :margin 0 (round (* 1.5 h)) 0 0)) 上式设置了图形上边距目的是为窗体的字体里的一行文本留下足够的空间。通过向叠置层原型发送:new-message消息我们可以构造叠置层对象 (setf interp-overlay (send graph-overlay-proto :new)) 接下来我们可以给予该叠置层一个槽用来容纳描述按钮容器和标签字符串的位置的信息。 (let* ((ascent (send w :text-ascent))(x ascent)(y (round (* 1.5 ascent)))(box ascent))(send interp-overlay :add-slot location(list x y box (round ( x (* 1.5 box)))))) 然后我们可以为该槽定义一个读取方法 (defmeth interp-overlay :location () (slot-value location)) 现在可以定义该叠置层的:redraw方法来获取位置信息这里使用:location消息然后在指定位置绘制一个盒子容器和一个标签 (defmeth interp-overlay :redraw ()(let* ((loc (send self :location))(x (first loc))(y (second loc))(box (third loc))(string-x (fourth loc))(graph (send self :graph)))(send graph :frame-rect x (- y box) box box)(send graph :draw-string Interpolate string-x y))) 该方法向叠置层发送了:graph消息目的是某图形是否含有某叠置层。当某叠置层安装到一个图形以后该叠置层系统将确保该消息返回合适的结果。 :do-click消息将返回nil除非点击落在按钮的盒子容器的内部如果点击发生在盒子容器里该方法将向图形发送:interpolate消息然后返回t进一步确保点击事件没有被做进一步处理 (defmeth interp-overlay :do-click (x y m1 m2)(let* ((loc (send self :location))(box (third loc))(left (first loc))(top (- (second loc) box))(right ( left box))(botton ( top box))(graph (send self :graph)))(when (and ( left x right) ( top y bottom))(send graph :interpolate)t))) 为了完成这个例子我们需要为我们的图形定义一个:interpolate方法。可以基于9.1.3节的平滑旋转循环做一个简单的定义例如 图 9.4 叠置层里带差值按钮的烟雾损失数据图形 (defmeth w :interpolate ()(send self :transformation nil)(dotimes (i 10)(send self :rotate-2 0 2 (/pi 20) :draw nil)(send self :rotate-2 1 3 (/ pi 20)))) 最后通过使用下式将叠置层安装到我们的图形里 (send w :add-overlay interp-overlay) 结果见图9.4图形内容部分发生的点击像以前一样被处理了但是在按钮里的点击引起图形运行了差值算法。 待使用叠置层试验之后我们可以使用以下表达式来移除该叠置层 (send w :delete-overlay interp-overlay) 练习9.8 略 练习9.9 略 练习9.10 略 9.1.8 菜单和菜单项 graph-proto原型的:isnew方法可以通过向图形对象发送:new-menu消息来添加一个菜单该消息对应的方法按顺序向对象发送:menu-title和:menu-template消息。:menu-title消息返回一个标题字符串 (send w :menu-title)
Plot 在构建新的图形菜单的时候该消息可以用来安装一个新的菜单标题在给予一个新的原型一个更合适的菜单标题上这是非常有用的。 :menu-template方法预期会返回一个列表该列表用来构建菜单项该列表可以包含菜单项对象这些菜单项对象可以简单地安装在菜单里或者也可能包含一些用来指定标准菜单项的符号。该消息的graph-proto原型对应的方法返回一个符号列表 (send w :menu-templates)
(LINK SHOWING-LABELS MOUSE REIZE-BRUSH ...) :new-ment方法为这些符号里的每个符号都构造了一个合适的菜单。下边的符号可以用来指定标准菜单项 color——发送:set-sselection-color消息该消息会显示一个对话框用来改变选中的点的颜色。 dash——一个处于不可用状态的分隔符。 focus-on-selection——发送:focus-on-selection消息。 link——使用:linked消息切换图形的链接。 mouse——发送:choose-mouse-mode消息该消息显示一个对话框用来改变鼠标模式。 option——向图形发送:set-options消息该消息显示一个对话框用来设置一些选项。 redraw——发送:redraw消息。 erase-selection——发送:erase-selection消息。 rescale——发送:adjust-to-data消息。 save-image——发送:ask-save-image消息该消息显示一个对话框用来将图像保存成文件。 selection——发送:selection-dialog消息。 show-all——发送:show-all-points消息。 show-labels——使用:showing-labels消息切换显示标签。 symbol——发送:set-selection-symbol消息该消息显示一个对话框用来改变选中的点的符号。 这些菜单项的一些使用:any-points-selected-p或者:all-points-showing-p来确定他们是否处于可用状态使能。 :new-menu消息还可以被赋予一个可替代的标题字符串参数该参数用来代替:menu-title消息的结果。菜单项的可选列表可以通过使用:items关键字来使用该列表可以包含上边列出的符号中的任意多个目的是指定标准菜单项。例如 (send w :new-menu Stack Loss :items (link dash rescale)) 该表达式赋予stack loss图形一个菜单它仅包括link项和rescale项中间用一条直线分开。可替代的方法就是去定义一个新的:menu-template方法然后发送:new-menu不带:items关键字的消息给它。对于开发一个新的原型是很有用的。 9.1.9 杂项消息 还有一些其它消息在graph-proto原型里是可用的。:showing-labels消息用于设置和获取标签选项的状态如果该值为非nil值绘图方法将把它放置于高点的选中的点数据附近。 有些方法是专为2维图形设计的。add-function消息接受一个带一个实数参数的函数代表一个区间的高低界限的两个实数还有一个指定一定数量点数据的关键字参数。该消息对应的方法构造了一个网格并在该网格上计算函数然后将对应的线集合添加到图形上。它也接受关键字参数:color和:type用来指定颜色和线型。该图形将被设置:redraw-content消息除非使用了参数为nil的:draw关键字。 对于线性函数:abline消息带两个参数一个截矩和一个斜率然后将该直线加到图形上。该独立变量的范围就是x轴的当前范围。 :adjust-depth-cuing消息主要由旋转图形来使用但是它也可以被其它图形使用它将整数维的索引作为参数对于那些维度使用画布坐标它们被视为超出屏幕的坐标目的是为图形里的点数据和线数据使用深度标识。通过将点数据的符号改变为符号dot1dot2,dot3和dot4中的一个该点数据是深度标识的。符号dot1是为离视点最远的点使用的dot4用于里视点最近的点。通过改变线的宽度也可以达到显得深度标识效果。 9.2 一些标准绘图原型 第二章里介绍的那5个标准图形类型它们是使用从graph-proto原型继承来的原型构造的。尽管这些图形在外观上相差很大但是它们仅需要向graph-proto提供的原型覆盖和增加少量的方法。 9.2.1 散点图 基础散点图原型scatterplot-proto用于构造一类图形即由plot-points和plot-lines函数构造的图形。它至少需要二个维度的数据。针对:add-points和:add-lines消息的二维散点图允许这样指定新数据——使用两个分离的列表代替所需的列表的列表。那样的话如果s是一个二维散点图x和y是等长度的数值型列表那么下边的两个语句是等价的 (send s :add-points x y) (send s :add-points (list x y)) 如果图形的尺度类型是nil的话:adjust-to-data方法向图形里添加一个坐标轴它还会将坐标范围设置到坐标轴上产生好看的坐标刻度。 9.2.2 散点图矩阵 散点图矩阵原型scatmat-proto至少需要2个维度的数据它使用一个固定的长宽比例它的内容的原点坐标在内容矩形的左下角每个变量在画布上的范围设置在一定范围内——子图的列的左右范围从原点开始量度。通过修改:do-click和:do-motion方法将内容变量设置为包含光标的那个子图的索引上这个操作允许很多继承它的方法可以不经修改就正常工作。 与:add-lines, :add-points, :adjust-screen-point和:redraw-content消息相对的方法通过修改它们可以让矩阵里的所有子图形合适地绘制。如果图形的尺度类型是nil默认地:redraw-content方法还可以在对角图里绘制变量标签和界限。 9.2.3 旋转图形 由spin-plot函数产生的用来旋转图形的原型就是spin-proto该原型至少需要三个维度的数据它使用一个固定的横宽比数据其默认的尺度类型是variable。:content-variables方法使用3个内容变量索引前两个以常规方式使用第三个变量是深度提示变量的索引。:resize方法将内容的原点放置于内容矩形的中间。 :redraw-content方法在进行标准内容重画之前调整深度提示。如果图形要显示坐标轴坐标轴可以通过调用:redraw-content方法来重画该方法可以通过向图形对象发送:draw-axes消息来构造。如果尺度类型为nil:adjust-to-data方法将调整图形的范围以确保所有的点在围绕原点所做的旋转过程中均是可见的。:depth-cuing和:showing-axes消息可以用来确定一个图形时使用深度提示还是显示坐标轴。通过发送一个值为nil或非nil的参数这两个消息可用来图形对应的状态。 旋转图形下边的控件可以以一个叠置层的形式实现该叠置层由:isnew方法加入。这些按钮将旋转的类型设置为pitching、rolling或yawing调整角度符号然后在鼠标按下时发送:rotate消息:do-idle方法也发送该消息。旋转图形里的当前角度可以通过使用:angle消息来设置和获取。角度以度计量。旋转类型可以通过使用:rotation-type消息设置和获取它的值可能是符号pitching, rolling或者yawing中的一个也可能是一个以渐进式旋转来使用的旋转矩阵。如果旋转类型是一个符号:rotate方法使用:rotate-2方法使用合适的变量和角度作为其参数如果该类型是一个矩阵:rotate使用:apply-transformation。 除了添加控件叠置层针对该原型:isnew方法将图形的横宽比设置为固定值并为叠置层设置一个合适的边缘。 针对旋转图形原型的:add-function方法是为三维图形设计的它需要一些参数一个两个参数x轴限值和y轴限值的函数和四个实数数值。:abcplane消息有三个实数参数a,b和c然后增加线段来显示函数f(x,y)abxcy的一段。 9.2.4 直方图 直方图原型是histogram-proto直方图在一个m1维的图形里显示m维的点数据剩余的维度用于图形的y坐标第一个维度被扔掉作为直方图来显示例如下边的表达式针对stack loss 数据构造了一个直方图 (setf hs (histogram (list air temp conc loss))) 该直方图变量的数量是5 (send hs :num-variables)
5 内容变量是 (send hs :content-variables)
(0 4) 那么初始情况下该图形显示的是数据集里第一个变量——气流的直方图。如果对图形hs使用了变换它将显示转换后的数据的第一维度数据的直方图。例如在为们为图形的尺度类型设置为variable之后 (send hs :scale-type variable) 下边的表达式将在10步之内旋转成温度变量的直方图。 对于一维数据的直方图可以给予:add-points方法一个数值序列而不是一个数值序列的列表对于一个m维数据的直方图:add-points方法需要一个m个序列的列表。那么stack loss数据可以使用下式来安装 (send hs :add-points (list air temp conc loss)) :add-lines消息相对应的方法它也可能用于向一个直方图里添加一个密度线它需要一个包含m1个序列的列表。最后一个序列用来作为y坐标轴。例如如果hp是一个一维的降水量数据的直方图该数据在第2.2.1节里它可以这样构造 (setf hp (histogram precipitation)) 那么下边的表达式将一个正常浓度的图形添加到直方图里。 因为:transfomation和:apply-transformation消息相对应的方法可以接受一个维度低于图形维度矩阵你也可以使用相同维度的变换作为直方图里的点数据。 针对:adjust-screen, :adjust-screen-point, :redraw-content和:adjust-points-in-rect这些消息直方图原型提供了它自己的方法。如果尺度类型为nil:adjust-to-data犯法增加一个x轴该方法也会重新计算直方图里的矩形条块与:clear-points和:resize相对应的方法也会修改以适应这些矩形条块。 有两个消息提供了对这些矩形条块的信息访问能力。:bin-count消息返回每一个矩形条块的数量的列表不带参数的:num-bins消息返回当前用到的矩形条块的数量如果给它传递一个正整数来调用的话它将矩形条块的数量给位指定的数量当指定了一个行的矩形条块的数量的时候同时向图形对象发送:redraw消息除非关键字参数:draw接受了值为nil的参数。 9.2.5 名称列表 最后一个标准绘图原型是name-list-proto它的目的是提供一个点标签的可连接的列表它不适用任何数值数据。 名称列表可以使用一个维度数m0的数字来构造。该原型的:add-points方法可以接受一个数值来指定添加的点的数量用来替代坐标值列表的列表。例如针对stack loss数据的名称列表可以这样构造 (setf n (send name-list-proto :new 0)) 拥有21个标签的集合和以使用下式来添加 (send n :add-points 21) :add-lines方法被重定义目的是不要做任何事因为名称列表不需要显示线数据。:adjust-screen, :adjust-screen-point, :redraw-content和:adjust-points-in-rect方法也会被重定义。:isnew方法名称列表拥有一个垂直滚动条。 转载于:https://my.oschina.net/u/1011760/blog/359393