<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【SketchUp圖元的族譜】附庸的附庸

          共 10700字,需瀏覽 22分鐘

           ·

          2024-03-21 06:30

          本系列“圖元族譜”的上一篇“對影成三人”介紹了 SketchUp 一個組件(或組件內(nèi)的圖元)的“影子”圖元,提供了一個返回所有“影子”圖元路徑(InstancePath)的方法,通過一個組件實(shí)例或組件定義,向上查找包含該圖元的父模型,從而查找所有在其他位置的同定義組件實(shí)例。這是一種自下而上的路徑搜索邏輯,將關(guān)注另一種查找方向,即自上而下的路徑搜索。

          501f98cdeaa682e8dc84998e16ad5672.webp

          通過一個組件實(shí)例向下查找全部其包含的全部子模型

          例如下圖展示的一個群組,其內(nèi)部是 20 個定義相同的組件,每個組件中有 6 個面 12 條邊,所以群組中總計(jì)有 120 個面 240 條邊。但是對于模型存儲來說,群組中僅有 20 個位置信息(Transformation)不同的組件實(shí)例(ComponentInstance),所有組件共用 6 個面 12 條邊的定義。如果現(xiàn)在需要知道群組中每一個平面和邊線的位置,則需要從這個群組出發(fā),向下查找所有 120 個面和 240 條邊;如果群組內(nèi)的組件實(shí)例內(nèi)部還有組件,就繼續(xù)向下查找。由于這 20 個組件內(nèi)部的圖元在模型定義中和上兩層的群組沒有直接的關(guān)聯(lián),需要借助中間層實(shí)現(xiàn)下一層的查找,就像歐洲中世紀(jì)的諺語“我的附庸的附庸不是我的附庸”一樣,因此用“附庸的附庸”作為本期的題目。

          ae62f76753d9aca3aaf29c9e9f89687c.webp

          群組中包含20個相同定義的組件,每個組件中有6個面12條邊

          一、子模型的搜索

          SketchUp 模型中呈現(xiàn)的每一個圖元,都通過實(shí)例路徑進(jìn)行表示,無論組件復(fù)用多少次,每一個組件的每一個圖形要素都有唯一的實(shí)例路徑(InstancePath)用于空間定位(詳見上一篇第二部分)。所以獲取一個組件內(nèi)所有圖形的位置,關(guān)鍵在于逐層獲取這些圖形的實(shí)例路徑,這同樣需要使用到遞歸的方法,與上一篇查找“影子”圖元的邏輯是相似的。只不過當(dāng)時是通過組件定義(definition)去尋找所有同定義的實(shí)例(instance),而現(xiàn)在是通過組件實(shí)例去找所有定義中的圖元,查找路徑的邏輯可以用下圖表示。

          363987aa1c2ac06f9fae29a1f1d4a2b8.webp

          上一篇文章中構(gòu)造了一個 InstancePathTree 類,通過 add_child 方法將父模型記錄為當(dāng)前模型的子節(jié)點(diǎn),從而實(shí)現(xiàn)組件嵌套樹狀結(jié)構(gòu)的解析。這里同樣可以使用 InstancePathTree 類,將子模型作為當(dāng)前模型的子節(jié)點(diǎn),即可反方向構(gòu)建樹狀結(jié)構(gòu)。

          在本部分末,呈現(xiàn)了在 InstancePathTree 類中新增的幾個類方法和實(shí)例方法,第 6 行的 subordinates 方法通過調(diào)用先前的 recur_paths 方法遞歸生成樹狀結(jié)構(gòu)的每一條路徑。由于創(chuàng)建樹的方向不同,需要用 .reverse 方法對先前自下而上的路徑結(jié)果進(jìn)行翻轉(zhuǎn)。

          第 15 行的 recur_find_subordinate 類方法,用于遞歸創(chuàng)建當(dāng)前節(jié)點(diǎn)模型的子節(jié)點(diǎn),當(dāng)模型擁有 :definition 方法時,說明當(dāng)前模型是群組或者組件,存在子模型,所以調(diào)用 add_child 方法將組件定義中的所有子模型創(chuàng)建為子節(jié)點(diǎn),并分別對子節(jié)點(diǎn)進(jìn)行相同的遞歸運(yùn)算。如果當(dāng)前節(jié)點(diǎn)的模型是 nil 則表示當(dāng)前節(jié)點(diǎn)是世界模型,從 Sketchup .active_model .entities 中讀取子模型信息。 如果不是組件或者群組,其他圖元調(diào)用此方法不會有任何動作,也就不會進(jìn)入下一層遞歸,不過更好的處理方式是先判斷圖元類型再決定是否遞歸,性能效果要好些。

          第 32 行和第 48 行的兩個類方法都是通過特定的條件返回所有子模型的實(shí)例路徑。其中 check_subordinate_instancepath 方法通過一個路徑返回所有次級路徑,例如文章開頭的案例中,如果雙擊打開群組,將當(dāng)前路徑作為參數(shù)執(zhí)行該方法,即可返回 120 個平面和 240 條邊線。

          a56f1daef4b5b0f608b7f26ae6dba15e.webp

            check_subordinate_definition  方法則以一個組件定義為參數(shù),判斷組件定義內(nèi)的所有圖元路徑,這個方法對于繪制臨時的組件圖形很有幫助。

          以下是 InstancePathTree 類新增的代碼部分:

                
                  
                    class InstancePathTree
                  
                
                
                    # 此處省略了部分上一篇文章中呈現(xiàn)的代碼
                
                
                    # 只展示新增的代碼
                
                
                    # 因此直接復(fù)制粘貼是無法運(yùn)行的
                
                
                    # 如需復(fù)制完整代碼可以見文末的鏈接
                
                
                    def subordinates
                
                
                      root_path = @instpath.to_a
                
                
                      result=[]
                
                
                      recur_paths(result,[])
                
                
                      return result.map{|path|Sketchup::InstancePath.new(root_path+path.compact.reverse)}
                
                
                    end
                
                
                
                    class << self
                
                
                      # 此處省略了部分上一篇文章中呈現(xiàn)的代碼
                
                
                      def recur_find_subordinate(node)
                
                
                        entity = node.instance
                
                
                        if entity.respond_to?(:definition) then
                
                
                          entity.definition.entities.each{|ent|
                
                
                            child = node.add_child(ent)
                
                
                            recur_find_subordinate(child)
                
                
                          }
                
                
                        elsif node.instance.nilthen
                
                
                          Sketchup.active_model.entities.each{|ent|
                
                
                            child = node.add_child(ent)
                
                
                            recur_find_subordinate(child)
                
                
                          }
                
                
                        end
                
                
                      end
                
                
                      private :recur_find_subordinate
                
                
                
                      # 返回path之下的所有path
                
                
                      def check_subordinate_instancepath(instance_path)
                
                
                        instance_path=[] if instance_path.nil?
                
                
                        instpath = Sketchup::InstancePath.new(instance_path)
                
                
                        if instpath.empty? then
                
                
                          result = InstancePathTree.new(nil)
                
                
                          result.instpath = []
                
                
                        else
                
                
                          instpath_ary = instpath.to_a
                
                
                          result = InstancePathTree.new(instpath_ary.pop)
                
                
                          result.instpath = instpath_ary
                
                
                        end
                
                
                        recur_find_subordinate(result)
                
                
                        return result
                
                
                      end
                
                
                
                      # 返回definition之下的所有path
                
                
                      def check_subordinate_definition(definition)
                
                
                        instpath=[] if instpath.nil?
                
                
                        raise ArgumentError.new("參數(shù)definition必須要有entities成員。") unless definition.respond_to?(:entities)
                
                
                        result = InstancePathTree.new(nil)
                
                
                        result.instpath = []  
                
                
                        definition.entities.each{|ent|
                
                
                          child = result.add_child(ent)
                
                
                          recur_find_subordinate(child)
                
                
                        }
                
                
                        return result
                
                
                      end
                
                
                
                      # 根據(jù)參數(shù)選擇性調(diào)用前兩個方法
                
                
                      def check_subordinate(instpath_or_definition)
                
                
                        if instpath_or_definition.respond_to?(:entities) then
                
                
                          check_subordinate_definition(instpath_or_definition)
                
                
                        else
                
                
                          check_subordinate_instancepath(instpath_or_definition)
                
                
                        end
                
                
                      end
                
                
                    end
                
                
                  
                    end
                  
                
              


          二、具體的使用示例

          首先將整個 InstancePathTree 類的代碼復(fù)制到控制臺中執(zhí)行,或者直接用 load 方法加載 “instpath_helper.rb” 文件(在本人github/gitee主頁中可以找到,鏈接在文末)。之后就可以在控制臺中測試前文介紹的方法,以下提供幾個案例。

          ①返回整個模型的全部路徑

          其中第 1 行最后的 ;nil 用于避免將 paths 結(jié)果打印在控制臺輸出中。這是因?yàn)?,如果模型很大,打印結(jié)果將會消耗大量時間,造成軟件假死。

                
                  paths = InstancePathTree.check_subordinate([]).subordinates;nil
                
                
                  paths.length
                
                
                  
                    #=> 1319
                  
                
              


          ②返回當(dāng)前路徑之下的全部路徑

          其中第 1 行將當(dāng)前路徑保存在變量 apath 中,在第 2 行作為參數(shù)返回此路徑下的結(jié)果。

                
                  apath = Sketchup.active_model.active_path
                
                
                  paths = InstancePathTree.check_subordinate(apath).subordinates;nil
                
                
                  paths.length
                
                
                  
                    #=> 348
                  
                
              


          ③返回某個組件內(nèi)部的全部路徑

          其中第 1~2 行將名稱為 "組件#1" 的組件的定義保存在變量 cpmdef 中,在第 4 行作為參數(shù)返回該組件定義之下的全部路徑。

                
                  defs = Sketchup.active_model.definitions
                
                
                  cpmdef = defs.select{|d|d.name=="組件#1"}[0]
                
                
                  
                    #=> #<Sketchup::ComponentDefinition:0x000001bac7641300>
                  
                
                
                  paths = InstancePathTree.check_subordinate(cpmdef).subordinates;nil
                
                
                  paths.length
                
                
                  
                    #=> 35
                  
                
              


          ④僅返回邊線的路徑

          借用上一個案例中返回的 paths 結(jié)果,可以進(jìn)一步篩選組件中的所有以邊線圖元為末梢的路徑。其中 .select 方法表示篩選,其后的花括號內(nèi)條件中的 .leaf 是 InstancePath 的方法,返回最末梢的圖元。

                
                  spath = paths.select{|path|path.leaf.is_a? Sketchup::Edge}
                
                
                  p spaths
                
                
                  
                    #=> [#<Sketchup::InstancePath:0x000001bacf8a35f0>, 
                  
                
                
                  
                    #=> ...,
                  
                
                
                  
                    #=> #<Sketchup::InstancePath:0x000001bacf8a1ae8>]
                  
                
              


          ⑤具體實(shí)例路徑下所有邊線的坐標(biāo)顯示

          多層的嵌套會讓組件中的坐標(biāo)處理變得復(fù)雜而不易想象,通過路徑的獲取可以簡化這個坐標(biāo)轉(zhuǎn)換的過程,因?yàn)槊恳粋€路徑都能直接返回對應(yīng)的相對位置變換矩陣,給多層嵌套的模型坐標(biāo)計(jì)算提供便利。例如以下代碼返回一個具體路徑的組件內(nèi)所有邊線的端點(diǎn)坐標(biāo)。

                
                  
                    # 先打開要測試的組件實(shí)例路徑
                  
                
                
                  test_path = Sketchup.active_model.active_path
                
                
                  Sketchup.active_model.active_path = []
                
                
                  
                    # 自動退出組件編輯,避免圖元所在entities與active_entities一致而返回世界坐標(biāo)
                  
                
                
                  paths = InstancePathTree.check_subordinate(test_path).subordinates;nil
                
                
                  paths.select!{|path|path.leaf.is_a? Sketchup::Edge}
                
                
                  paths.each{|path|
                
                
                    trans = path.transformation
                
                
                    v1 = path.leaf.vertices[0]
                
                
                    v2 = path.leaf.vertices[1]
                
                
                    p1 = trans*v1.position
                
                
                    p2 = trans*v2.position
                
                
                    puts "Line #{p1} To #{p2}"
                
                
                  };nil
                
                
                  
                    #=> Line (254 m, 254 m, 0 m) To (254 m, 254 m, 168.4105 m)
                  
                
                
                  
                    #=> Line (254 m, 254 m, 0 m) To (254 m, 508 m, 0 m)
                  
                
                
                  
                    #=> Line (508 m, 508 m, 0 m) To (508 m, 254 m, 0 m)
                  
                
                
                  
                    #=> ...
                  
                
              


          三、圖元族譜的總結(jié)

          到目前為止,“SketchUp 圖元的族譜”系列的三篇文章已經(jīng)全部結(jié)束,第一篇“包含與嵌套”介紹 SketchUp 中群組和組件的嵌套方式,區(qū)分了組件實(shí)例與組件定義的區(qū)別,并對世界坐標(biāo)系和組件坐標(biāo)系進(jìn)行了辨析,為后兩篇內(nèi)容提供了一些背景內(nèi)容。而第二篇“對影成三人”和本篇“附庸的附庸”則分別從向上和向下兩個搜索方向介紹如何通過模型中一個確切的結(jié)構(gòu)位置,計(jì)算出與之相關(guān)的所有圖元實(shí)例路徑。兩個功能都需要借助遞歸的方式構(gòu)建模型結(jié)構(gòu)樹,因此都需要使用 InstancePathTree 類實(shí)現(xiàn)。

          e33787d6438c33658991ede21d07e85a.webp

          三篇文章對應(yīng)的模型嵌套結(jié)構(gòu)

          而兩篇如此不厭其煩地 構(gòu)造 自定義類 實(shí) 現(xiàn)路徑的查找,都是為了更便捷地對嵌套模型的坐標(biāo)轉(zhuǎn)換進(jìn)行描述。模型嵌套往往會讓人對不同層圖元的坐標(biāo)產(chǎn)生困惑,而 InstancePath 是模型坐標(biāo)定位的本質(zhì),直接計(jì)算符合條件的 InstancePath 就可以通過 .transformation 方法便捷地獲取代表相對位置的變換矩陣 ,只需要將路徑末端圖元 .leaf 的坐標(biāo)與相對位置變換矩陣相乘,就能輕易得到這個末端圖元的世界坐標(biāo),這也就是兩篇文章中 InstancePathTree 類的意義。

          (完)



          從SU-2022-01期開始,本人的 SketchUp Ruby “小功能與靈感” 的文章中涉及的功能會在 Gitee/GitHub 中相應(yīng)更新。這樣一來,如果日后發(fā)現(xiàn)存在bug需要修正或者是想追加相關(guān)的功能,都有極大的便利。

          這個代碼倉庫同樣也包含大部分之前文章的代碼,可以訪問以下網(wǎng)址查看,也歡迎大家共同提交修改:

          https://gitee.com/apiglio/sketchup-script-tool

          https://github.com/Apiglio/SketchupScriptTool

          本期代碼在 class/ instpath_helper.rb  文件中,文中的 InstancePathTree 類 按如下方式調(diào)用:

                
                  
                    #選擇一個組件
                  
                
                
                  sels = Sketchup.active_model.selection
                
                
                  inst = sels[0]
                
                
                  
                    # 如果查找當(dāng)前組件定義中的所有路徑:
                  
                
                
                  n = InstancePathTree.check_subordinate(inst.definition)
                
                
                  
                    # 如果查找整個模型的所有路徑:
                  
                
                
                  n = InstancePathTree.check_definition(nil)
                
                
                  p n.subordinates
                
                
                  
                    #=> [...]
                  
                
              

          之前更新的教程到組件部分之后一直就沒有再更新,總覺得需要一個比較好的講述邏輯,又希望能夠統(tǒng)一組件、群組和圖像三種圖元,又要兼顧組件定義和定義列表這兩個實(shí)體,所以總顯得雜亂無章。因此打算先在這個系列大概聊一聊組件的一些特點(diǎn),也是給詳細(xì)的組件教程進(jìn)行一個預(yù)熱。

          SketchUp ruby的中文資料相對較少,并且相對小眾,歡迎加入 SketchUp Ruby 自學(xué)俱樂部 Q群(群號:368842817)。



          本文編號:SU-2023-04


          瀏覽 141
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美1区| 免费一区二区三区无码 | 国产精品特级毛片 | 操逼网网址 | 99久久久无码国产精品免费麻豆 |