SketchUpで作った3D図面から板の一覧を作る

最近家の棚を全部自作してるんですが、SketchUpで3Dモデルを作るまでは楽しいものの、出来上がったモデルを構成する木の寸法やら枚数のリストを作るのが面倒になりました。のでRubyプラグインで作ってみました。ちなみにRubyを書いたのは人生で2度目くらいなのでしょうもない書き方している可能性大ですが、ご自由にお使いください。

Sketchupでのプラグインの使い方は[本家の解説ページ](http://www.sketchup.com/intl/en/developer/docs/tutorial_helloworld)を見れば分かりますが、4行でまとめると以下の通りです。

-  rbファイルをSketchUp所定のフォルダに入れる
-  SketchUpを起動し、適当な.skpファイルを開く
-  メニューのウィンドウ>Rubyコンソールを開く
-  rbファイルに定義している関数をタイプする(下のスクリプトの場合はcuboidlistとタイプする)と結果がもらえる

# モデル内の直方体をサイズ毎に集計して表示するスクリプト

```rb
def cuboidlist
	model = Sketchup.active_model

  # モデル中の直方体を再起的に発見して種類ごとにまとめる 
  printf("[finding]\n")
  cuboids = findCuboids(model) # モデル中の全直方体をリストアップする
  cuboids.sort! # ソートを追加
  cuboidSet = {}
  cuboids.each_with_index do |cuboid, c|
    printf("  cuboid #%02d %s\n", c, sizestr(cuboid))
      
    # 同一形状でカウントする
    if cuboidSet[cuboid] == nil then
      cuboidSet[cuboid] = 1
    else
      cuboidSet[cuboid] += 1
    end
	end

	# 結果の表示	
	printf("[summary]\n")
	cuboidSet.each_with_index do |(cuboid, count), c|
    printf("  cuboid #%02d %s * %d\n", c, sizestr(cuboid), count)
	end
	
	return cuboidSet
end

def findCuboids(container)
  cuboids = []
    
  # 現在のcontainerにfaceが6枚あれば、それが直方体かどうかをチェックする  
  cuboid = faces2cuboid(getTypes(container, Sketchup::Face))
  if cuboid!= nil then
    cuboids << cuboid
  end
  
  # 現在のcontainerの下にgroupがあれば、それを探索する
  getTypes(container, Sketchup::Group).each_with_index do |subgroup, g|
    cuboids.concat(findCuboids(subgroup))
  end
  
  # 現在のcontainerの下にcomponentがあれば、それを探索する
  getTypes(container, Sketchup::ComponentInstance).each_with_index do |cmp, g|
    cuboids.concat(findCuboids(cmp.definition))
  end

  return cuboids
end

def faces2cuboid(faces)
  # 直方体の条件: faceが6枚, 全edgeの長さは1or2or3通りであること
  if faces.length != 6
    return nil
  end
  
  # 辺を探索して長さ->出現回数の辞書を作成する
  # これが1通り24個 = 立方体, 2通り8,16個 = 正方形直方体, 3通り8,8,8個 = 一般の直方体
  edges = {} # 長さ -> 出現数
  faces.each_with_index do |face, f|
    face.edges.each_with_index do |edge, e|
      length = (edge.length * 25.4).round # inch->mili
      # 辺長さ辞書に追加する
      if edges[length] == nil then
        edges[length] = 1
      else
        edges[length] += 1
      end
    end
  end
  
  if (edges.length.between?(1,3)) then
    # 直方体の3辺の長さのリストを作る
    cuboid = []
    edges.each{|length, count|
      for num in 0...(count / 8) do
        cuboid << length
      end
    }
    return cuboid.sort()
  else
    return nil
  end
end

def getTypes(container, type)
  # sketchupのcontainerから特定タイプのentityをリスト形式で取り出す
  ret = []
  for g_ent in container.entities do
    if g_ent.kind_of?(type) then
      ret << g_ent
    end
  end
  return ret
end

def sizestr(cuboid)
  return sprintf("[%4d, %4d, %4d]", cuboid[0], cuboid[1], cuboid[2])
end
```

# 実行例
例えばこんな感じの棚に対して,
![スクリーンショット 2015-03-29 20.06.06.png](https://qiita-image-store.s3.amazonaws.com/0/56846/996809e7-491d-1065-af65-ae2fda6b60c8.png)

最終的にこんなリストが得られます。後はこのリストをもとに木取り図まで自動で作れたら完璧なんですが、それは未完です。

```
[summary]
  cuboid #00 [   4,   71,  400] * 26
  cuboid #01 [  15,   71,  150] * 18
  cuboid #02 [   4,  158,  400] * 9
  cuboid #03 [  24,  300,  315] * 3
  cuboid #04 [  15,   71,  230] * 8
  cuboid #05 [   4,  238,  400] * 4
  cuboid #06 [  15,  300,  480] * 3
```

# 注意点
-  面6枚をグループ化/コンポーネント化したものだけを抜粋します。三角柱など6面でないものは無視されます。
-  直方体かどうかの判定に「辺の長さが1or2or3種類だったら直方体」という適当ルールを用いているので、平行四辺形の柱などがあるとその辺の長さをもつ直方体として認識されます。
-  グループやコンポーネントにした後で拡大縮小をしていると表示されるサイズが正しくないようです。一度グループ化を解除するなどしてどの面も後から拡大縮小をされていない状態にすれば正しい数値が出ます。
-  単位はミリメートルです(内部データがインチ単位っぽいので×25.4しています)。

# 書き換えてリロードする場合は
例えば上のスクリプトをtest.rbという名前でpluginフォルダに入れていた場合、SketchUpのRubyコンソールで

```
load "test.rb"
```

とすれば最新の状態をリロードしてくれるようです。