<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>

          【零基礎(chǔ)】SU組件自由落體

          共 6828字,需瀏覽 14分鐘

           ·

          2021-09-12 12:05

           礎(chǔ)  本系列將回顧和串聯(lián)往期幾篇文章中的部分內(nèi)容,并通過(guò)具體的例子作盡可能詳盡的操作介紹。其中的部分細(xì)節(jié)和特殊情況將不再重復(fù)描述,專注于 零基礎(chǔ)  step-by-step 兩個(gè)屬性。




          【本期導(dǎo)覽】

          〇、原理總述

          一、創(chuàng)建表面

          二、組件的平面生成

          三、下落的過(guò)程

          四、后續(xù)調(diào)整和修飾

          “組件自由落體”即:規(guī)定的圖元,在三維空間中向下移動(dòng)到剛剛接觸給定表面??梢杂糜谠诓灰?guī)則表面(例如地形表面)上放置裝飾組件。


          〇、原理總述


          有這樣一個(gè)植樹任務(wù):確定一個(gè)地形表面,和幾種樹種組件模型,需要在地形表面上隨機(jī)種植不同的樹種。這個(gè)工作可以分成兩步,第一步是平面上的隨機(jī)生成若干樹組件,第二步是根據(jù)地形表面給每一個(gè)樹組件高度賦值。


          第一步在平面上隨機(jī)植樹同樣也可以是確切的平面設(shè)計(jì),總之是先在平面圖中完成植樹。如果是隨機(jī)創(chuàng)建,這里需要使用陣列復(fù)制和一個(gè)隨機(jī)移動(dòng)過(guò)程。其中前者使用 SU 自帶的工具即可完成;后者不僅可以通過(guò)插件實(shí)現(xiàn),也可以通過(guò) ruby 控制臺(tái)實(shí)現(xiàn),下文的示例會(huì)采用第二種方式。


          第二步則是將平面設(shè)計(jì)投影到具體的表面上,可以形象地解釋為自由落體。這一步需要分別取得每一個(gè)組件的下界和對(duì)應(yīng)空間上表面的高度,并作差值得到需要下落多少高度才能讓兩點(diǎn)相遇。


          每一個(gè)圖元都可以通過(guò) .bounds 方法獲得一個(gè)粗略的空間范圍,其范圍等于可容納下圖形的最小軸向立方體。如果是2D模型則返回包絡(luò)圓柱體所對(duì)應(yīng)的立方體。


          經(jīng)過(guò)立方體底面中心作鉛垂線,尋找與表面的交點(diǎn)。兩點(diǎn)之間距離即為要下落的距離,如果沒(méi)有找到交點(diǎn)則不移動(dòng)組件。


          一、創(chuàng)建表面


          地形創(chuàng)建有多種方式,可以使用 SketchUp 自帶的地形工具創(chuàng)建,或是導(dǎo)入已有的模型,還可以自行繪制。無(wú)論哪一種表面,只要滿足選擇工具左鍵單擊一次可以選取整個(gè)范圍即可,此時(shí)選擇表面后圖元信息窗口如下圖:


          通常出于保護(hù)模型的考慮會(huì)將地形表面打組,但是無(wú)論組件還是群組都不是地形表面本身的結(jié)構(gòu),通過(guò)圖元信息窗口可以查看當(dāng)前選擇的是否是表面本身。


          關(guān)于表面,更詳細(xì)的介紹可以查看 SU Ruby 教程中的相應(yīng)篇目 [SU-R14]。另外,使用 ruby 代碼創(chuàng)建地形可以參考此篇 [SU-2020-12]。


          下圖是在群組中選擇表面的效果:


          創(chuàng)建表面之后需要在控制臺(tái)中用變量表示這個(gè)表面,這需要使用到選區(qū)功能。單擊選擇表面之后運(yùn)行以下代碼:

          surf=Sketchup.active_model.selection.to_a;nil


          以上代碼將 surf 變量指向選中的表面,其中 selection 方法返回選區(qū)結(jié)構(gòu),而后 to_a 方法將選區(qū)中的圖元保存到新的一個(gè)數(shù)組中。最后的 nil 則是避免控制臺(tái)運(yùn)行完此行代碼后直接將數(shù)組輸出到屏幕上的一個(gè)小技巧。


          額外補(bǔ)充一下, to_a 方法來(lái)自 Enumerable 模塊。由于選區(qū) Selection 類包含有此模塊,因此可以使用 to_a 方法。而單次點(diǎn)選一個(gè)表面時(shí),選區(qū)中包含且只有構(gòu)成此表面的平面圖元,因此可以用平面的數(shù)組來(lái)表示一個(gè)表面,詳細(xì)的介紹可以參考此篇教程 [SU-R14]。


          二、組件的平面生成


          選取一個(gè)樹種組件陣列復(fù)制,并覆蓋整個(gè)地形表面,并且空間位置在地形表面之上:


          注意:此處組件是直接與表面在同一個(gè)群組內(nèi)的,這很重要。如果用于下落的組件和表面不在一個(gè)層次內(nèi),會(huì)存在絕對(duì)坐標(biāo)與相對(duì)坐標(biāo)的問(wèn)題,這里出于簡(jiǎn)化的目的,要求兩者需在同一個(gè)群組內(nèi)。


          此時(shí)的樹模型還是完全規(guī)整的排布方式,這時(shí)需要將這些組件隨機(jī)移動(dòng)。


          首先打開 ruby 控制臺(tái),復(fù)制以下的代碼:

          module Apiglio  def self.random_2d_movement(ent,dist=1000.mm)    centre=ent.bounds.center    angle=rand(360).degrees    trans_h=Geom::Transformation.rotation([0,0,0],[0,0,1],angle)    rdist=Geom::Vector3d.new([dist*rand,0,0])    rdist.transform!(trans_h)    trans=Geom::Transformation.translation(rdist)    Sketchup.active_model.entities.transform_entities(trans,ent)  endend


          如果是早期版本的 SketchUp,控制臺(tái)可能不支持多行粘貼代碼,這時(shí)需要將這個(gè)代碼保存到文本文檔中(例如 “F:\Temp\RandMov.rb”),再使用以下方法加載這個(gè)腳本(請(qǐng)注意斜杠的不同):

          load "F:/Temp/RandMov.rb"


          以上代碼定義了一個(gè)水平方向上隨機(jī)移動(dòng)的方法,將參數(shù) ent 所代表的圖元在水平方向上向任意某個(gè)方向移動(dòng) dist 的距離。例如以下示例代碼將選中的唯一的一個(gè)(或第一個(gè))圖元隨機(jī)水平移動(dòng)1米以內(nèi)的距離:

          ent=Sketchup.active_model.selection[0]Apiglio.random_2d_movement(ent,1000.mm)


          其中第一行將 ent 指向選區(qū)中的第一個(gè)圖元,而后第二行按照以上定義的方法隨機(jī)移動(dòng)這個(gè)圖元,兩行可以分別輸入控制臺(tái)依次執(zhí)行。


          如果要批量隨機(jī)移動(dòng)所有選擇的圖元,則需要使用選區(qū)的 .each 迭代器:

          sels=Sketchup.active_model.selectionsels.each{|ent|Apiglio.random_2d_movement(ent,4500.mm)}


          隨機(jī)移動(dòng)的距離要與陣列間距在同一個(gè)數(shù)量級(jí)(例如例子中使用4.5米,而陣列復(fù)制的間距為9米),否則隨機(jī)效果不佳。以下是隨機(jī)前后的對(duì)比:


          由于隨機(jī)的對(duì)象是樹,因此這里只要考慮水平方向的移動(dòng),如果有垂直方向上的移動(dòng)或者是旋轉(zhuǎn)的需求,則需要將以上的方法進(jìn)行修改,詳見此篇 [SU-2021-01]。


          三、下落的過(guò)程


          對(duì)于單個(gè)需要下落的組件,首先需要定義一個(gè)函數(shù)用于返回具體一個(gè)點(diǎn)坐標(biāo)到地形表面上的投影坐標(biāo)。復(fù)制以下代碼到 ruby 控制臺(tái):

          def project_point_to_surface(point,surface)  plumb_line=[point,[0,0,1]]  pi=nil  if surface.find{|f|    pi=Geom.intersect_line_plane(plumb_line,f.plane)    if pi.nilthen false    elsif f.classify_point(pi)==Sketchup::Face::PointOutside then false    else true end  }.nilthen    return(nil)  else    return(pi)  endend


          以上代碼定義的方法需要兩個(gè)參數(shù),第一個(gè)參數(shù) point 為具體一個(gè)空間坐標(biāo),類型為 Geom:: Point3d(三維點(diǎn)坐標(biāo)) 或 Array(數(shù)組)。第二個(gè)參數(shù) surface 為一個(gè)表面,即前文描述中的“平面圖元的數(shù)組”。


          第2行的 plumb_line 為過(guò)點(diǎn) point 所作的鉛垂線。第3行創(chuàng)建一個(gè)名為 pi 的變量,并賦值為空值;此行如果刪除,之后第5行所創(chuàng)建的變量的作用域?qū)⑾薅ㄔ?nbsp;.find 方法的塊參數(shù)之中(也就是說(shuō)只在第4~9行之間有效)。第4~9行在表面中的每一個(gè)平面圖元中尋找符合條件的結(jié)果,其中將鉛垂線 plumb_line 和平面圖元所在平面的交點(diǎn)記作 pi。如果 pi 存在并且在平面圖元內(nèi),則代表 point 能夠投影在 surface 上,那么整個(gè)方法的返回值即這個(gè)交點(diǎn) pi;否則就繼續(xù)尋找表面中的其他平面,如果所有平面都不符合條件則返回空值。整個(gè)4~13行是一個(gè)整體,它由 “if 條件 then 分支A else 分支B end” 嵌套了 find{}.nil? 的條件。


          以下是一個(gè)簡(jiǎn)單的測(cè)試效果:


          能夠計(jì)算單個(gè)點(diǎn)坐標(biāo)在表面上的投影以后,就可以將組件下界中心點(diǎn)坐標(biāo)代入其中,計(jì)算出該點(diǎn)與投影點(diǎn)的距離。根據(jù)此距離進(jìn)行組件移動(dòng)即可實(shí)現(xiàn)下落效果。以下是代碼:

          def falling_onto_surface(grp,surf)  b=grp.bounds  bc=b.center-Geom::Vector3d.new([0,0,b.depth/2])  proj=project_point_to_surface(bc,surf)  return(nilif proj.nil?  t=Geom::Transformation.new(proj-bc)  grp.parent.entities.transform_entities(t,grp)end


          以上代碼中定義的方法需要兩個(gè)參數(shù),第一個(gè)參數(shù) grp 表示需要下落的組件實(shí)例,第二個(gè)參數(shù) surf 則為表面。第2行返回組件的邊界對(duì)象 b,第3行得到邊界底面中心點(diǎn)的坐標(biāo) bc。第4行計(jì)算投影點(diǎn)坐標(biāo) proj,第5行排除無(wú)法投影的情況,之后第6行根據(jù)投影點(diǎn) proj 和底面中心 bc 的距離創(chuàng)建平移變換對(duì)象 t,第7行平移組件 grp。


          使用一個(gè)簡(jiǎn)單的迭代器就能批量調(diào)用這個(gè)下落方法:

          #以下代碼原理上有效,但是不推薦使用sels=Sketchup.active_model.selection.to_a;nilsels.each{|g|falling_onto_surface(g,surf)}


          但是并不推薦使用以上代碼來(lái)進(jìn)行下落操作。這是因?yàn)?,?dāng)需要下落的組件非常多時(shí),如果需要撤銷下落操作,你將會(huì)發(fā)現(xiàn),每一次撤銷只會(huì)撤銷一個(gè)組件的下落操作。這樣是十分不友好的,因此應(yīng)該使用以下方法:

          def falling_selected_onto_surface(surface)  Sketchup.active_model.start_operation("Falling Onto Surface",true)  sels=Sketchup.active_model.selection.to_a  sels.each{|g|    if g.is_a?(Sketchup::ComponentInstance) or g.is_a?(Sketchup::Group) then      falling_onto_surface(g,surface)    else      Sketchup.active_model.abort_operation      return nil    end  }  Sketchup.active_model.commit_operationend


          以上代碼中定義了一個(gè)僅需要一個(gè)參數(shù)的方法,參數(shù) surface 代表表面,即平面圖元的數(shù)組;此外還有一個(gè)隱含的參數(shù),即選區(qū)中的圖元;方法中會(huì)調(diào)用選區(qū),依次操作每一個(gè)選中的圖元。第3行即調(diào)用選區(qū)中的圖元的代碼。4~11行遍歷每一個(gè)選區(qū)中的圖元,如果是群組或者組件就調(diào)用 falling_onto_surface 方法移動(dòng)該圖元;如果不是符合要求的圖元?jiǎng)t表示選區(qū)參數(shù)有誤,當(dāng)即退出此方法,并且放棄之前的編輯。其中第2、8和12行完整地定義了一個(gè)可撤銷的操作,以達(dá)到通過(guò)一次撤銷命令還原到下落之前模型狀態(tài)的目的:


          選擇需要下落的組件后在控制臺(tái)中輸入以下代碼并運(yùn)行:

          falling_selected_onto_surface(surf)


          就可以得到如下的效果(如果投影不在表面內(nèi)的圖元?jiǎng)t不會(huì)下落)


          四、后續(xù)的調(diào)整與修飾


          由于下落元素是組件,還可以通過(guò)編輯組件,對(duì)所有樹種進(jìn)行統(tǒng)一的編輯,也可以進(jìn)行相對(duì)位置的改變。這種微調(diào)能夠在底部寬度不可忽略、且所在表面坡度較大時(shí)進(jìn)行簡(jiǎn)易地修飾,以達(dá)到將基底埋藏在表面以下的效果。


          另外還可以通過(guò)組件定義的替換,在保留組件實(shí)例坐標(biāo)的前提下布置其他組件,不過(guò)這種方法成功與否取決于組件替換前后的坐標(biāo)軸是否相適應(yīng)。例如:可以將下落的樹隨機(jī)替換成不同種類的樹種,以達(dá)到自然混合生長(zhǎng)的效果。


          首先需要整理好需要使用的樹種模型:


          之后將樹種模型的定義存為一個(gè)列表:

          defs=Sketchup.active_model.definitionstrees=[]trees<<defs["Tree_1"]trees<<defs["Tree_2"]trees<<defs["Tree_3"]trees<<defs["Tree_4"]trees<<defs["Tree_5"]tress[1]


          以上代碼執(zhí)行完成后, trees 將是一個(gè)存有5個(gè)樹種定義的數(shù)組,之后使用諸如 trees[1] 這樣的表達(dá)就可以引用具體一個(gè)組件定義。對(duì)于一個(gè)組件實(shí)例 g,可以使用 g .definition = trees[1] 這樣的表達(dá)將其改為相應(yīng)的樹種。


          選擇所有樹組件后,執(zhí)行以下代碼:

          sels=Sketchup.active_model.selectionsels.grep(Sketchup::ComponentInstance).each{|g|  g.definition=trees[rand(5)]}


          其中的 rand(5) 會(huì)返回0~4的的整數(shù),相當(dāng)于在 trees 數(shù)組中隨機(jī)選擇一個(gè)組件定義。執(zhí)行后就可以得到以下效果:


          如果下落以后不方便一次性選取所有樹組件,也可以使用選區(qū)輔助代碼來(lái)提高選擇圖元的效率,詳見此篇 [SU-2021-05]。


          (完)




          本篇文章盡量從最淺層的內(nèi)容開始解釋,其中出現(xiàn)了表面圖元、幾何計(jì)算、隨機(jī)變換、選區(qū)讀取和組件替換等內(nèi)容,可以參考以下幾個(gè)篇目:



          也可以參考更系統(tǒng)的教程:





          本文編號(hào):SU-2021-06


          瀏覽 320
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  天天躁日日躁狠狠躁喷水 | 91啦丨九色丨刺激中文 | 亚洲少妞视频 | 操美逼69 | 国产免费一区二区三区最新不卡 |