Lisp-Stat翻译 —— 第九章 统计绘图窗体
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
第九章 統(tǒng)計(jì)繪圖窗體
????除了前幾章略述的繪圖窗體原型提供的基本繪圖工具之外,Lisp-Stat里的統(tǒng)計(jì)繪圖還需要用來(lái)管理數(shù)據(jù)和將那些數(shù)據(jù)轉(zhuǎn)換成屏幕上的圖形的工具集。這些工具由繪圖原型graph-proto提供。更多的專業(yè)繪圖工具,比如直方圖和散點(diǎn)圖矩陣,它們都是基于繼承自graph-proto的原型。本章的第一節(jié)描述了繪圖原型,第二節(jié)略述了更加專用的原型,下一章將展示那些描述如何從這些原型來(lái)開發(fā)新的繪圖工具類型的例子。
9.1 繪圖原型
????graph-proto原型實(shí)現(xiàn)了一個(gè)散點(diǎn)圖,該散點(diǎn)圖用來(lái)表示在m維空間中的點(diǎn)和線的二維視圖。該視圖是這樣構(gòu)造的,首先對(duì)數(shù)據(jù)進(jìn)行中心化和尺度化,然后使用線性變換,比如旋轉(zhuǎn)變換,最后產(chǎn)生該變換的圖形里的維度的兩個(gè)維度的一個(gè)散點(diǎn)圖。該原型的:resize和:redraw方法可以保證:當(dāng)窗體出現(xiàn)或改變大小時(shí),圖形可以得到合適的重畫。鼠標(biāo)點(diǎn)擊和移動(dòng)方法支持以下語(yǔ)法:即在第2.5節(jié)描述的在選擇模式和刷模式里使用的繪圖方式。該原型也提供了一個(gè)基本的菜單用來(lái)與圖形交互。
? ? 為了給出該原型提供的機(jī)制的詳細(xì)的說(shuō)明,本節(jié)使用一個(gè)圖形來(lái)檢測(cè)5.6.2節(jié)引入的stack loss數(shù)據(jù)。
9.1.1 構(gòu)造一個(gè)新的圖形
????graph-proto原型繼承自graph-window-proto原型,graph-proto的:isnew方法需要一個(gè)參數(shù),即表示將被視圖化的那個(gè)空間的維度的整型值m。stack loss數(shù)據(jù)由4個(gè)變量組成,氣流(Air)、溫度(Temp)、濃度(Conc)和氨損耗(Loss)。視圖化這些數(shù)據(jù)的圖形可以這樣構(gòu)造:
> (setf w (send graph-proto :new 4)) #<Object: 141e7e8, prototype = GRAPH-PROTO> 圖形里的變量數(shù)目可以使用:num-variables消息來(lái)獲取: > (send w :num-variables) 4 但是,一個(gè)圖形創(chuàng)建,該值就不能改變。? ? 圖形包含為每個(gè)維度描述的標(biāo)簽字符串,這些字符串可以使用:variable-label消息來(lái)設(shè)置和獲取。初始情況下,這些字符串是空值:
> (send w :variable-label 0) "" 可以通過(guò)使用一個(gè)字符串作為其第二個(gè)參數(shù)的方式來(lái)改變它的值: > (send w :variable-label 0 "Air") "Air" 這個(gè)消息的方法是矢量化的。表示三個(gè)維度的標(biāo)簽可以通過(guò)下式指定: > (send w :variable-label '(1 2 3) (list "Temp." "Conc" "Loss")) ("Temp." "Conc" "Loss") 現(xiàn)在我們可以獲得這4個(gè)變量了: > (send w :variable-label '(0 1 2 3)) ("Air" "Temp." "Conc" "Loss") ? ? :graph-proto原型的:isnew方法可以接受graph-window-proto原型的:isnew方法能夠接受的所有關(guān)鍵字參數(shù)。此外,:variable-label關(guān)鍵字可用來(lái)指定一個(gè)m維度的初始化變量標(biāo)簽字符串。還有一個(gè)關(guān)鍵字:scale-type將在接下來(lái)的9.1.3節(jié)里描述。9.1.2 增加數(shù)據(jù)可坐標(biāo)軸
點(diǎn)數(shù)據(jù)
????一個(gè)圖形里可以包含兩類數(shù)據(jù):點(diǎn)數(shù)據(jù)和線數(shù)據(jù)。點(diǎn)由m維空間里的坐標(biāo)和一些附加信息組成,這些附加信息比如像用來(lái)繪制點(diǎn)的顏色和符號(hào)。初始情況下,圖形里不包含點(diǎn)數(shù)據(jù):
> (send w :num-points) 0 點(diǎn)數(shù)據(jù)可以使用:add-points消息來(lái)添加,該消息的方法需要一個(gè)參數(shù):一個(gè)m維的列表,表示將要添加的點(diǎn)的坐標(biāo)值。下邊的表達(dá)式將stack loss數(shù)據(jù)添加到圖形里: > (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 數(shù)據(jù)集里有21個(gè)點(diǎn): > (send w :num-points) 21 :add-points方法會(huì)在屏幕上繪制了新的點(diǎn),除非提供了值為nil的:draw關(guān)鍵字。該方法也允許使用:point-label關(guān)鍵字的標(biāo)簽字符串列表。? ? 盡管現(xiàn)在該圖形包含這些數(shù)據(jù),在它的窗體上不會(huì)顯示任何點(diǎn)。原因是這些數(shù)據(jù)點(diǎn)在初始情況下,當(dāng)做在每一個(gè)變量的單位間隔組成的一個(gè)數(shù)據(jù)范圍。為了調(diào)整圖形視圖化的范圍以適應(yīng)這些數(shù)據(jù),你可以向圖形發(fā)送:adjust-to-data消息,你可以使用下邊的表達(dá)式發(fā)送該消息:
> (send w :adjust-to-data) NIL 或者也可以從圖形的菜單里選擇Rescale Plot菜單項(xiàng)。這個(gè)消息的方法將調(diào)整圖形視圖化的數(shù)據(jù)范圍以精確地適應(yīng)對(duì)應(yīng)的數(shù)據(jù)的范圍跨度。發(fā)送該消息之后,圖形應(yīng)該顯示了一個(gè)散點(diǎn)圖,該散點(diǎn)圖表示出數(shù)據(jù)集中前兩個(gè)變量。結(jié)果圖形如圖9.1所示。? ? 當(dāng)向一個(gè)圖形里加入新點(diǎn)的時(shí)候,每一個(gè)點(diǎn)都會(huì)分給一個(gè)默認(rèn)的符號(hào)、顏色和標(biāo)簽,對(duì)于第一個(gè)點(diǎn):
> (send w :point-symbol 0) DISK > (send w :point-color 0) NIL > (send w :point-label 0) "0"
圖9.1 stack loss數(shù)據(jù)集中氣流與溫度變量圖示
????默認(rèn)符號(hào)是一個(gè)叫disk的符號(hào)。默認(rèn)顏色是nil,意思是改點(diǎn)使用當(dāng)前繪圖窗體的顏色來(lái)繪制。默認(rèn)標(biāo)簽是該點(diǎn)索引的字符串形式。通過(guò)向這些消息傳遞第二個(gè)參數(shù)來(lái)為這些屬性指定新值。符號(hào)應(yīng)該取自plot-symbol-symbols函數(shù)返回的列表。顏色值應(yīng)該是nil或者由color-symbols返回的列表的一個(gè)值。:point-symbol, :point-color和:point-label消息的方法是矢量化的,因此將所有21個(gè)點(diǎn)的符號(hào)設(shè)置到diamond變量里。這三個(gè)消息都不會(huì)引起圖形重畫;為了看到屏幕上改變的影響,你不得不向圖形發(fā)送一個(gè)重畫命令。
? ? 每個(gè)點(diǎn)還有一個(gè)狀態(tài)值,該值可以使用:point-stat消息來(lái)設(shè)置和獲取。該狀態(tài)值可以是invisible, normal, hilited和selected這四個(gè)符號(hào)中的一個(gè)。點(diǎn)數(shù)據(jù)狀態(tài)用做鏈接機(jī)制的一部分,它將在9.1.5節(jié)詳細(xì)描述。
? ? :point-coordinate消息可以用來(lái)為某一特定點(diǎn)獲取和設(shè)置單一變量坐標(biāo)的值。該消息的方法需要兩個(gè)參數(shù):變量的索引和點(diǎn)數(shù)據(jù)的索引。因此,第一次觀察到的氣流、溫度、濃度和氨損耗的值是:
> (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消息的方法也是矢量化的。新的坐標(biāo)值可以以第三個(gè)參數(shù)的形式來(lái)指定。在強(qiáng)調(diào)一次,提供新值的時(shí)候,方法不會(huì)重畫圖形。 練習(xí) 9.1
略。
線數(shù)據(jù)
????線數(shù)據(jù)表示從處在m維空間里叫做linestart的點(diǎn)位開始,每個(gè)起點(diǎn)都包含額外的信息(比如在繪制線段時(shí)使用的寬度和線型信息),還有用來(lái)作為線段終點(diǎn)的另一個(gè)linestart的索引,nil的下標(biāo)表示該linestart僅用作在其它地方凱斯的線段的終點(diǎn)。環(huán)形定義是是運(yùn)行的:這不會(huì)帶來(lái)什么問(wèn)題,因?yàn)槔L圖路徑斤通過(guò)linestart集合一次。
? ? 當(dāng)創(chuàng)建一個(gè)圖形的時(shí)候,它沒有l(wèi)inestart:
> (send w :num-lines) 0? ? stack loss數(shù)據(jù)事實(shí)上是隨著時(shí)間收集到的。通過(guò)繪制一條從第一個(gè)觀察點(diǎn)到第二個(gè)觀察點(diǎn)、從第二個(gè)到第三個(gè)觀察點(diǎn)等等的直線的方式,來(lái)表示時(shí)間關(guān)系,這可能是很有用的。:add-line消息可以增加這樣一個(gè)線段序列,它的方法需要一個(gè)參數(shù),一個(gè)針對(duì)linestart的坐標(biāo)的m維列表的列表。因此,下式將針對(duì)stack loss數(shù)據(jù)向我們的圖形里添加連在一起的線段序列。這些線數(shù)據(jù)有助于說(shuō)明,在數(shù)據(jù)集的前10個(gè)觀測(cè)量里,氣流和溫度是下降的。
> (send w :add-lines (list air temp conc loss)) NIL? ? :add-lines消息的方法還允許使用:type關(guān)鍵字來(lái)提供線型,這時(shí)直線會(huì)會(huì)自導(dǎo)圖形上,除非使用了值為nil的:draw關(guān)鍵字。
? ? 每一個(gè)linestart都有一個(gè)寬度、類型和顏色,用來(lái)從一個(gè)linestart繪制到下一個(gè)linestart。對(duì)于第一個(gè)linestart,對(duì)應(yīng)第一個(gè)數(shù)據(jù)點(diǎn):
> (send w :linestart-width 0) 1 > (send w :linestart-type 0) SOLID > (send w :linestart-color 0) NIL該方法也是矢量化的,通過(guò)提供一個(gè)新值作為第二個(gè)參數(shù)它可以被用來(lái)改變linestart屬性的值,數(shù)值的改變不會(huì)引起任何繪圖行為的發(fā)生。
? ? 每個(gè)linestart都包含序列里下一個(gè)linestart的索引,在繪圖中用來(lái)作為一個(gè)線段的終點(diǎn),:add-lines方法將這些linestart連接成序列,因此:
> (send w :linestart-next 0) 1 > (send w :linestart-next 1) 2 序列里的最后一個(gè)linestart沒有關(guān)于下一個(gè)linestart的索引: > (send w :linestart-next 20) NIL 可以通過(guò)在線段的開始處為linestart設(shè)置下一個(gè)線段的值為nil,來(lái)移除一個(gè)線段。例如: > (send w :linestart-next 7 nil) NIL 上式移除了點(diǎn)號(hào)索引為7和8的點(diǎn)之間的線段,為了使該改變可見,你可以向圖形發(fā)送:redraw消息。? ? 就想點(diǎn)一樣,你也可以獲取和改變linestart的坐標(biāo)。第一個(gè)linestart的前兩個(gè)坐標(biāo)值可以這樣給出(對(duì)應(yīng)氣流和溫度變量):
> (send w :linestart-coordinate 0 0) 80.0 > (send w :linestart-coordinate 1 0) 27.0 :linestart-coordinate消息的方法是矢量化的,可以用來(lái)改變坐標(biāo)的值。如果坐標(biāo)改變了,圖形不會(huì)重畫。練習(xí) 9.2
略。
坐標(biāo)軸和當(dāng)前變量
????由graph-proto原型實(shí)現(xiàn)的散點(diǎn)圖,使用:x-axis和:y-axis消息,可以用來(lái)表示x和y坐標(biāo)軸。不提供參數(shù),這些消息返回當(dāng)前坐標(biāo)軸的狀態(tài)。例如:
> (send w :x-axis) (NIL NIL 0) 列表的三個(gè)元素表示該坐標(biāo)軸是否正處于顯示狀態(tài),是否有標(biāo)簽,它使用的刻度的數(shù)目。發(fā)送一個(gè)值為t的參數(shù)將重畫帶坐標(biāo)軸的圖形。默認(rèn)地,不使用標(biāo)簽,使用4為刻度: > (send w :x-axis t) (T NIL 4) 你可以使用可選的第二、三個(gè)參數(shù),來(lái)指定一個(gè)替代的選項(xiàng)。:y-axis方法是相同的。當(dāng)坐標(biāo)軸狀態(tài)改變時(shí),這兩個(gè)方法都向圖形發(fā)送:resize和:redraw消息,除非使用了值為nil的:draw關(guān)鍵字。為了使用該關(guān)鍵字,你需要給出所有這3個(gè)可選參數(shù)。在加入x、y坐標(biāo)軸之后,我們的圖形如圖9.2所示。
圖9.2 stack loss數(shù)據(jù)集中氣流和溫度變量的x坐標(biāo)與y坐標(biāo)圖形,連續(xù)觀測(cè)量由直線相連
? ? :adjust-to-data方法會(huì)將被一個(gè)圖形視圖化的數(shù)據(jù)范圍設(shè)置到數(shù)據(jù)范圍,該方法通常不會(huì)產(chǎn)生一個(gè)效果很好的坐標(biāo)軸標(biāo)簽。你也可以使用:range消息為每個(gè)變量獲取和改變數(shù)據(jù)范圍。對(duì)于氣流和溫度變量:
> (send w :range 0) (50.0 80.0) > (send w :range 1) (17.0 29.0) 結(jié)果列表的元素表示該數(shù)據(jù)范圍的高低邊界。為了改變一個(gè)變量的數(shù)據(jù)范圍,你需要使用兩個(gè)附加值,新的高低邊界。 > (send w :range 1 15 30) (15.0 30.0) 上式將溫度變量的數(shù)據(jù)范圍設(shè)置到區(qū)間[15, 30]之間。當(dāng)數(shù)據(jù)范圍改變的時(shí)候,圖形將重畫,除非使用了值為nil的:draw關(guān)鍵字。:range消息的方法是矢量化的。? ? get-nice-range函數(shù)可用來(lái)幫助找到數(shù)據(jù)范圍和刻度的一個(gè)好的組合。該函數(shù)帶3個(gè)參數(shù),區(qū)間的高、低邊界端點(diǎn)和刻度值的整型數(shù)值。它返回一個(gè)3值列表,表示包含原始區(qū)間的區(qū)間端點(diǎn),和接近指定數(shù)值得刻度的數(shù)值。新值應(yīng)該產(chǎn)生合理的坐標(biāo)軸。例如,對(duì)于大約有4刻度值的溫度變量的數(shù)據(jù)范圍來(lái)說(shuō):
> (get-nice-range 17 27 4) (16.0 28.0 7) 對(duì)于這個(gè)變量推薦的設(shè)置范圍是[16, 28],表達(dá)式如下: > (send w :range 1 16 28) (16.0 28.0) 將y軸設(shè)置為使用7作為刻度值: > (send w :y-axis t t 7) (T T 7) 結(jié)果坐標(biāo)軸標(biāo)記為16, 18, 20, ..., 28.? ? 到目前為止,我們的圖形只顯示了我們的4變量數(shù)據(jù)集的前兩個(gè)。:current-variables消息可用來(lái)設(shè)置獲取構(gòu)造該圖形的兩個(gè)當(dāng)前變量。默認(rèn)的情況是顯示前兩個(gè)變量:
> (send w :current-variables) (0 1) 下邊的表達(dá)式命令圖形切換到使用其它兩個(gè)變量。該方法向圖像發(fā)送:redraw消息,除非使用了值為nil的:draw關(guān)鍵字。練習(xí)9.3
略。
清除繪圖數(shù)據(jù)
????圖形里當(dāng)前的數(shù)據(jù)可以使用:clear, :clear-points和:clear-lines消息來(lái)清除。:clear-points消息移除內(nèi)部數(shù)據(jù),并將點(diǎn)數(shù)設(shè)為0,該消息會(huì)重畫圖形,除非使用了值為nil的:draw關(guān)鍵字。用來(lái)清除linestart數(shù)據(jù)的:clear-lines消息是相同的。:clear消息會(huì)將點(diǎn)數(shù)據(jù)和線數(shù)據(jù)一并移除。
? ? 這些消息對(duì)于動(dòng)畫是非常有用的,即顯示在圖形里的快速變化的數(shù)據(jù)動(dòng)畫。鑒于之后第9.1.6節(jié)里描述的重畫方法使用了雙緩沖技術(shù),該技術(shù)會(huì)產(chǎn)生一個(gè)平滑的動(dòng)畫效果。
9.1.3 縮放與變換
????graph-proto原型最主要的特征就是其允許對(duì)數(shù)據(jù)進(jìn)行線性變換的能力,尤其是旋轉(zhuǎn)變換。變換過(guò)程可以分解為兩個(gè)階段。第一階段由數(shù)據(jù)的中心化和縮放組成;第二階段由對(duì)已經(jīng)中心化和縮放過(guò)的詩(shī)句使用一個(gè)變化矩陣組成。然后,將顯示一個(gè)散點(diǎn)圖,即該變換的結(jié)果的坐標(biāo)中的兩個(gè)數(shù)據(jù)。
縮放和中心化
????縮放和中心化階段引入了一個(gè)新的坐標(biāo)系統(tǒng),即縮放坐標(biāo)系。作為對(duì)比,原來(lái)指定數(shù)據(jù)的那個(gè)坐標(biāo)系叫做真實(shí)坐標(biāo)系。可以使用人造的縮放體系,但是有兩個(gè)叫定縮放和變縮放的標(biāo)準(zhǔn)的縮放體系對(duì)大多數(shù)情況來(lái)說(shuō)是足夠用了。這些縮放類型可以通過(guò)使用:adjust-to-data方法和:scale-type方法來(lái)實(shí)現(xiàn)。在處理之前,我們可以私用下邊兩個(gè)表達(dá)式將坐標(biāo)軸從圖形中移除:
> (send w :x-axis nil) (NIL NIL 4) > (send w :y-axis nil) (NIL NIL 4) 對(duì)于變換后的數(shù)據(jù)來(lái)說(shuō),坐標(biāo)軸是沒有意義的。????當(dāng)構(gòu)造一個(gè)新的圖形時(shí),它的縮放類型是nil:
> (send w :scale-type) NIL 這意味著沒有進(jìn)行中心化和縮放,每個(gè)維度內(nèi)的視圖范圍設(shè)置成真實(shí)坐標(biāo)系的對(duì)應(yīng)維度里的數(shù)據(jù)范圍。縮放坐標(biāo)系里的數(shù)據(jù)范圍可以通過(guò)使用:scale-range消息來(lái)獲取: > (send w :range 0) (50.0 80.0)> (send w :scaled-range 0) (50.0 80.0) 因?yàn)樵诔跏记闆r下沒有進(jìn)行縮放,縮放過(guò)的和真實(shí)的數(shù)據(jù)范圍是相等的。? ? 為了檢測(cè)這個(gè)新的縮放類型,我們可以通過(guò)將我們的圖形的縮放類型設(shè)置為variable符號(hào)來(lái)檢測(cè),表達(dá)式如下:
> (send w :scale-type 'variable) VARIABLE :scale-type消息的方法將發(fā)送:adjust-to-data消息,并重畫圖形,除非使用了值為nil的:draw關(guān)鍵字。對(duì)于一個(gè)使用變化的縮放的圖形,:adjust-to-data方法為每個(gè)變量在數(shù)據(jù)范圍的中心位置進(jìn)行中心化,在將每個(gè)中心化后的變量縮放到[-1, 1]區(qū)間上,最后將縮放后的數(shù)據(jù)范圍設(shè)置到[-sqrt(m), sqrt(m)]區(qū)間。這將確保數(shù)據(jù)進(jìn)行旋轉(zhuǎn)之后都在縮放范圍之內(nèi)。在將縮放類型設(shè)置成variable之后,圖形的第一個(gè)維度的數(shù)據(jù)范圍和縮放后的數(shù)據(jù)范圍如下: > (send w :range 0) (34.99999999999999 95.0) > (send w :scaled-range 0) (-2.0 2.0) ? ? 當(dāng)變量在可比較的尺度內(nèi)無(wú)法量度的時(shí)候,可變縮放是合適的。如果變量在可比較的尺度上是可以量度的,通過(guò)在每個(gè)維度上使用相同的比例系數(shù),我們可能想要在數(shù)據(jù)內(nèi)保持一定的角度,這在固定縮放策略里是一體的。如果尺度類型設(shè)置為符號(hào)fixed,那么:adjust-to-data方法將在數(shù)據(jù)區(qū)間的中間進(jìn)行數(shù)據(jù)的中心化,并為每個(gè)維度選擇縮放系數(shù)為1,然后為每個(gè)變量設(shè)置其縮放后的區(qū)間,該數(shù)值為數(shù)據(jù)中心化的數(shù)據(jù)最大范圍的sqrt(m)倍。? ? 如果可變縮放和固定縮放體系都不能勝任你的需求,你可以通過(guò)定義一個(gè)新的:adjudt-to-data方法來(lái)定義你自己的縮放體系。該方法可以使用:scale-range方法來(lái)設(shè)置一個(gè)新的縮放范圍,再使用:center和:scale消息來(lái)設(shè)置或者獲取中心和縮放系數(shù)。對(duì)于我們的圖形,因?yàn)槭强勺兛s放,第一個(gè)變量的縮放系數(shù)和中心是:
> (send w :scale 0) 15.0 > (send w :center 0) 65.0 當(dāng)使用:scaled-range消息設(shè)置縮放范圍時(shí),原始坐標(biāo)系內(nèi)的數(shù)據(jù)范圍可會(huì)調(diào)整以適應(yīng)該變化。:scale, :center和:scaled-range等消息是矢量化的。當(dāng)使用該方法設(shè)置新值時(shí),將會(huì)重畫圖形,除非使用了值為nil的:draw關(guān)鍵字。? ? 對(duì)于定義自定義的縮放體系來(lái)說(shuō)另一個(gè)重要的消息是:visible-range消息。該消息的方法接受維度索引作為參數(shù),返回在那個(gè)維度里所有可見的點(diǎn)數(shù)據(jù)和linestart的數(shù)據(jù)范圍。例如,對(duì)于圖形里的第一維度:
> (send w :visible-range 0) (50.0 80.0) ? ? 一個(gè)圖形的初始縮放類型可以這樣指定——在graph-proto原型的:isnew方法里指定:scale-type關(guān)鍵字。 練習(xí) 9.4
略。
變換
????在選定一個(gè)縮放體系之后,比如說(shuō)可變縮放,現(xiàn)在我們可以使用變換了。初始情況下,是沒有變換的:
> (send w :transformation) NIL 我們可以通過(guò)向:transformation消息傳遞一個(gè)參數(shù),對(duì)數(shù)據(jù)中的每一個(gè)點(diǎn)和linestart使用一個(gè)轉(zhuǎn)換矩陣。浙江轉(zhuǎn)換每一個(gè)數(shù)據(jù)點(diǎn),這些數(shù)據(jù)點(diǎn)被視為一個(gè)列向量,通過(guò)在該列向量左側(cè)乘一個(gè)變換矩陣完成變換。除非使用了值為nil的:draw關(guān)鍵字,否則在給定一個(gè)新的變換時(shí):transformation方法將重畫該圖形。舉個(gè)例子,如果這樣給定當(dāng)前變量: > (send w :current-variables) (0 1)那么我們可以使用下邊這個(gè)表達(dá)式使用一個(gè)旋轉(zhuǎn)變換,該變換使用濃度變量代替氣流變量,氨損失代替溫度。
> (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)) 下式將返回一個(gè)未變換狀態(tài)的圖形: > (send w :transformation nil) NIL ? ? 對(duì)于變換的使用,一些其它方法也是可用的。為了更容易理解原始圖形中的溫度相對(duì)于氣流的點(diǎn)數(shù)據(jù),與變換后圖形中的氨損失相對(duì)于濃度的點(diǎn)數(shù)據(jù),這兩種點(diǎn)數(shù)據(jù)之間的對(duì)應(yīng)關(guān)系,從第一個(gè)圖形平滑地旋轉(zhuǎn)到第二個(gè)圖形時(shí)有用的。這樣的從第一個(gè)散點(diǎn)圖到另一個(gè)散點(diǎn)圖的旋轉(zhuǎn)叫做圖形差值。通過(guò)將變換矩陣設(shè)置成一系列不同的中間旋轉(zhuǎn)變換,你可以執(zhí)行這個(gè)旋轉(zhuǎn)。另一個(gè)方法就是使用:apply-transformation消息。該消息的方法接受一個(gè)增加的變換矩陣,然后通過(guò)使用該增加的變換左乘當(dāng)前變換來(lái)構(gòu)造一個(gè)新的圖形變換。默認(rèn)地,該方法重畫圖形。因?yàn)樵撝禺嬍褂镁彌_區(qū),下式將花費(fèi)10個(gè)步驟將原始圖形平滑地旋轉(zhuǎn)成為變換后的圖形: > (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 旋轉(zhuǎn)后的圖形數(shù)據(jù)? ? :rotate-2消息可用來(lái)使用一個(gè)由兩個(gè)變量索引定義的平面的二維旋轉(zhuǎn)變換。該消息需要一個(gè)旋轉(zhuǎn)角度作為第三個(gè)參數(shù)。例如,下式將當(dāng)前已經(jīng)變換后的數(shù)據(jù)的0維到2維的數(shù)據(jù)旋轉(zhuǎn)pi/20角度。通過(guò)在你的圖形里將變換重設(shè)為nil,我們可以再表達(dá)式里使用:rotate-2消息,目的是將溫度對(duì)氣流圖形平滑地變換為氨損失對(duì)濃度圖形。每步都由兩個(gè)二維旋轉(zhuǎn)組成。圖形僅在第二次旋轉(zhuǎn)之后重畫。
? ? 可以對(duì):transformation和:apply-transformation方法傳遞一個(gè)維度k<m的方陣作為參數(shù)。這種情況下改變換將作用到當(dāng)前數(shù)據(jù)空間的前k維上。這對(duì)于一些原型來(lái)說(shuō)是很有用的,比如說(shuō)繼承自graph-proto原型的histogram原型。還可以向:apply-transformation消息里傳遞一個(gè)與矩陣參數(shù)相同維度的序列,這里的矩陣參數(shù)是帶;basis關(guān)鍵字的。該序列應(yīng)該包含nil和非nil元素。該變換系統(tǒng)會(huì)忽略基里值為nil的對(duì)應(yīng)位置處旋轉(zhuǎn)矩陣的元素。該特性允許低維旋轉(zhuǎn)變換可以在高維圖形里有效地使用。
練習(xí) 9.5
略。
獲取轉(zhuǎn)換后的數(shù)據(jù)
????graph-proto原型提供了用來(lái)獲取轉(zhuǎn)換后的點(diǎn)數(shù)據(jù)和linestart坐標(biāo)數(shù)據(jù)當(dāng)前值的消息。例如,第一個(gè)轉(zhuǎn)換后的點(diǎn)數(shù)據(jù)和linestart數(shù)據(jù)可以這樣給出:
> (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.00000000000000029.1.4 鼠標(biāo)事件和鼠標(biāo)模式
????graph-proto原型的:do-click和:do-motion方法是用來(lái)支持這樣的語(yǔ)法的——即使圖形可以在不同的鼠標(biāo)模式里。新的模式可以添加進(jìn)來(lái),你可以通過(guò)使用一個(gè)由Mouse Mode菜單項(xiàng)產(chǎn)生的對(duì)話框,或者向圖形發(fā)送一個(gè)消息,在幾個(gè)可用模式之間進(jìn)行切換。鼠標(biāo)模式可以使用一個(gè)Lisp符號(hào)來(lái)區(qū)別。每個(gè)鼠標(biāo)模式都有一個(gè)標(biāo)簽字符串,用在用來(lái)切換模式的那個(gè)對(duì)話框里,還有一個(gè)光標(biāo)用來(lái)提供當(dāng)前模式、自身點(diǎn)擊和行為動(dòng)作的可視化暗示。為了自定義一個(gè)圖形和一個(gè)新的原型,通常不需要覆蓋:do-click和:do-motion方法。添加新的鼠標(biāo)模式或者覆蓋由標(biāo)準(zhǔn)鼠標(biāo)模式使用的消息是足夠的。
增加新的鼠標(biāo)模式
????初始情況下,一個(gè)圖形由兩個(gè)鼠標(biāo)模式,選擇模式和刷模式。:mouse-modes返回區(qū)別可用模式的符號(hào)列表:
> (send w :mouse-modes) (SELECTING BRUSHING) ? ? :add-mouse-mode消息添加一個(gè)新的鼠標(biāo)模式,或者重定義一個(gè)現(xiàn)有的模式。該消息需要一個(gè)參數(shù),即鼠標(biāo)模式符號(hào),還接受一些關(guān)鍵字參數(shù)。這些關(guān)鍵字包括用來(lái)指定標(biāo)簽字符串的:title,用來(lái)指定光標(biāo)符號(hào)的:cusor。:click和:motion關(guān)鍵字可以用來(lái)指定消息選擇器,當(dāng)圖形在新的模式里的時(shí)候該消息選擇器用來(lái)處理點(diǎn)擊和動(dòng)作事件。? ? 舉個(gè)例子,我們可以向圖形里添加一個(gè)新的模式,該圖形僅展示在窗體上點(diǎn)擊的鼠標(biāo)的坐標(biāo)。該模式可以這樣添加:
> (send w :add-mouse-mode 'show-coordinates:title "Show Coordinates":click :do-show-coordinates:cursor 'finger) SHOW-COORDINATES 添加了這個(gè)模式之后,該圖形就有3個(gè)可用的模式了: > (send w :mouse-modes) (SELECTING BRUSHING SHOW-COORDINATES) 當(dāng)圖形處于show-coordinate模式的時(shí)候,光標(biāo)設(shè)成了finger(手指形)。當(dāng)在該模式里點(diǎn)擊鼠標(biāo)時(shí),圖形將向:do-click消息發(fā)送:do-show-coordinates消息,該消息帶4個(gè)參數(shù)。因?yàn)闆]有指定動(dòng)作,動(dòng)作事件將被忽略。? ? 在使用該新模式之前,我們需要定義:do-show-coordinates方法。為了避免重畫圖形,我們可以使用XOR繪圖,在按鈕按下的時(shí)候繪制一個(gè)表示點(diǎn)擊位置的字符串。當(dāng)按鈕釋放的時(shí)候,我們可以再次繪制字符串以移除字符串映像:
> (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動(dòng)作僅僅是等待鼠標(biāo)按鈕彈起。? ? 現(xiàn)在我們使用圖形菜單提供的對(duì)話框切換到新的模式,或者向圖形發(fā)送一個(gè)參數(shù)為模型符號(hào)的:mouse-mode消息,向圖形發(fā)送一個(gè)不帶參數(shù)的:mouse-mode消息將返回當(dāng)前模式的符號(hào):
> (send w :mouse-mode 'show-coordinates) SHOW-COORDINATES > (send w :mouse-mode) SHOW-COORDINATES? ? 我們可以展示縮放后的坐標(biāo)系統(tǒng)里的當(dāng)前變量的當(dāng)前坐標(biāo),而不是展示鼠標(biāo)點(diǎn)擊處的圖上坐標(biāo)。:canvas-to-scaled消息帶兩個(gè)整型參數(shù)——代表畫布上的一個(gè)點(diǎn),返回兩個(gè)實(shí)數(shù)——代表縮放后坐標(biāo)系統(tǒng)的兩個(gè)當(dāng)前變量的對(duì)應(yīng)的點(diǎn):
> (send w :canvas-to-scaled 100 150) (-0.4 -0.3904382470119522) :canvas-to-real消息試圖轉(zhuǎn)換回是坐標(biāo)系,但是它僅在圖形變換為nil時(shí)才合適地工作。為了允許你在顯示畫布、縮放和真實(shí)坐標(biāo)系,我們可以定義一個(gè):do-show-coordinates消息來(lái)檢查修飾符,在點(diǎn)擊時(shí)同時(shí)按下shift時(shí)顯示實(shí)坐標(biāo),在alt使用時(shí)顯示縮放坐標(biāo)。如果沒有按下這些擴(kuò)展修飾符,它僅顯示圖上坐標(biāo): > (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消息帶兩個(gè)實(shí)數(shù)參數(shù),表示縮放坐標(biāo)或?qū)崝?shù)坐標(biāo)系中當(dāng)前變量的坐標(biāo),然后返回響應(yīng)畫布坐標(biāo)的列表。? ? 為了助于開發(fā)新的鼠標(biāo)模式,一些其它的消息也是可用的。為了說(shuō)明這些消息中的一些,我們可以構(gòu)造一些鼠標(biāo)模式,用于當(dāng)鼠標(biāo)在某點(diǎn)附近點(diǎn)擊和按下的時(shí)候通過(guò)在附近放置一個(gè)標(biāo)簽來(lái)標(biāo)示該點(diǎn)。對(duì)于使所有高亮的或選中的點(diǎn)顯示它們的標(biāo)簽這一標(biāo)準(zhǔn)選項(xiàng),我們提供的方法是對(duì)其的替代物。首先,我們可以通過(guò)使用以下表達(dá)式移除定義的模式:
(send w :delete-mouse-mode 'show-coordinates) 該操作不會(huì)移除與模式有聯(lián)系的鼠標(biāo)點(diǎn)擊或者動(dòng)作消息。該消息需要每個(gè)圖形至少有一個(gè)鼠標(biāo)模式。其次,為了識(shí)別各點(diǎn),我們可以制定一個(gè)新模式: (send w :add-mouse-mode 'identify:title "Identify":click :do-identify:cursor 'finger) 為了確定一次點(diǎn)擊足夠接近一個(gè)點(diǎn),新模式的點(diǎn)擊方法需要設(shè)置一個(gè)容差值。每個(gè)圖形都保持一個(gè)容差值范圍,該范圍可以通過(guò):click-range消息設(shè)置或獲取。該消息返回一個(gè)兩個(gè)整型數(shù)的列表,用像素表示的該容差值的寬度和高度。默認(rèn)范圍如下: > (send w :click-range) (4 4) 該范圍被標(biāo)準(zhǔn)selection模式采用,當(dāng)鼠標(biāo)點(diǎn)擊的時(shí)候用來(lái)確定選擇的點(diǎn)。可以通過(guò)發(fā)送帶兩個(gè)整型參數(shù)的消息來(lái)指定一個(gè)新的點(diǎn)擊范圍,這兩個(gè)整型參數(shù)是新范圍的寬度和高度。? ? 使用范圍和鼠標(biāo)點(diǎn)擊位置的坐標(biāo),我們可以下個(gè)圖形發(fā)送:points-in-rect消息來(lái)獲取落在指定矩形區(qū)域內(nèi)的影響的所有點(diǎn)的索引列表。例如:
(send w :points-in-rect 100 150 10 15) 然后,返回位于左上角坐標(biāo)為(100, 150),寬10像素,高15像素的矩形區(qū)域內(nèi)的影像的所有點(diǎn)的索引列表。如果該該區(qū)域內(nèi)沒有點(diǎn)數(shù)據(jù),返回nil。? ? 使用這兩個(gè)詳細(xì),我們的: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消息使用中心點(diǎn)為鼠標(biāo)點(diǎn)擊處的矩形,這里的點(diǎn)擊位置的寬度和高度與點(diǎn)擊范圍內(nèi)指定的值相等。函數(shù)first返回由:points-in-rect消息返回的列表的第一個(gè)索引,如果列表為空則返回nil。因此,如果在點(diǎn)擊處附近的發(fā)現(xiàn)一個(gè)點(diǎn),變量p是一個(gè)整數(shù),如果沒有這樣的一個(gè)點(diǎn),變量p就是nil。
? ? 為了說(shuō)明另一個(gè)消息:drag-point,我們可以通過(guò)在圖形里移動(dòng)數(shù)據(jù)點(diǎn)來(lái)定義一個(gè)鼠標(biāo)模式,用來(lái)編輯我們的數(shù)據(jù)。我們可以這樣設(shè)置新的模式:
(send w :add-mouse-mode 'point-moving:title "Point Moving":cursor 'hand:click :do-point-moving) 在圖形里,:drag-point消息用來(lái)拖動(dòng)一個(gè)點(diǎn),該消息帶兩個(gè)參數(shù),鼠標(biāo)點(diǎn)擊的圖上坐標(biāo)和檢測(cè)某點(diǎn)是否接近點(diǎn)擊位置。如果存在這樣的點(diǎn),將在窗體周圍拖拽出一個(gè)灰色的長(zhǎng)方形直到鼠標(biāo)按鍵釋放。在此期間,該點(diǎn)的坐標(biāo)將改變同時(shí)圖形重畫,除非使用了一個(gè)值為nil的:draw關(guān)鍵字。新的坐標(biāo)將使用上邊提到的變換消息來(lái)計(jì)算。對(duì)于一個(gè)轉(zhuǎn)換后的圖形,;drag-point消息試圖移動(dòng)與當(dāng)前變量相關(guān)的平面上的點(diǎn),并保持正交分量不變。如果變換是正交的,這才會(huì)正確地起作用。:drag-point消息的方法返回被拖動(dòng)的點(diǎn)的索引,如果沒有這樣的足夠接近點(diǎn)擊處的點(diǎn)將返回nil。使用:drag-point,我們可以這樣為:do-point-moving消息定義一個(gè)方法: (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)))) 練習(xí) 9.6略。
標(biāo)準(zhǔn)鼠標(biāo)模式
????初始情況下,由graph-proto原型構(gòu)造的圖形包括兩個(gè)模式:selecting模式和brushing模式。selecting模式使用arrow光標(biāo),僅需要一個(gè)點(diǎn)擊方法:do-select-click;brushing模式使用brush光標(biāo),它的點(diǎn)擊消息和動(dòng)作消息是:do-brush-click和:do-brush-motion。
? ? 針對(duì)selecting和brushing模式的點(diǎn)擊和動(dòng)作手勢(shì)是被用來(lái)設(shè)計(jì)基本的用戶接口以針對(duì)這些鼠標(biāo)模式。實(shí)際的選擇和高亮操作是通過(guò)兩個(gè)其它消息實(shí)現(xiàn)的,:unselect-all-points和:adjust-points-in-rect。:unselect-all-points消息不帶參數(shù)。:adjust-points-in-rect消息需要5個(gè)參數(shù),前4個(gè)參數(shù)矩形的整型坐標(biāo)值,左上角的兩個(gè)坐標(biāo)值,還有矩形的寬和高。第5個(gè)參數(shù)是點(diǎn)狀態(tài)符號(hào)selected或hilited中的的一個(gè)。當(dāng)發(fā)生一個(gè)點(diǎn)擊事件時(shí),該圖形處于selecting模式,:do-select-click方法以下邊的方式使用這些消息:
- 如果點(diǎn)擊操作不包括擴(kuò)展修飾器(即shift或alt鍵),:unselect-all-points消息發(fā)向任意當(dāng)前選中點(diǎn)集中未被選中的點(diǎn)。
- 然后,:adjust-points-in-rect消息被發(fā)送,其作用范圍是以點(diǎn)擊點(diǎn)為中心的,由當(dāng)前點(diǎn)擊范圍指定的寬和高的矩形區(qū)域的坐標(biāo)范圍。第5個(gè)參數(shù)是selected。這個(gè)消息的方法應(yīng)該會(huì)選擇指定矩形范圍內(nèi)的所有可視點(diǎn)。
- 當(dāng)鼠標(biāo)按鍵按下時(shí),一個(gè)角固定在鼠標(biāo)點(diǎn)擊處的虛線矩形將拖拽出來(lái)并覆蓋圖形。當(dāng)鼠標(biāo)按鍵釋放時(shí),一個(gè)帶有最終矩形坐標(biāo)和符號(hào)selected作為參數(shù)的:adjust-points-in-rect消息將被發(fā)送出去。
? ? 當(dāng)圖形處于brushing模式時(shí),:do-brush-click方法首先取消對(duì)所有點(diǎn)的選擇操作,除非給定了擴(kuò)展修飾符;然后,該方法將發(fā)送一個(gè)以當(dāng)前刷坐標(biāo)和符號(hào)'selected為參數(shù)的:adjust-points-in-rect消息。每次鼠標(biāo)按下并移動(dòng)的時(shí)候,它都會(huì)持續(xù)發(fā)送該消息。:do-brush-motion方法將發(fā)送參數(shù)為當(dāng)前刷坐標(biāo)和符號(hào)hilited為參數(shù)的消息。當(dāng)?shù)?個(gè)參數(shù)是hilited時(shí),該消息的方法有望高亮矩形區(qū)域內(nèi)所有可見的點(diǎn),然后將矩形外高亮的點(diǎn)取消高亮效果。
? ? 當(dāng)前刷的位置和大小可以使用:brush消息來(lái)獲取。刷由4個(gè)整數(shù)指定:右下角的畫布坐標(biāo)x、y,連接鼠標(biāo)的角,還有刷的寬度和高度。通過(guò)發(fā)送一個(gè)帶四個(gè)整數(shù)參數(shù)(即新坐標(biāo))的:brush消息,可以指定新刷的坐標(biāo);:resize-brush消息提供了一個(gè)對(duì)話框來(lái)交互地設(shè)置新刷的大小,該消息可以通過(guò)使用圖形菜單里的Resize Brush菜單項(xiàng)來(lái)發(fā)送給該圖形。
? ? 使用:unselect-all-points和:adjust-points-in-rectx消息的協(xié)議允許開發(fā)新的圖形,而不用修改刷模式或選擇模式方法自身。例如,在彩色顯示器的系統(tǒng)上,我們可以為這兩個(gè)消息修改這些方法,以確保選中的點(diǎn)著紅色,未選中的點(diǎn)著繪圖色。在發(fā)送下邊這條消息以確保使用彩色繪圖之后,
(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)) 該方法將所有點(diǎn)的顏色設(shè)置為nil,然后調(diào)用從graph-proto原型繼承來(lái)的方法。新的: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包含指定的矩形區(qū)域內(nèi)的點(diǎn)的索引列表,如果該列表不是nil的則金發(fā)送:point-color消息。
9.1.5 連接
????圖形里的點(diǎn)可能處于一些不同的狀態(tài),selecting和brushing操作提供了一種容易的方式來(lái)改變圖形里點(diǎn)的狀態(tài)。為了瀏覽數(shù)據(jù)集的一些視圖之間的關(guān)系,Lisp-Stat繪圖系統(tǒng)允許圖形之間的互連操作。當(dāng)兩個(gè)或更多圖形連接到一起時(shí),系統(tǒng)將試圖保證連接在一起的圖形里的點(diǎn)狀態(tài)時(shí)相同的。
? ? 默認(rèn)的Lisp-Stat連接系統(tǒng)基于一個(gè)松散的聯(lián)合模型,在這個(gè)模型里,被連接到一起的不同圖形的點(diǎn)通過(guò)他們的索引值彼此關(guān)聯(lián)。因此,在一個(gè)圖形里選擇索引值為3的點(diǎn),同時(shí)也會(huì)選中與該圖形連接的圖形里索引值同為3的點(diǎn)。在不存在點(diǎn)的其它屬性匹配的意義下,這個(gè)聯(lián)合是松散的。在連接的圖形中的點(diǎn)可以有不同的標(biāo)簽、顏色或者符號(hào)。其它的連接模式也是可能的,其中一個(gè)可替換的連接模式將在10.6節(jié)里說(shuō)明。
確定哪些圖形可以連接
????確定哪些圖形可以連接到其它圖形上的系統(tǒng)依賴:linked和:links兩個(gè)消息。當(dāng)向圖形發(fā)送:links消息時(shí),如果該圖形沒有被連接則返回nil;如果該圖形被連接了,它應(yīng)該返回一個(gè)列表,該類表包含所有連接到該圖形的接收了該消息的其它圖形,圖形自身可能是該列表的一個(gè)元素。:linked消息可以以不帶參數(shù)的形式發(fā)送,用來(lái)確定該圖形是否已經(jīng)被連接,如果已經(jīng)被連接了,返回t;否則,返回nil。該消息可以接受一個(gè)可選參數(shù),該參數(shù)為t就是連接該圖形,該參數(shù)為nil就是解開連接。
? ? graph-proto原型的:links和linked方法將維護(hù)一個(gè)包含已連接圖形的單列表,該列表由被連接的圖形的:links消息返回,該列表也可以通過(guò)使用linked-plots函數(shù)好獲取,當(dāng)某個(gè)圖形被連接時(shí),該圖形的點(diǎn)的狀態(tài)傳遞到其它被連接的圖形,就像由:links消息返回的結(jié)果確定的一樣。
? ? 因?yàn)楸贿B接圖形的確認(rèn)完全基于這兩個(gè)消息,定義一個(gè)可代替的策略是相當(dāng)簡(jiǎn)單的。例如,如果我們想確保所有與stack loss data相關(guān)的圖形都與另一個(gè)數(shù)據(jù)相連而不是與所有的圖形都相連,我們可以這樣設(shè)置一個(gè)stack loss圖形的列表:
(setf *stack-plots* nil) 然后,給予每個(gè)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))) 避免為每個(gè)新圖形添加這些方法的一個(gè)方式是定義一個(gè)獨(dú)立的stack loss圖形原型。
點(diǎn)狀態(tài)和連接
????圖形里的每個(gè)點(diǎn)都是四種狀態(tài)中的一種,對(duì)應(yīng)的符號(hào)為invisible, normal, hilited和selected。graph-proto繪圖方法可以繪制高亮的點(diǎn)和由點(diǎn)的符號(hào)的高亮版本選中的點(diǎn)。處于正常狀態(tài)的點(diǎn)使用正常符號(hào)來(lái)繪制,處于invisible狀態(tài)的點(diǎn)不被繪制。點(diǎn)的狀態(tài)可以通過(guò):point-state消息來(lái)確定和繪制。對(duì)于處于stack loss數(shù)據(jù)圖形里的第一個(gè)點(diǎn):
> (send w :point-state 0) NORMAL :point-state消息的方法是矢量化的,所以它可以用來(lái)確定圖形里的所有點(diǎn)的狀態(tài),方法如下: > (send w :point-state (iseq 21)) (NORMAL NORMAL NORMAL NORMAL ...) ? ? 為了改變點(diǎn)的狀態(tài),可以向:point-state消息的第二個(gè)參數(shù)賦值為一個(gè)新狀態(tài),下邊的表達(dá)式將第一個(gè)點(diǎn)的狀態(tài)改為selected。當(dāng)圖形里的一個(gè)點(diǎn)的狀態(tài)發(fā)生了改變,在所有被連接的圖形了對(duì)應(yīng)的點(diǎn)的狀態(tài)也會(huì)發(fā)生改變。另外,會(huì)向每個(gè)圖形發(fā)送一個(gè)帶一個(gè)參數(shù)的:adjust-screen-point方法,該參數(shù)是想要改變的那個(gè)點(diǎn)的索引值。該方法負(fù)責(zé)改變屏幕上點(diǎn)的影像。
? ? :adjust-screen-point方法采取的動(dòng)作即取決于改點(diǎn)的新狀態(tài)也取決于改點(diǎn)的上一個(gè)狀態(tài)。當(dāng)發(fā)送:adjust-screen-point消息的時(shí)候,:point-state消息返回的值將表達(dá)新的狀態(tài),點(diǎn)的上一個(gè)狀態(tài)可以通過(guò)向:last-point-state消息發(fā)送一個(gè)參數(shù)來(lái)獲取,該參數(shù)是改點(diǎn)的索引值。
? ? 為了在所有連接在一起的圖形該處最佳的對(duì)應(yīng)關(guān)系,:adjust-screen-ponit方法試圖盡可能快地重畫點(diǎn)數(shù)據(jù),以表達(dá)它們之間的任何狀態(tài)變化。對(duì)于有些狀態(tài)改變來(lái)說(shuō),這是相當(dāng)簡(jiǎn)單的。如果點(diǎn)的狀態(tài)在normal, hilited和selected這些值之間變化的話,那么:adjust-screen-point方法將使用上一章引入的:replace-symbol方法來(lái)改變屏幕上顯示的符號(hào)。不幸的是,如果點(diǎn)的新狀態(tài)是不可見的,不重畫圖形而從屏幕上清晰地移除改點(diǎn)是很困難的。由于每次將一點(diǎn)設(shè)置成不可見狀態(tài)都要進(jìn)行重畫操作是很浪費(fèi)的,所以:adjust-screen-point方法采取了一個(gè)間接的方法:它向圖形發(fā)送一個(gè)標(biāo)識(shí)位來(lái)指示是否需要調(diào)整以適合窗體,:needs-adjusting消息用來(lái)設(shè)置和獲取這個(gè)標(biāo)識(shí)位的值。在將一個(gè)點(diǎn)的狀態(tài)設(shè)置為不可見后,該標(biāo)識(shí)位的值為t:
> (send w :needs-adjusting) NIL> (send w :point-state 0 'invisible) INVISIBLE> (send w :needs-adjusting) T 在方便的時(shí)候,也可以向圖形發(fā)送:adjust-screen消息,如果需要的話,該消息的方法將檢查該標(biāo)志位、重設(shè)它,然后重畫圖形。因此,在將一個(gè)點(diǎn)的狀態(tài)設(shè)置為不可見之后,在對(duì)所有需要改變狀態(tài)的點(diǎn)進(jìn)行調(diào)整之后,:adjust-points-in-rect方法將向所有連接的圖形發(fā)送:adjust-screen消息。? ? 作為這些想法的一個(gè)簡(jiǎn)單的展示,我們可以為我們的圖形定義一個(gè)新的:adjust-screen-point方法,該方法將所有高亮的點(diǎn)和選中的點(diǎn)設(shè)置為紅色,然后將所有未高亮的點(diǎn)設(shè)置為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)) 該方法與早先我們使用的那個(gè)為:adjust-points-in-rect方法修改的方法有些不同,之前的方法,僅當(dāng)在圖形自身內(nèi)部的點(diǎn)的狀態(tài)被刷模式或選擇模式改變的時(shí)候才會(huì)著色;而現(xiàn)在的方法可以確保即使是在被連接的圖形里由刷模式或選擇模式改變狀態(tài)的點(diǎn),其顏色也會(huì)調(diào)整。 練習(xí) 9.7
略。
高級(jí)狀態(tài)消息
????對(duì)于獲取和改變點(diǎn)的狀態(tài),還有一些其它消息是可用的。:point-showing, :point-hilited和:point-selected消息需要一個(gè)點(diǎn)的索引做參數(shù),然后返回t或nil來(lái)分別指示點(diǎn)的狀態(tài)是否是可見的、高亮的或被選中的;對(duì)這些消息的第二個(gè)參數(shù)賦值為t或nil,將會(huì)對(duì)其狀態(tài)做合適的改變。這3個(gè)方法都是矢量化的,這3個(gè)方法在返回之前都會(huì)發(fā)送:adjust-screen消息。
? ? :selection消息返回當(dāng)前被選中的所有點(diǎn)的列表,如果向該消息傳遞一個(gè)索引列表作為它的可選參數(shù)的話,該消息將取消所有選中的點(diǎn),然后選中參數(shù)列表里指定的所有可見的點(diǎn),他在返回前將發(fā)送:adjust-screen消息。:points-selected消息的方法與:selection方法是等效的;:points-hilited和:points-showing的方法也是相同的。
? ? :erase-selection方法將所有當(dāng)前選中的點(diǎn)的狀態(tài)改變成不可見狀態(tài),:show-all-points方法將所有點(diǎn)的狀態(tài)改為正常狀態(tài);:focus-on-selection方法將所有未選中的方法設(shè)置為不可見狀態(tài)。這3個(gè)方法在返回之前都發(fā)送:adjust-screen消息。
? ? 這里有兩個(gè)有用的謂詞消息是可用的。:any-points-selected-p消息的方法在圖形里任何一個(gè)點(diǎn)被選中時(shí)都返回t,否則返回nil。:all-points-showing-p消息在所有點(diǎn)都是可見狀態(tài)小返回t,任意一個(gè)點(diǎn)為不可見狀態(tài)則返回nil。
9.1.6 窗體布局、縮放和重畫
縮放方法
????graph-proto原型的:resize方法基于一定的假設(shè),該假設(shè)是關(guān)于圖形的布局的。圖形的內(nèi)容,點(diǎn)數(shù)據(jù)與線數(shù)據(jù)的影像被限制在一個(gè)叫做內(nèi)容矩形的矩形里。如果圖形里包含坐標(biāo)軸,它們將立即在內(nèi)容矩形的外部被畫出來(lái)。由內(nèi)容和坐標(biāo)軸組成的整個(gè)圖形被一個(gè)邊緣包裹起來(lái),這個(gè)邊緣被用來(lái)束縛圖形,像標(biāo)準(zhǔn)旋轉(zhuǎn)圖形的旋轉(zhuǎn)控制,這個(gè)邊緣沿著窗體的畫布的左側(cè)、上側(cè)、右側(cè)和下側(cè)都有一個(gè)固定的像素?cái)?shù)的留白。當(dāng)該圖形變換大小的時(shí)候,:resize消息會(huì)保持邊緣不變,然后內(nèi)容矩形的大小以填充畫布的剩余部分。內(nèi)容矩形的精確的大小和形狀依賴于該空間是否需要x和y坐標(biāo)軸,坐標(biāo)軸是否擁有標(biāo)簽,內(nèi)容矩形是否保持固定的方位比例。對(duì)于一個(gè)固定的方位比例,內(nèi)容多邊形是這樣的一個(gè)最大矩形,它是能裝下邊緣的并為坐標(biāo)軸留下空間的那個(gè)最大正方形。如果該圖形允許方位比例改變,內(nèi)容多邊形將是滿足這個(gè)限制的最大的矩形。圖9.3針對(duì)一個(gè)帶有坐標(biāo)軸和固定方位比例的圖形的邊緣和內(nèi)容矩形。
圖9.3?帶有坐標(biāo)軸和固定方位比例的圖形的邊緣和內(nèi)容矩形
內(nèi)容矩形可以通過(guò)使用:content-rect消息來(lái)設(shè)置和獲取。不適用參數(shù)的話,該消息將返回內(nèi)容矩形的坐標(biāo)的列表(比如說(shuō)一個(gè)包含左上角兩個(gè)坐標(biāo)值、寬度和高度的列表):
> (send w :content-rect) (0 0 250 251) 如果該消息使用四個(gè)參數(shù)來(lái)調(diào)用的話,它將會(huì)把內(nèi)容矩形設(shè)置成指定的坐標(biāo)。? ? 圖形的邊緣可以使用:margin消息來(lái)設(shè)置和獲取。不適用參數(shù)調(diào)用,該消息返回一個(gè)四個(gè)整型數(shù)表示的列表,代表邊緣距離畫布的上側(cè)、頂側(cè)、右側(cè)和下側(cè)的距離的像素值。初始情況下,一個(gè)從graph-proto原型創(chuàng)建的圖形的邊緣,其值都是0:
> (send w :margin) (0 0 0 0) 為了設(shè)置一個(gè)新的邊緣,可以給:margin消息賦予4個(gè)參數(shù)。? ? 圖形的方位比例可以使用:fixed-aspect消息確定和設(shè)置,對(duì)于固定的方位比例該結(jié)果是一個(gè)非空值,對(duì)于一個(gè)可變的方位比例該結(jié)果返回nil。對(duì)于我們的例子,方位比例初始情況下是可變的:
> (send w :fixed-aspect) NIL 為了改變方位比例,你可以向該消息發(fā)送一個(gè)nil和一個(gè)非空的參數(shù),以下表達(dá)式給予我們的圖形一個(gè)固定的方位比例。? ? 如果使用:margin, :fixed-aspect, x-axis 或 :y-axis消息,布局特征的任意一個(gè)發(fā)生改變的話,那么這些消息的方法將向圖形發(fā)送:resize和:redraw消息,除非:draw關(guān)鍵字被賦予了值為nil的參數(shù)。
? ? 在返回之前,:resize方法將向圖形發(fā)送:redraw-overlays消息,這里的疊置(Overlays)將在9.1.7節(jié)里描述,它提供方便的方法來(lái)實(shí)現(xiàn)圖形控制,比如對(duì)旋轉(zhuǎn)圖形的旋轉(zhuǎn)控制。
重畫方法
????繪圖原型的重畫方法:redraw將重畫過(guò)程打斷成幾個(gè)階段。首先,它發(fā)送:redraw-backgroud消息,該消息的方法將擦除畫布,然后當(dāng)前圖形設(shè)置需要的所有坐標(biāo)軸。下一步,:redraw消息向圖形發(fā)送:redraw-overlays消息。最后,向圖形發(fā)送:redraw-content消息,該消息對(duì)應(yīng)的方法負(fù)責(zé)點(diǎn)數(shù)據(jù)和線數(shù)據(jù)自身。:redraw, :redraw-content和:redraw-backgroud消息對(duì)應(yīng)的方法都使用了緩沖區(qū)。
? ? 將背景繪制與內(nèi)容繪制分離的原因是,繪制坐標(biāo)軸和疊置操作可能很耗時(shí),并且僅有內(nèi)容改變時(shí)這是不需要的。結(jié)果,像:adjust-screen和:rotate-2這樣的消息對(duì)應(yīng)的方法僅需要向圖形發(fā)送:redraw-content消息以改變圖形的內(nèi)容。
? ? 開發(fā)新圖形時(shí),很少要覆蓋:graph-proto的:redraw方法,通常寫一個(gè)新的:redraw-content方法就最夠了。
下層消息
????為了有助于繪圖方法,graph-proto原型轉(zhuǎn)換系統(tǒng)保持將待轉(zhuǎn)換的點(diǎn)數(shù)據(jù)和線數(shù)據(jù)坐標(biāo)的整型版本(即轉(zhuǎn)換后的坐標(biāo)數(shù)據(jù)為整型值)。整型數(shù)的范圍,即所謂的畫布范圍,與每個(gè)變量都有關(guān)系。該系統(tǒng)將被變換坐標(biāo)值從被縮放的比例縮放到畫布的比例。那么如果一個(gè)變量的范圍是從0到200,那么在-1.0到1.0范圍內(nèi)坐標(biāo)值為0.5的坐標(biāo)對(duì)應(yīng)的畫布坐標(biāo)是150。
? ? 變量的畫布范圍可以使用:canvas-range消息進(jìn)行獲取和設(shè)置。對(duì)于我們的事例圖形里的第一個(gè)變量,它的范圍就是:
> (send w :canvas-range 0) (0 232)對(duì)于當(dāng)前變量的第一個(gè),:resize方法將畫布范圍設(shè)置為從0到內(nèi)容矩形寬度這樣的范圍,對(duì)于所有其它變量來(lái)說(shuō),該范圍被設(shè)置到從0到內(nèi)容多邊形高度這一范圍中。
? ? 點(diǎn)數(shù)據(jù)和直線中的點(diǎn)數(shù)據(jù)的畫布坐標(biāo)可以使用:point-canvas-coordinate和:linestart-canvas-coordinate消息來(lái)獲取。這些消息可以向我們?cè)谏线吔榻B的其它的坐標(biāo)消息一樣來(lái)使用。第一個(gè)點(diǎn)的前兩個(gè)變量的畫布坐標(biāo)可以這樣獲取:
> (send w :point-canvas-coordinate 0 0) 80> (send w :point-canvas-coordinate 1 0) 58 ????繪圖系統(tǒng)還會(huì)使用一個(gè)在內(nèi)容矩形里的叫做內(nèi)容原點(diǎn)的點(diǎn),該原點(diǎn)可以使用:content-origin消息來(lái)設(shè)置和獲取: > (send w :content-origin) (9 250) :resize方法將該原點(diǎn)放置在內(nèi)容矩形的左上角稍低點(diǎn)的位置。:redraw-content方法使用畫布坐標(biāo)作為偏移量。? ? 在這個(gè)上下文里使用的一個(gè)終極消息是:content-variables,該消息對(duì)應(yīng)的方法與:current-variables方法等價(jià),除非在這樣的情況下——當(dāng)該變量變化時(shí)它沒有重畫圖形。
9.1.7 圖形疊置
????疊置可以被視為一張透明的紙,這張紙?jiān)趦?nèi)容繪制之前放置在圖形的背景上,這些紙保持一定的順序,并從下向上繪制。為了不被圖形的內(nèi)容覆蓋,疊置層通常只在圖形的邊緣里繪制。
? ? 圖形疊置的一個(gè)關(guān)鍵的特性是它們的攔截或者說(shuō)是過(guò)濾鼠標(biāo)點(diǎn)擊的能力。graph-proto原型的:do-click方法允許每個(gè)疊置層,從頂層逐層向下,來(lái)接收或者傳遞一個(gè)鼠標(biāo)點(diǎn)擊事件。如果該點(diǎn)擊事件未被任何疊置層接收,那么它僅能傳遞到當(dāng)前鼠標(biāo)模式上。該特性使使用疊置層來(lái)實(shí)現(xiàn)簡(jiǎn)單的圖形控制成為可能,比如讓某個(gè)圖形執(zhí)行特殊的動(dòng)作。對(duì)于旋轉(zhuǎn)圖形的旋轉(zhuǎn)操作就是以疊置層實(shí)現(xiàn)的。
? ? 圖形疊置層是一個(gè)對(duì)象,它繼承自graph-overlay-proto原型。通過(guò)向圖形發(fā)送帶一個(gè)參數(shù)的:add-overlay消息(該參數(shù)是疊置層對(duì)象)來(lái)向一個(gè)圖形里添加疊置層。該操作將新的疊置層置于所有其它疊置層之上;通過(guò)向圖形發(fā)送帶一個(gè)參數(shù)的:delete-overlay消息來(lái)移除疊置層(該參數(shù)是要移除的疊置層對(duì)象)。
? ? 當(dāng)圖形放縮的時(shí)候,:resize方法在返回之前向圖形發(fā)送:resize-overlays消息,該消息對(duì)應(yīng)的方法向圖形里的每個(gè)疊置層發(fā)送:resize消息。當(dāng)圖形重畫時(shí),:redraw方法向圖形發(fā)送:redraw-overlays消息,該消息對(duì)應(yīng)的方法向每個(gè)疊置層發(fā)送:redraw消息,從最底層的疊置層開始一直向上直到疊置堆棧的最頂端。
? ? graph-proto :do-click方法向疊置層發(fā)送:do-click消息,該消息帶它接受到的一些參數(shù),該過(guò)程從最上邊的疊置層開始,持續(xù)穿過(guò)其它疊置層,直到返回一個(gè)非nil值作為點(diǎn)擊的結(jié)果。如果所有的疊置層對(duì)點(diǎn)擊動(dòng)作的反應(yīng)都返回nil,那么對(duì)于當(dāng)前的鼠標(biāo)模式該click事件將被傳遞給click方法。
? ? 舉個(gè)例子,我們可以向我們的氨氣損耗數(shù)據(jù)圖形里添加一個(gè)疊置層,那個(gè)圖形包含一個(gè)按鈕的和一個(gè)跟著一個(gè)標(biāo)簽的正方形。當(dāng)在正方形里發(fā)生點(diǎn)擊事件時(shí),圖形將接受指令運(yùn)行一個(gè)平滑差值算法,該算法從溫度對(duì)氣流的圖形到氨氣損失到濃度的圖形。為了避免圖形縮放中需要重新定位按鈕,我們可以將它放置在圖形的左上角位置。
> (let ((h (+ (send w :text-ascent) (send w :text-descent))))(send w :margin 0 (round (* 1.5 h)) 0 0)) ? ? 上式設(shè)置了圖形上邊距,目的是為窗體的字體里的一行文本留下足夠的空間。通過(guò)向疊置層原型發(fā)送:new-message消息,我們可以構(gòu)造疊置層對(duì)象: > (setf interp-overlay (send graph-overlay-proto :new))接下來(lái),我們可以給予該疊置層一個(gè)槽,用來(lái)容納描述按鈕容器和標(biāo)簽字符串的位置的信息。
> (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)))))) 然后,我們可以為該槽定義一個(gè)讀取方法: > (defmeth interp-overlay :location () (slot-value 'location)) ? ? 現(xiàn)在可以定義該疊置層的:redraw方法來(lái)獲取位置信息,這里使用:location消息,然后在指定位置繪制一個(gè)盒子容器和一個(gè)標(biāo)簽: > (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))) 該方法向疊置層發(fā)送了:graph消息,目的是某圖形是否含有某疊置層。當(dāng)某疊置層安裝到一個(gè)圖形以后,該疊置層系統(tǒng)將確保該消息返回合適的結(jié)果。? ? :do-click消息將返回nil,除非點(diǎn)擊落在按鈕的盒子容器的內(nèi)部;如果點(diǎn)擊發(fā)生在盒子容器里,該方法將向圖形發(fā)送:interpolate消息然后返回t,進(jìn)一步確保點(diǎn)擊事件沒有被做進(jìn)一步處理:
> (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))) 為了完成這個(gè)例子,我們需要為我們的圖形定義一個(gè):interpolate方法。可以基于9.1.3節(jié)的平滑旋轉(zhuǎn)循環(huán),做一個(gè)簡(jiǎn)單的定義,例如:
圖 9.4 疊置層里帶差值按鈕的煙霧損失數(shù)據(jù)圖形
> (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)))) 最后,通過(guò)使用下式將疊置層安裝到我們的圖形里: > (send w :add-overlay interp-overlay) 結(jié)果見圖9.4,圖形內(nèi)容部分發(fā)生的點(diǎn)擊像以前一樣被處理了,但是在按鈕里的點(diǎn)擊引起圖形運(yùn)行了差值算法。? ? 待使用疊置層試驗(yàn)之后,我們可以使用以下表達(dá)式來(lái)移除該疊置層:
> (send w :delete-overlay interp-overlay) 練習(xí)9.8略
練習(xí)9.9
略
練習(xí)9.10
略
9.1.8 菜單和菜單項(xiàng)
????graph-proto原型的:isnew方法可以通過(guò)向圖形對(duì)象發(fā)送:new-menu消息來(lái)添加一個(gè)菜單,該消息對(duì)應(yīng)的方法按順序向?qū)ο蟀l(fā)送:menu-title和:menu-template消息。:menu-title消息返回一個(gè)標(biāo)題字符串:
> (send w :menu-title) "Plot" 在構(gòu)建新的圖形菜單的時(shí)候,該消息可以用來(lái)安裝一個(gè)新的菜單標(biāo)題,在給予一個(gè)新的原型一個(gè)更合適的菜單標(biāo)題上,這是非常有用的。? ? :menu-template方法預(yù)期會(huì)返回一個(gè)列表,該列表用來(lái)構(gòu)建菜單項(xiàng),該列表可以包含菜單項(xiàng)對(duì)象,這些菜單項(xiàng)對(duì)象可以簡(jiǎn)單地安裝在菜單里,或者也可能包含一些用來(lái)指定標(biāo)準(zhǔn)菜單項(xiàng)的符號(hào)。該消息的graph-proto原型對(duì)應(yīng)的方法返回一個(gè)符號(hào)列表:
> (send w :menu-templates) (LINK SHOWING-LABELS MOUSE REIZE-BRUSH ...)- :new-ment方法為這些符號(hào)里的每個(gè)符號(hào)都構(gòu)造了一個(gè)合適的菜單。下邊的符號(hào)可以用來(lái)指定標(biāo)準(zhǔn)菜單項(xiàng):
- color——發(fā)送:set-sselection-color消息,該消息會(huì)顯示一個(gè)對(duì)話框,用來(lái)改變選中的點(diǎn)的顏色。
- dash——一個(gè)處于不可用狀態(tài)的分隔符。
- focus-on-selection——發(fā)送:focus-on-selection消息。
- link——使用:linked消息切換圖形的鏈接。
- mouse——發(fā)送:choose-mouse-mode消息,該消息顯示一個(gè)對(duì)話框,用來(lái)改變鼠標(biāo)模式。
- option——向圖形發(fā)送:set-options消息,該消息顯示一個(gè)對(duì)話框,用來(lái)設(shè)置一些選項(xiàng)。
- redraw——發(fā)送:redraw消息。
- erase-selection——發(fā)送:erase-selection消息。
- rescale——發(fā)送:adjust-to-data消息。
- save-image——發(fā)送:ask-save-image消息,該消息顯示一個(gè)對(duì)話框,用來(lái)將圖像保存成文件。
- selection——發(fā)送:selection-dialog消息。
- show-all——發(fā)送:show-all-points消息。
- show-labels——使用:showing-labels消息切換顯示標(biāo)簽。
- symbol——發(fā)送:set-selection-symbol消息,該消息顯示一個(gè)對(duì)話框,用來(lái)改變選中的點(diǎn)的符號(hào)。
這些菜單項(xiàng)的一些使用:any-points-selected-p或者:all-points-showing-p來(lái)確定他們是否處于可用狀態(tài)(使能)。
? ? :new-menu消息還可以被賦予一個(gè)可替代的標(biāo)題字符串參數(shù),該參數(shù)用來(lái)代替:menu-title消息的結(jié)果。菜單項(xiàng)的可選列表可以通過(guò)使用:items關(guān)鍵字來(lái)使用,該列表可以包含上邊列出的符號(hào)中的任意多個(gè),目的是指定標(biāo)準(zhǔn)菜單項(xiàng)。例如:
> (send w :new-menu "Stack Loss" :items '(link dash rescale)) 該表達(dá)式賦予stack loss圖形一個(gè)菜單,它僅包括link項(xiàng)和rescale項(xiàng),中間用一條直線分開。可替代的方法就是去定義一個(gè)新的:menu-template方法,然后發(fā)送:new-menu不帶:items關(guān)鍵字的消息給它。對(duì)于開發(fā)一個(gè)新的原型是很有用的。9.1.9 雜項(xiàng)消息
????還有一些其它消息在graph-proto原型里是可用的。:showing-labels消息用于設(shè)置和獲取標(biāo)簽選項(xiàng)的狀態(tài),如果該值為非nil值,繪圖方法將把它放置于高點(diǎn)的選中的點(diǎn)數(shù)據(jù)附近。
? ? 有些方法是專為2維圖形設(shè)計(jì)的。add-function消息接受一個(gè)帶一個(gè)實(shí)數(shù)參數(shù)的函數(shù),代表一個(gè)區(qū)間的高低界限的兩個(gè)實(shí)數(shù),還有一個(gè)指定一定數(shù)量點(diǎn)數(shù)據(jù)的關(guān)鍵字參數(shù)。該消息對(duì)應(yīng)的方法構(gòu)造了一個(gè)網(wǎng)格,并在該網(wǎng)格上計(jì)算函數(shù),然后將對(duì)應(yīng)的線集合添加到圖形上。它也接受關(guān)鍵字參數(shù):color和:type,用來(lái)指定顏色和線型。該圖形將被設(shè)置:redraw-content消息,除非使用了參數(shù)為nil的:draw關(guān)鍵字。
? ? 對(duì)于線性函數(shù),:abline消息帶兩個(gè)參數(shù),一個(gè)截矩和一個(gè)斜率,然后將該直線加到圖形上。該獨(dú)立變量的范圍就是x軸的當(dāng)前范圍。
? ? :adjust-depth-cuing消息主要由旋轉(zhuǎn)圖形來(lái)使用,但是它也可以被其它圖形使用,它將整數(shù)維的索引作為參數(shù),對(duì)于那些維度使用畫布坐標(biāo)(它們被視為超出屏幕的坐標(biāo)),目的是為圖形里的點(diǎn)數(shù)據(jù)和線數(shù)據(jù)使用深度標(biāo)識(shí)。通過(guò)將點(diǎn)數(shù)據(jù)的符號(hào)改變?yōu)榉?hào)dot1,dot2,dot3和dot4中的一個(gè),該點(diǎn)數(shù)據(jù)是深度標(biāo)識(shí)的。符號(hào)dot1是為離視點(diǎn)最遠(yuǎn)的點(diǎn)使用的,dot4用于里視點(diǎn)最近的點(diǎn)。通過(guò)改變線的寬度也可以達(dá)到顯得深度標(biāo)識(shí)效果。
9.2 一些標(biāo)準(zhǔn)繪圖原型
????第二章里介紹的那5個(gè)標(biāo)準(zhǔn)圖形類型,它們是使用從graph-proto原型繼承來(lái)的原型構(gòu)造的。盡管這些圖形在外觀上相差很大,但是它們僅需要向graph-proto提供的原型,覆蓋和增加少量的方法。
9.2.1 散點(diǎn)圖
????基礎(chǔ)散點(diǎn)圖原型scatterplot-proto用于構(gòu)造一類圖形,即由plot-points和plot-lines函數(shù)構(gòu)造的圖形。它至少需要二個(gè)維度的數(shù)據(jù)。針對(duì):add-points和:add-lines消息的二維散點(diǎn)圖,允許這樣指定新數(shù)據(jù)——使用兩個(gè)分離的列表代替所需的列表的列表。那樣的話,如果s是一個(gè)二維散點(diǎn)圖,x和y是等長(zhǎng)度的數(shù)值型列表,那么下邊的兩個(gè)語(yǔ)句是等價(jià)的:
> (send s :add-points x y) > (send s :add-points (list x y)) ? ? 如果圖形的尺度類型是nil的話,:adjust-to-data方法向圖形里添加一個(gè)坐標(biāo)軸,它還會(huì)將坐標(biāo)范圍設(shè)置到坐標(biāo)軸上產(chǎn)生好看的坐標(biāo)刻度。9.2.2 散點(diǎn)圖矩陣
????散點(diǎn)圖矩陣原型scatmat-proto至少需要2個(gè)維度的數(shù)據(jù),它使用一個(gè)固定的長(zhǎng)寬比例,它的內(nèi)容的原點(diǎn)坐標(biāo)在內(nèi)容矩形的左下角,每個(gè)變量在畫布上的范圍設(shè)置在一定范圍內(nèi)——子圖的列的左右范圍,從原點(diǎn)開始量度。通過(guò)修改:do-click和:do-motion方法,將內(nèi)容變量設(shè)置為包含光標(biāo)的那個(gè)子圖的索引上,這個(gè)操作允許很多繼承它的方法可以不經(jīng)修改就正常工作。
? ? 與:add-lines, :add-points, :adjust-screen-point和:redraw-content消息相對(duì)的方法,通過(guò)修改它們可以讓矩陣?yán)锏乃凶訄D形合適地繪制。如果圖形的尺度類型是nil,默認(rèn)地,:redraw-content方法還可以在對(duì)角圖里繪制變量標(biāo)簽和界限。
9.2.3 旋轉(zhuǎn)圖形
????由spin-plot函數(shù)產(chǎn)生的,用來(lái)旋轉(zhuǎn)圖形的原型就是spin-proto,該原型至少需要三個(gè)維度的數(shù)據(jù),它使用一個(gè)固定的橫寬比數(shù)據(jù),其默認(rèn)的尺度類型是variable。:content-variables方法使用3個(gè)內(nèi)容變量索引,前兩個(gè)以常規(guī)方式使用,第三個(gè)變量是深度提示變量的索引。:resize方法將內(nèi)容的原點(diǎn)放置于內(nèi)容矩形的中間。
? ? :redraw-content方法在進(jìn)行標(biāo)準(zhǔn)內(nèi)容重畫之前調(diào)整深度提示。如果圖形要顯示坐標(biāo)軸,坐標(biāo)軸可以通過(guò)調(diào)用:redraw-content方法來(lái)重畫,該方法可以通過(guò)向圖形對(duì)象發(fā)送:draw-axes消息來(lái)構(gòu)造。如果尺度類型為nil,:adjust-to-data方法將調(diào)整圖形的范圍,以確保所有的點(diǎn)在圍繞原點(diǎn)所做的旋轉(zhuǎn)過(guò)程中均是可見的。:depth-cuing和:showing-axes消息可以用來(lái)確定一個(gè)圖形時(shí)使用深度提示還是顯示坐標(biāo)軸。通過(guò)發(fā)送一個(gè)值為nil或非nil的參數(shù),這兩個(gè)消息可用來(lái)圖形對(duì)應(yīng)的狀態(tài)。
? ? 旋轉(zhuǎn)圖形下邊的控件可以以一個(gè)疊置層的形式實(shí)現(xiàn),該疊置層由:isnew方法加入。這些按鈕將旋轉(zhuǎn)的類型設(shè)置為pitching、rolling或yawing,調(diào)整角度符號(hào),然后在鼠標(biāo)按下時(shí)發(fā)送:rotate消息,:do-idle方法也發(fā)送該消息。旋轉(zhuǎn)圖形里的當(dāng)前角度可以通過(guò)使用:angle消息來(lái)設(shè)置和獲取。角度以度計(jì)量。旋轉(zhuǎn)類型可以通過(guò)使用:rotation-type消息設(shè)置和獲取,它的值可能是符號(hào)pitching, rolling或者yawing中的一個(gè),也可能是一個(gè)以漸進(jìn)式旋轉(zhuǎn)來(lái)使用的旋轉(zhuǎn)矩陣。如果旋轉(zhuǎn)類型是一個(gè)符號(hào),:rotate方法使用:rotate-2方法(使用合適的變量和角度作為其參數(shù));如果該類型是一個(gè)矩陣,:rotate使用:apply-transformation。
? ? 除了添加控件疊置層,針對(duì)該原型:isnew方法將圖形的橫寬比設(shè)置為固定值,并為疊置層設(shè)置一個(gè)合適的邊緣。
? ? 針對(duì)旋轉(zhuǎn)圖形原型的:add-function方法是為三維圖形設(shè)計(jì)的,它需要一些參數(shù):一個(gè)兩個(gè)參數(shù)(x軸限值和y軸限值)的函數(shù),和四個(gè)實(shí)數(shù)數(shù)值。:abcplane消息有三個(gè)實(shí)數(shù)參數(shù)a,b和c,然后增加線段來(lái)顯示函數(shù)f(x,y)=a+bx+cy的一段。
9.2.4 直方圖
????直方圖原型是histogram-proto,直方圖在一個(gè)m+1維的圖形里顯示m維的點(diǎn)數(shù)據(jù),剩余的維度用于圖形的y坐標(biāo),第一個(gè)維度被扔掉,作為直方圖來(lái)顯示,例如,下邊的表達(dá)式針對(duì)stack loss 數(shù)據(jù)構(gòu)造了一個(gè)直方圖:
> (setf hs (histogram (list air temp conc loss))) 該直方圖變量的數(shù)量是5: > (send hs :num-variables) 5 內(nèi)容變量是: > (send hs :content-variables) (0 4) 那么,初始情況下該圖形顯示的是數(shù)據(jù)集里第一個(gè)變量——?dú)饬鞯闹狈綀D。如果對(duì)圖形hs使用了變換,它將顯示轉(zhuǎn)換后的數(shù)據(jù)的第一維度數(shù)據(jù)的直方圖。例如,在為們?yōu)閳D形的尺度類型設(shè)置為variable之后: > (send hs :scale-type 'variable) 下邊的表達(dá)式將在10步之內(nèi)旋轉(zhuǎn)成溫度變量的直方圖。? ? 對(duì)于一維數(shù)據(jù)的直方圖,可以給予:add-points方法一個(gè)數(shù)值序列而不是一個(gè)數(shù)值序列的列表;對(duì)于一個(gè)m維數(shù)據(jù)的直方圖,:add-points方法需要一個(gè)m個(gè)序列的列表。那么stack loss數(shù)據(jù)可以使用下式來(lái)安裝:
> (send hs :add-points (list air temp conc loss)) ? ? :add-lines消息相對(duì)應(yīng)的方法(它也可能用于向一個(gè)直方圖里添加一個(gè)密度線),它需要一個(gè)包含m+1個(gè)序列的列表。最后一個(gè)序列用來(lái)作為y坐標(biāo)軸。例如,如果hp是一個(gè)一維的降水量數(shù)據(jù)的直方圖(該數(shù)據(jù)在第2.2.1節(jié)里),它可以這樣構(gòu)造: > (setf hp (histogram precipitation)) 那么下邊的表達(dá)式將一個(gè)正常濃度的圖形添加到直方圖里。? ? 因?yàn)?transfomation和:apply-transformation消息相對(duì)應(yīng)的方法可以接受一個(gè)維度低于圖形維度矩陣,你也可以使用相同維度的變換作為直方圖里的點(diǎn)數(shù)據(jù)。
? ? 針對(duì):adjust-screen, :adjust-screen-point, :redraw-content和:adjust-points-in-rect這些消息,直方圖原型提供了它自己的方法。如果尺度類型為nil,:adjust-to-data犯法增加一個(gè)x軸,該方法也會(huì)重新計(jì)算直方圖里的矩形條塊,與:clear-points和:resize相對(duì)應(yīng)的方法也會(huì)修改以適應(yīng)這些矩形條塊。
? ? 有兩個(gè)消息提供了對(duì)這些矩形條塊的信息訪問(wèn)能力。:bin-count消息返回每一個(gè)矩形條塊的數(shù)量的列表;不帶參數(shù)的:num-bins消息返回當(dāng)前用到的矩形條塊的數(shù)量,如果給它傳遞一個(gè)正整數(shù)來(lái)調(diào)用的話,它將矩形條塊的數(shù)量給位指定的數(shù)量,當(dāng)指定了一個(gè)行的矩形條塊的數(shù)量的時(shí)候,同時(shí)向圖形對(duì)象發(fā)送:redraw消息,除非關(guān)鍵字參數(shù):draw接受了值為nil的參數(shù)。
9.2.5 名稱列表
????最后一個(gè)標(biāo)準(zhǔn)繪圖原型是name-list-proto,它的目的是提供一個(gè)點(diǎn)標(biāo)簽的可連接的列表,它不適用任何數(shù)值數(shù)據(jù)。
? ? 名稱列表可以使用一個(gè)維度數(shù)m=0的數(shù)字來(lái)構(gòu)造。該原型的:add-points方法可以接受一個(gè)數(shù)值來(lái)指定添加的點(diǎn)的數(shù)量,用來(lái)替代坐標(biāo)值列表的列表。例如,針對(duì)stack loss數(shù)據(jù)的名稱列表可以這樣構(gòu)造:
> (setf n (send name-list-proto :new 0)) 擁有21個(gè)標(biāo)簽的集合和以使用下式來(lái)添加: > (send n :add-points 21) ? ? :add-lines方法被重定義,目的是不要做任何事,因?yàn)槊Q列表不需要顯示線數(shù)據(jù)。:adjust-screen, :adjust-screen-point, :redraw-content和:adjust-points-in-rect方法也會(huì)被重定義。:isnew方法名稱列表?yè)碛幸粋€(gè)垂直滾動(dòng)條。轉(zhuǎn)載于:https://my.oschina.net/u/1011760/blog/359393
總結(jié)
以上是生活随笔為你收集整理的Lisp-Stat翻译 —— 第九章 统计绘图窗体的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何删除多余系统引导项
- 下一篇: MPI编程简单介绍