【SU Ruby教程】幾何與變換(2):范圍

上一篇用較長的篇幅介紹了向量與點線面的表示方法,而這一篇繼續(xù)關注Geom模塊中提供的另一個空間概念——范圍。
范圍(Bounds),是一組空間坐標在各坐標軸方向上最大最小值的范圍,對于SketchUp中的三維空間而言,是一個其各條棱與坐標軸平行的、包含一組坐標的最小長方體。
Geom模塊中有三個與“Bounds”有關的類定義,分別是 Geom:: Bounds2d、Geom:: OrientedBounds2d 和 Geom:: BoundingBox。其中前兩個是二維的范圍,與 Geom:: Point2d、 Geom:: Vector2d 均是二維空間概念,主要用于 Layout, SketchUp 則一般不會使用到。
因此本篇重點在于介紹Geom:: BoundingBox類。
幾何與變換(2):范圍
| 【本期目錄】 | |
(1)范圍類 ①Geom::BoundingBox類 ②三種訪問方法 | (3)判斷與運算 ①判斷是否在范圍內(nèi) ②并集運算 ③交集運算 |
(3)范圍基本屬性 ①錨點與中點 ②長寬高與對角線 | |
(1)范圍類
①Geom:: BoundingBox類
Geom 模塊定義了 BoundingBox 類用于表示空間范圍,用于判斷空間中某點是否在某個范圍中。
SU中的圖元實體包含多個空間點,而通過這些點可以得到一個空間范圍,這個范圍在一定程度上能夠反映圖元的空間位置。通過比較兩個圖元的范圍可以粗略地判斷兩個圖元是否有可能相交。
②三種訪問方法
一般情況下,我們不需要自己新建 Geom:: BoundingBox 類的實例,而是直接通過 Sketchup:: Drawingelement 類實例的 .bounds 方法返回范圍類的一個實例:
#令當前模型至少有一個圖元puts Sketchup.active_model.entities[0].bounds#>> #<Geom::BoundingBox:0x0000000051afe0>
與Sketchup:: Vertex的 .position 方法一樣, .bounds 方法也是根據(jù)圖元的空間位置范圍創(chuàng)建一個新的 BoundingBox 類實例,所以對同一個圖元多次調(diào)用 .bounds 方法會返回不同的范圍類實例,盡管這些實例所代表的范圍是一致的。
除了圖元類,Sketchup:: Model類也可以返回整個模型所有圖元的空間范圍,同樣是 .bounds 方法:
puts Sketchup.active_model.bounds#>> #<Geom::BoundingBox:0x0000000b5bb638>
依附于模型的范圍類固然十分便捷,但有時可能需要使用抽象的、不依附于模型的范圍類,這時就需要使用 .new 方法這種最基本的創(chuàng)建方式了。
puts Geom::BoundingBox.new#>> #<Geom::BoundingBox:0x0000000b5694c8>
不同于前兩個方法獲得的 BoundingBox 實例,此時新建的實例是一個空范圍,可以用 .empty? 方法和 .valid? 方法檢驗。
#令當前模型至少有一個圖元bb_1 = Sketchup.active_model.boundsbb_2 = Geom::BoundingBox.newputs bb_1.empty? #>> falseputs bb_1.valid? #>> trueputs bb_2.empty? #>> trueputs bb_2.valid? #>> false
以上兩個方法結(jié)果總是相反的,因此 !bb. empty? 和 bb. valid? 相同, !bb. valid? 和 bb. empty? 相同。
如果當前模型中沒有任何圖元, Sketchup. active_model. bounds. empty? 的結(jié)果為 true。
(2)范圍基本屬性

