除了前幾章略述的繪圖窗體原型提供的基本繪圖工具以外,Lisp-Stat裏的統計繪圖還須要用來管理數據和將那些數據轉換成屏幕上的圖形的工具集。這些工具由繪圖原型graph-proto提供。更多的專業繪圖工具,好比直方圖和散點圖矩陣,它們都是基於繼承自graph-proto的原型。本章的第一節描述了繪圖原型,第二節略述了更加專用的原型,下一章將展現那些描述如何從這些原型來開發新的繪圖工具類型的例子。 php
graph-proto原型實現了一個散點圖,該散點圖用來表示在m維空間中的點和線的二維視圖。該視圖是這樣構造的,首先對數據進行中心化和尺度化,而後使用線性變換,好比旋轉變換,最後產生該變換的圖形裏的維度的兩個維度的一個散點圖。該原型的:resize和:redraw方法能夠保證:當窗體出現或改變大小時,圖形能夠獲得合適的重畫。鼠標點擊和移動方法支持如下語法:即在第2.5節描述的在選擇模式和刷模式裏使用的繪圖方式。該原型也提供了一個基本的菜單用來與圖形交互。 算法
爲了給出該原型提供的機制的詳細的說明,本節使用一個圖形來檢測5.6.2節引入的stack loss數據。 canvas
graph-proto原型繼承自graph-window-proto原型,graph-proto的:isnew方法須要一個參數,即表示將被視圖化的那個空間的維度的整型值m。stack loss數據由4個變量組成,氣流(Air)、溫度(Temp)、濃度(Conc)和氨損耗(Loss)。視圖化這些數據的圖形能夠這樣構造: app
> (setf w (send graph-proto :new 4)) #<Object: 141e7e8, prototype = GRAPH-PROTO>圖形裏的變量數目可使用:num-variables消息來獲取:
> (send w :num-variables) 4可是,一個圖形建立,該值就不能改變。
圖形包含爲每一個維度描述的標籤字符串,這些字符串可使用:variable-label消息來設置和獲取。初始狀況下,這些字符串是空值: ide
> (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節裏描述。
一個圖形裏能夠包含兩類數據:點數據和線數據。點由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)你可使用可選的第2、三個參數,來指定一個替代的選項。: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節裏描述的重畫方法使用了雙緩衝技術,該技術會產生一個平滑的動畫效果。
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方法傳遞一個維度k<m的方陣做爲參數。這種狀況下改變換將做用到當前數據空間的前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
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-click;brushing模式使用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方法如下邊的方式使用這些消息:
當圖形處於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消息。
圖形裏的點可能處於一些不一樣的狀態,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。
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方法等價,除非在這樣的狀況下——當該變量變化時它沒有重畫圖形。
疊置能夠被視爲一張透明的紙,這張紙在內容繪製以前放置在圖形的背景上,這些紙保持必定的順序,並從下向上繪製。爲了避免被圖形的內容覆蓋,疊置層一般只在圖形的邊緣裏繪製。
圖形疊置的一個關鍵的特性是它們的攔截或者說是過濾鼠標點擊的能力。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
略
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 ...)
這些菜單項的一些使用: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關鍵字的消息給它。對於開發一個新的原型是頗有用的。
還有一些其它消息在graph-proto原型裏是可用的。:showing-labels消息用於設置和獲取標籤選項的狀態,若是該值爲非nil值,繪圖方法將把它放置於高點的選中的點數據附近。
有些方法是專爲2維圖形設計的。add-function消息接受一個帶一個實數參數的函數,表明一個區間的高低界限的兩個實數,還有一個指定必定數量點數據的關鍵字參數。該消息對應的方法構造了一個網格,並在該網格上計算函數,而後將對應的線集合添加到圖形上。它也接受關鍵字參數:color和:type,用來指定顏色和線型。該圖形將被設置:redraw-content消息,除非使用了參數爲nil的:draw關鍵字。
對於線性函數,:abline消息帶兩個參數,一個截矩和一個斜率,而後將該直線加到圖形上。該獨立變量的範圍就是x軸的當前範圍。
:adjust-depth-cuing消息主要由旋轉圖形來使用,可是它也能夠被其它圖形使用,它將整數維的索引做爲參數,對於那些維度使用畫布座標(它們被視爲超出屏幕的座標),目的是爲圖形裏的點數據和線數據使用深度標識。經過將點數據的符號改變爲符號dot1,dot2,dot3和dot4中的一個,該點數據是深度標識的。符號dot1是爲離視點最遠的點使用的,dot4用於裏視點最近的點。經過改變線的寬度也能夠達到顯得深度標識效果。
第二章裏介紹的那5個標準圖形類型,它們是使用從graph-proto原型繼承來的原型構造的。儘管這些圖形在外觀上相差很大,可是它們僅須要向graph-proto提供的原型,覆蓋和增長少許的方法。
基礎散點圖原型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方法向圖形裏添加一個座標軸,它還會將座標範圍設置到座標軸上產生好看的座標刻度。
散點圖矩陣原型scatmat-proto至少須要2個維度的數據,它使用一個固定的長寬比例,它的內容的原點座標在內容矩形的左下角,每一個變量在畫布上的範圍設置在必定範圍內——子圖的列的左右範圍,從原點開始量度。經過修改:do-click和:do-motion方法,將內容變量設置爲包含光標的那個子圖的索引上,這個操做容許不少繼承它的方法能夠不經修改就正常工做。
與:add-lines, :add-points, :adjust-screen-point和:redraw-content消息相對的方法,經過修改它們可讓矩陣裏的全部子圖形合適地繪製。若是圖形的尺度類型是nil,默認地,:redraw-content方法還能夠在對角圖裏繪製變量標籤和界限。
由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)=a+bx+cy的一段。
直方圖原型是histogram-proto,直方圖在一個m+1維的圖形裏顯示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消息相對應的方法(它也可能用於向一個直方圖裏添加一個密度線),它須要一個包含m+1個序列的列表。最後一個序列用來做爲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的參數。
最後一個標準繪圖原型是name-list-proto,它的目的是提供一個點標籤的可鏈接的列表,它不適用任何數值數據。
名稱列表可使用一個維度數m=0的數字來構造。該原型的: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方法名稱列表擁有一個垂直滾動條。