①錨點與中點
如上圖,一個 BoundingBox 類實例包含九個關鍵點,八個頂點和一個中心點,它們分別由以下方法返回,返回類型為Geom:: Point3d:
bb = Sketchup.active_model.boundsputs bb.center#>> (1061443.319932mm, 1074824.726654mm, 1088928.066406mm)corners=[]for point_id in 0..7 docorners << bb.corner(point_id)endputs corners#>> [Point3d(-17306.2, -30879.8, -8293.75),#>> Point3d(100884, -30879.8, -8293.75),#>> Point3d(-17306.2, 115512, -8293.75),#>> Point3d(100884, 115512, -8293.75),#>> Point3d(-17306.2, -30879.8, 94036.1),#>> Point3d(100884, -30879.8, 94036.1),#>> Point3d(-17306.2, 115512, 94036.1),#>> Point3d(100884, 115512, 94036.1)]
其中, .corner() 方法并不是 “.corners”,因此不是返回一整個端點數(shù)組,而是根據(jù)給定的端點編號返回一個端點。
錨點中有兩個點比較特殊,分別是 corner(0) 和 corner(7) ,這兩個頂點分別代表坐標之和最小和最大的兩個點,通過這兩個點就可以還原整個范圍。因此這兩個頂點還可以專門用 .min 和 .max 方法來訪問。
puts bb.min#>> Point3d(-17306.2, -30879.8, -8293.75)puts bb.max#>> Point3d(100884, 115512, 94036.1)
②長寬高與對角線
除了關鍵點以外,BoundingBox還提供四個與范圍有關的長度參數(shù),分別是“長”“寬”“高”(或者應該說是“寬”“高”“深”),和對角線長。其中, .width 對應的是x軸(紅軸)方向上的寬度, .height 對應的是y軸(綠軸)方向上的寬度, .depth 對應的是z軸(藍軸)方向上的寬度。 .diagonal 即對角線長度。
bb = Sketchup.active_model.boundsputs bb.width #>> ~ 3002041mmputs bb.height #>> ~ 3718342mmputs bb.depth #>> ~ 2599179mmputs bb.diagonal #>> ~ 5440041mm
當然也可以自己補充一個體積,在某些特定的場合可以使用:
module Geomclass BoundingBoxdef volumereturn self.height*self.width*self.depthendendend
(3)判斷與運算
①判斷是否在范圍內(nèi)
范圍類的重要作用就是判斷點是否在特定范圍內(nèi),而這個功能需要 .contains? 方法來實現(xiàn):
bb = Sketchup.active_model.boundsputs bb.contains?([0,0,0])puts bb.contains?(Geom::Point3d.new([2,54,-64]))
除了判斷點位置, .contains? 方法也可以判斷另一個范圍類是否在此范圍之內(nèi):
bb_model = Sketchup.active_model.boundsbb_ent = Sketchup.active_model.entities[0].boundsputs bb_model.contains?(bb_ent)#>> trueputs bb_model.contains?(Geom::BoundingBox.new)#>> false
只有前者完全包含后者(包括相切),才會返回 true。另外,如果作為參數(shù)的范圍是空范圍,包含的結(jié)果必然是 false。其原因如下:
bb=Geom::BoundingBox.newfor i in 0..2 doputs bb.min[i]>bb.max[i]end#>> true#>> true#>> true
可以發(fā)現(xiàn)一個新創(chuàng)建的空范圍,它的max點要小于min點,這是檢驗范圍是否有效的標志。而 .contains? 方法中需要對比兩個范圍的min點和max點,類似于如下的定義:
module Geomclass BoundingBoxdef contains?(bb_or_point)if bb_or_point.is_a?(::Array) then_max=bb_or_point_min=bb_or_pointelsif bb_or_point.is_a?(Geom::Point3d) then_max=bb_or_point.to_a_min=bb_or_point.to_aelsif bb_or_point.is_a?(Geom::BoundingBox) then_max=bb_or_point.max.to_a_min=bb_or_point.min.to_aelse return false end_min.each_index{|i|if _min[i]<self.min[i] then return false end}_max.each_index{|i|if _max[i]>self.max[i] then return false end}return trueendendend
因此,只有同時滿足條件才會返回 true,所以空范圍一定不能同時滿足最大和最小兩個要求。
②并集運算
BoundingBox 類實例也可以在原有范圍的基礎上追加坐標或范圍,擴大包含給定的坐標或者范圍,可以理解為范圍的并集操作。這個功能需要 .add 方法來實現(xiàn):
bb=Geom::BoundingBox.newbb.add([-100.mm,-100.mm,-300.mm])bb.add([0.mm,100.mm,100.mm])bb.add([200.mm,300.mm,100.mm])puts bb.minputs bb.max#>> (-100mm, -100mm, -300mm)#>> (200mm, 300mm, 100mm)
除了接受表示點坐標的Array類參數(shù), .add 方法還可以接受 Geom:: Point3d 類、 Sketchup:: Vertex類和 Geom:: BoundingBox 類的實例:
bb.add(Geom::Point3d.new(-100.mm,-100.mm,-300.mm))#令當前模型至少包含一個邊線圖元ent=Sketchup.active_model.entities.grep(Sketchup::Edge)[0]bb.add(ent.vertices[0])bb.add(ent.bounds)
還可以接受以上這些類型實例組成的數(shù)組:
points_or_bb = []points_or_bb << Geom::Point3d.new(-100.mm,-100.mm,-300.mm)points_or_bb << ent.vertices[0]points_or_bb << ent.boundsbb.add(points_or_bb)
③交集運算
BoundingBox 類實例同樣也可以進行交集運算,這個功能需要 .intersect 方法來實現(xiàn):
#令當前模型包含兩個群組ents=Sketchup.active_model.entities.grep(Sketchup::Group)bb=ents[0].bounds.intersect(ents[1].bounds)puts bb.widthputs bb.heightputs bb.depth#>> ~ 1374mm#>> ~ 1089mm#>> ~ 2110mm
以上代碼計算兩個群組的范圍交疊部分,示意圖如下(其中立方體為輔助顯示范圍大小):

交集運算和并集運算的原理都是判斷min點和max點的坐標是否需要更新,有興趣的可以自己仿寫練習。
最后還是按照慣例放一些補充內(nèi)容。本部分最后一張圖的立方體不是手工創(chuàng)建的,而是通過以下代碼生成的:
module Geomclass BoundingBoxdef create_boxp=[]for point_id in 0..3 dop << self.corner(point_id)endents=Sketchup.active_model.entitiesgrp=ents.add_grouplines=[]lines << (grp.entities.add_line(p[0],p[1]))lines << (grp.entities.add_line(p[1],p[3]))lines << (grp.entities.add_line(p[3],p[2]))lines << (grp.entities.add_line(p[2],p[0]))f=grp.entities.add_face linesf.pushpull self.depthreturn grpendendend
其中包括一些有關圖元類的代碼,在后續(xù)的教程中會繼續(xù)介紹。有關圖元的代碼有很多例子已經(jīng)出現(xiàn)在往期的其他文章中,例如:
本文編號:SU-R07
